Codex CLI(新しいウィンドウで開く) は、クロスプラットフォームのローカルソフトウェアエージェントであり、お使いのマシン上で安全かつ効率的に動作しながら、高品質で信頼性の高いソフトウェア変更を生成するように設計されています。私たちは、4月にCLIを初めてリリースして以来、世界クラスのソフトウェアエージェントを構築する方法について多くのことを学びました。本稿は、そのような洞察を解き明かすべく、Codex の仕組みや、その学びから得た貴重な教訓を探る継続的なシリーズの第1回となります。(Codex CLI の構築方法についてさらに詳しく知りたい方は、オープンソースのリポジトリを https://github.com/openai/codex(新しいウィンドウで開く) でご覧ください。設計上の判断に関する詳細の多くは、GitHub の Issue やプルリクエストに記録されておりますので、さらに詳しく知りたい場合は参照してください。)
まずは、エージェントループに焦点を当てます。これは Codex CLI の中核ロジックであり、ユーザー、モデル、およびモデルが有意義なソフトウェア作業を実行するために呼び出すツールの間のやり取りを調整する役割を担っています。この投稿が、LLM を活用する際に当社のエージェント(または「ハーネス」)が果たす役割について理解を深める一助となれば幸いです。
本題に入る前に、用語について簡単に説明します。OpenAI では、「Codex」は Codex CLI、Codex Cloud、Codex VS Code 拡張機能を含む一連のソフトウェアエージェント製品を指します。この投稿では Codex ハーネス に焦点を当てています。これは、すべての Codex 体験の基盤となる中核のエージェントループと実行ロジックを提供し、Codex CLI を通じて表示されます。ここでは便宜上、「Codex」と「Codex CLI」を同じ意味で使用します。
すべてのAIエージェントの中心には、「エージェントループ」と呼ばれるものがあります。エージェントループの簡略化された図は以下の通りです。
まず、エージェントはユーザーから入力(インプット)を受け取り、それをプロンプトと呼ばれるモデル向けに用意するテキスト指示のセットに含めます。
次のステップは、指示を送信して応答を生成するようにモデルに問い合わせることです。このプロセスは 推論(インファレンス) と呼ばれます。推論中、テキストプロンプトはまず入力トークン(新しいウィンドウで開く)のシーケンスに変換されます。これらはモデルの語彙を指す整数です。これらのトークンはモデルのサンプリングに使用され、その結果、新しい出力トークンのシーケンスが生成されます。
出力トークンはテキストに変換され、モデルの応答となります。トークンは増分的に生成されるため、この変換はモデルの実行中に行うことができます。そのため、多くのLLMベースのアプリケーションではストリーミング出力が表示されます。実際には、推論は通常、テキストを処理するAPIの背後にカプセル化されており、トークン化の詳細は抽象化されています。
推論ステップの結果として、モデルは(1)ユーザーの元の入力に対する最終的な応答を生成するか、または(2)エージェントが実行することが期待されるツール呼び出しを要求します(例:「 lsを実行して出力を報告する」)。(2) の場合、エージェントはツール呼び出しを実行し、その出力を元のプロンプトに追加します。この出力は、モデルに再クエリするための新しい入力を生成する際に使用されます。その後エージェントは、この新しい情報を考慮して、再度試行することができます。
このプロセスは、モデルがツール呼び出しを停止し、代わりにユーザー向けのメッセージ(OpenAIモデルではアシスタントメッセージと呼ばれます)を生成するまで繰り返されます。多くの場合、このメッセージはユーザーの元の要求に直接答えますが、ユーザーに対するフォローアップの質問になることもあります。
エージェントはローカル環境を変更するツール呼び出しを実行できるため、その「出力」はアシスタントメッセージに限定されません。多くの場合、ソフトウェアエージェントの主な出力は、皆さんのマシン上で記述または編集されるコードです。それにもかかわらず、各ターンは常にアシスタントメッセージ(例:「要求された architecture.md を追加しました」)で終わり、エージェントループの終了状態が示されます。エージェントの視点から見ると、作業は完了しており、制御はユーザーに戻ります。
図に示されているユーザー入力からエージェント応答までの一連の流れは、会話の1回のターン(Codexではスレッド)と呼ばれます。ただし、この会話ターンには、モデル推論とツール呼び出しの間で複数回の反復が含まれることがあります。既存の会話に新しいメッセージを送信するたびに、過去のメッセージやツール呼び出しを含む会話履歴が、新しいターンのプロンプトの一部として含まれます。
これは、会話が進むにつれて、モデルをサンプリングするために使用されるプロンプトの長さも増加することを意味します。この長さが重要なのは、すべてのモデルにコンテキストウィンドウがあり、それが1回の推論呼び出しに使用できるトークンの最大数になるからです。このウィンドウには入力トークンと出力トークンの両方が含まれていることに注意してください。ご想像のとおり、エージェントは1回のターンで数百回のツール呼び出しを行うことを決定し、結果としてコンテキストウィンドウを使い果たす可能性があります。このため、コンテキストウィンドウ管理はエージェントの多くの責務の一つです。それでは、Codex がエージェントループをどのように実行するのか見てみましょう。
Codex CLI はモデル推論を実行するために、 Responses API(新しいウィンドウで開く) に対して HTTP リクエストを送信します。Responses API を使用してエージェントループを駆動する Codex を通じて、情報がどのように流れるかを調べます。
Codex CLI が使用する Responses API エンドポイントは設定可能(新しいウィンドウで開く)であり、Responses API を実装している(新しいウィンドウで開く)任意のエンドポイントで使用できます。
- Codex CLIで ChatGPT ログインを使用する場合(新しいウィンドウで開く)、エンドポイントとして
https://chatgpt.com/backend-api/codex/responsesが使用されます。 - OpenAI ホスト型モデルで API キー認証を使用する場合(新しいウィンドウで開く)、エンドポイントとして
https://api.openai.com/v1/responsesが使用されます。 - Codex CLI を
--oss付きで実行し、gpt-oss を ollama 0.13.4+(新しいウィンドウで開く) または LM Studio 0.3.39+(新しいウィンドウで開く) と共に使用する場合、デフォルトでhttp://localhost:11434/v1/responsesがコンピュータ上でローカルに実行されます - Codex CLI は、Azure などのクラウドプロバイダーによってホストされている Responses API で使用できます
Codex が会話の最初の推論呼び出しのためにどのようにプロンプトを作成するかを探ってみましょう。
あなたがエンドユーザーとして Responses API をクエリする際に、モデルをサンプリングするためのプロンプトを逐語的に指定することはありません。その代わりに、クエリの一部としてさまざまな入力タイプを指定すると、Responses API サーバーは、この情報をモデルが使用するように設計されたプロンプトにどう構造化するかを決定します。このプロンプトは「項目のリスト」と考えることができます。このセクションでは、クエリがどのようにしてそのリストに変換されるかを説明します。
最初のプロンプトでは、リスト内の各項目がロール(役割)に関連付けられています。ロール は関連するコンテンツの重要度を示し、優先度の高い順に system、developer、user、assistant のいずれかの値になります。
Responses API(新しいウィンドウで開く) は、多くのパラメーターを含む JSON ペイロードを受け取ります。ここでは次のの3つに焦点を当てます。
インストラクション(新しいウィンドウで開く):モデルのコンテキストに挿入されるシステム(または開発者)メッセージtools(新しいウィンドウで開く):モデルがレスポンスを生成する際に呼び出す可能性のあるツールの一覧input(新しいウィンドウで開く):モデルに対するテキスト、画像、またはファイルのリスト
Codex では、instructions フィールドは、指定されている場合は ~/.codex/config.toml 内の model_instructions_file(新しいウィンドウで開く) から読み込まれます。指定がない場合は、モデルに関連付けられた base_instructions(新しいウィンドウで開く) が使用されます。モデル固有の指示は Codex リポジトリに格納されており、CLI に組み込まれています(例:gpt-5.2-codex_prompt.md(新しいウィンドウで開く))。
tools フィールドは、Responses API によって定義されたスキーマに準拠したツール定義のリストです。Codex の場合、これには Codex CLI によって提供されるツール、Codex で利用可能にすべき Responses API によって提供されるツール、および通常は MCP サーバー経由でユーザーによって提供されるツールが含まれます。
最後に、JSON ペイロードの input フィールドは項目のリストです。Codex 次の項目を(新しいウィンドウで開く) input に挿入します。その後、ユーザーメッセージを追加します。
1.toolsセクションで定義されたCodex 提供のshellツールにのみ適用されるサンドボックスを説明する、 role=developer のメッセージ。つまり、MCP サーバーから提供されるツールなどの他のツールは Codex によってサンドボックス化されておらず、独自のガードレールを適用する責任があります。
このメッセージはテンプレートから作成されており、主要なコンテンツは Codex CLI にバンドルされている Markdown のスニペット(例:workspace_write.md(新しいウィンドウで開く) や on_request.md(新しいウィンドウで開く))から取得されます。
2. (オプション)role=developer を持つメッセージで、その内容はユーザーの config.toml ファイルから読み取った developer_instructions の値です。
3. (オプション)role=userを持つメッセージで、その内容は「ユーザー指示」であり、単一のファイルから取得されるのではなく、複数のソースから集約され(新しいウィンドウで開く)ます。一般的に、次のようなより具体的な指示は後に示されます。
$CODEX_HOME内のAGENTS.override.mdおよびAGENTS.mdの内容- デフォルトで32 KiBの制限内で、
cwdのGit/project ルート(存在する場合)からcwd自体までの各フォルダーを確認し、AGENTS.override.md、AGENTS.md、またはconfig.toml の project_doc_fallback_filenamesで指定された任意のファイル名のいずれかの内容を追加します。 - いずれかの スキル(新しいウィンドウで開く) が設定されている場合:
- スキルに関する簡潔な前置き
- 各スキルの スキルメタデータ(新しいウィンドウで開く)
- スキルの使い方(新しいウィンドウで開く)に関するセクション
4. エージェントが現在動作中のローカル環境を説明する role=user のメッセージ。これは 現在の作業ディレクトリとユーザーのシェルを指定します(新しいウィンドウで開く):
Codex が上記のすべての計算を行って input を初期化した後、会話を始めるためにユーザーメッセージを追加します。
前の例では各メッセージの内容に焦点を当てましたが、input の各要素は次のように、type、role(新しいウィンドウで開く)、content を含む JSON オブジェクトであることに注意してください。
Codex は Responses API に送信する完全な JSON ペイロードを構築した後、~/.codex/config.toml における Responses API エンドポイントの構成に応じて、Authorization ヘッダーを付けた HTTP POST リクエストを作成します(指定されている場合は、追加の HTTP ヘッダーとクエリパラメータも追加されます)。
OpenAI Responses API サーバーがこのリクエストを受信すると、JSON を使用してモデルへのプロンプトを次のように導出します(念のため、Responses API のカスタム実装では異なる選択が行われる可能性があります)。
ご覧のとおり、プロンプト内の最初の3つの項目の順序は、クライアントではなくサーバーによって決定されます。ただし、これら3つの項目のうち、toolsとinstructionsはクライアントによって決定され、システムメッセージの内容のみがサーバーによっても制御されます。これらの後に JSON ペイロードからの input が続き、プロンプトが完成します。
プロンプトが完成したので、モデルをサンプリングする準備ができました。
Responses API へのこの HTTP リクエストにより、Codex における会話の最初の「ターン」が開始されます。サーバーは Server-Sent Events(SSE(新しいウィンドウで開く))ストリームで応答します。各イベントの data は、"response" で始まる "type" を持つ JSON ペイロードで、次のようなものです(イベントの完全な一覧は API ドキュメント(新しいウィンドウで開く) にあります)。
Codexは イベントのストリームを取り込み(新しいウィンドウで開く)、クライアントが利用可能な内部イベントオブジェクトとして再公開します。response.output_text.delta のようなイベントは、UI でのストリーミングをサポートするために使用されますが、response.output_item.added のような他のイベントはオブジェクトに変換され、後続の Responses API 呼び出しのために input に追加されます。
Responses API への最初のリクエストに、2 つの response.output_item.done イベント(1 つは type=reasoning、もう 1 つは type=function_call)が含まれていると仮定します。これらのイベントは、ツール呼び出しへの応答を用いて再度モデルにクエリする際に、JSONのinputフィールドにおいて表現される必要があります。
後続のクエリの一部としてモデルをサンプリングするために使用されるプロンプトは、結果として次のようになります。
特に、古いプロンプトが新しいプロンプトの正確な接頭辞(プレフィックス)になっていることに注意してください。これは意図的なもので、プロンプトキャッシュ(パフォーマンスに関する次のセクションで説明します)を活用できることから、後続のリクエストの効率性が大幅に向上します。
エージェントループの最初の図について振り返ると、推論とツール呼び出しの間に多くの反復が発生する可能性があることが分かります。プロンプトは、最終的にアシスタントメッセージを受信し、ターンの終了を示すまで増え続ける可能性があります。
Codex CLI では、アシスタントメッセージをユーザーに表示し、コンポーザーに焦点を当てて、会話を続ける「順番」がユーザーにあることを示します。ユーザーが応答した場合、新しいターンを開始するために、前のターンのアシスタントメッセージとユーザーの新しいメッセージの両方を、Responses API リクエストの input に追加する必要があります。
再度申し上げますが、会話を継続しているため、当社がResponses API に送信する input の長さは増加し続けます。
では、この増え続けるプロンプトがパフォーマンスにどのような影響を与えるのかを検討しましょう。
「ちょっと待って、会話の過程で Responses API に送信される JSON の量という点では、エージェントループは 二次的 ではないのか?」と疑問に思うかもしれません。おっしゃるとおりです。Responses API は、この問題を軽減するためのオプションの previous_response_id(新しいウィンドウで開く) パラメーターをサポートしていますが、Codex は現在これを使用していません。主な理由は、リクエストを完全にステートレスに保ち、ゼロデータ保持(ZDR)構成をサポートするためです。
previous_response_id を回避することで、Responses API の提供者にとっては、すべてのリクエストがステートレスであることが保証されるため、物事が簡素化されます。これにより、ゼロデータ保持(ZDR)(新しいウィンドウで開く)を選択したお客様をサポートすることも容易になります。なぜなら、previous_response_id をサポートするために必要なデータを保存することは、ZDR と相反するからです。ZDR のお客様は、関連する encrypted_content をサーバーで復号化できるため、以前のターンの独自の推論メッセージから得られるメリットを失うことはありません。(OpenAI は ZDR のお客様の復号鍵を保持しますが、データは保持しません。)ZDR をサポートするための Codex への関連変更については、PR #642(新しいウィンドウで開く) と #1641(新しいウィンドウで開く) を参照してください。
一般的に、モデルのサンプリングコストはネットワークトラフィックのコストの大部分を占めるため、効率化の主な対象となります。これがプロンプトキャッシュが非常に重要である理由です。以前の推論呼び出しでの計算を再利用できるからです。キャッシュヒットが発生すると、モデルのサンプリングは二次的ではなく線形的になります。当社のプロンプトキャッシング(新しいウィンドウで開く)のドキュメントでは、これについてさらに詳しく説明しています。
キャッシュヒットは、プロンプト内でプレフィックスが完全に一致する場合にのみ可能です。キャッシュの利点を活用するためには、指示や例のような静的コンテンツをプロンプトの先頭に配置し、ユーザー固有の情報などの可変コンテンツを最後に置いてください。これは画像やツールにも適用され、リクエスト間で同一である必要があります。
これを踏まえて、Codex でどのような種類の操作が「キャッシュミス」を引き起こす可能性があるかを考えてみましょう。
- 会話の途中でモデルが使用できる
toolsを変更します。 - Responses API リクエストの対象である
モデルを変更します(実際には、元のプロンプトの3番目の項目が変更されます。そこにはモデル固有の指示が含まれています)。 - サンドボックスの構成、承認モード、または現在の作業ディレクトリを変更します。
Codex チームは、プロンプトキャッシュを損なう可能性のある Codex CLI の新機能を導入する際に、細心の注意を払う必要があります。例として、MCP ツールの初期サポートでは、ツールを一貫した順序で列挙できないバグ(新しいウィンドウで開く)が発生し、キャッシュミスを引き起こしました。MCP ツールは、MCP サーバーがnotifications/tools/list_changed(新しいウィンドウで開く)通知を通じて提供するツールのリストをその場で変更できるため、特に扱いが難しいことに注意してください。長い会話の途中でこの通知を受け入れると、高コストなキャッシュミスが発生する可能性があります。
可能な場合は、会話の途中で発生する構成の変更について、以前のメッセージを変更するのではなく、変更を反映する新しいメッセージをinputに追加して対応します。
- サンドボックスの設定または承認モードが変更された場合、元の <permissions instructions>(新しいウィンドウで開く) の項目と同じ形式で、新しい
role=developerメッセージを挿入します。 - 現在の作業ディレクトリが変更された場合、元の <environment_context>(新しいウィンドウで開く) と同じ形式で、新しい
role=userメッセージを挿入します。
当社はパフォーマンス向上のため、キャッシュヒットを確実にするためのあらゆる努力をしています。管理すべき重要なリソースがもう1つあります。それはコンテキストウィンドウです。
コンテキストウィンドウが不足しないようにするための一般的な戦略は、トークン数がしきい値を超えた場合に会話をコンパクト化することです。具体的には、 inputを会話を代表する新しい、より小さな項目リストに置き換え、エージェントがこれまでに何が起こったかを理解しながら続行できるようにします。圧縮の初期実装(新しいウィンドウで開く)では、ユーザーが手動で /compact コマンドを呼び出し、既存の会話にカスタム指示を加えて 要約(新しいウィンドウで開く)を行うために Responses API をクエリする必要がありました。Codex は、サマリーを含む結果のアシスタントメッセージを、後続の会話ターンの新しい入力(新しいウィンドウで開く)として使用しました。
それ以来、Responses API は、圧縮をより効率的に行う特別な /responses/compact エンドポイント(新しいウィンドウで開く) をサポートするように進化してきました。これは、コンテキストウィンドウを解放しながら会話を続行するために、前の input(新しいウィンドウで開く) の代わりに使用できる 項目のリストを返します。このリストには、元の会話に対するモデルの潜在的な理解を保持する不透明な encrypted_content 項目を含む、特別なtype=compaction項目が含まれています。これにより、auto_compact_limit(新しいウィンドウで開く) を超えた場合に、Codex はこのエンドポイントを自動的に使用して会話を圧縮します。
当社はCodex エージェントループを導入し、モデルにクエリを行う際にCodexがどのようにコンテキストを構築し、管理するかについて解説しました。その過程で、Responses API 上にエージェントループを構築するすべての方に適用される実践的な考慮事項とベストプラクティスを強調しました。
エージェントループは Codex の基盤を提供しますが、これは始まりに過ぎません。今後の投稿では、CLI のアーキテクチャを詳しく掘り下げ、ツールの使用方法の実装について探り、Codex のサンドボックスモデルをより詳細に見ていきます。


