8月4日、0xkatoが「What Codex Actually Sends to the Model」と題した記事を公開した。「Reply with pong.」という16文字のプロンプトを入力しただけで、Codexはモデルに42,980バイト・約9,435トークンのHTTPリクエストを送信していた。ユーザーの入力が占めるのはそのうち約25トークン、全体の**わずか0.3%**にすぎない。残りの99.7%はCodex自身が付加したものだ。この記事はその内訳を実測・解析した調査報告である。
16文字のプロンプトが42,980バイトになる
実験の出発点はシンプルな問いだ。「Reply with pong.」という16文字のプロンプトを入力したとき、Codexはモデルに何を送るのか。
答えは42,980バイト、約9,435トークンのHTTPリクエストだった。ユーザーのプロンプトが占めるのはそのうち約25トークン、全体の**わずか0.3%**にすぎない。残りの99.7%はCodex自身が付加したものだ。
内訳を見ると、トークン消費の大半は3つの要素に集中している。
| 要素 | 内容 | ローカル推定 |
|---|---|---|
additional_tools developer item |
exec、wait等4つのトップレベルツール定義 |
約3,942トークン |
| Developer message | Codexのメイン指示文 | 約3,729トークン |
| User message | Reply with pong. |
25トークン |
ツール定義だけで全体の約42%を占める。execは単なるコマンド実行だけでなく、パッチ適用、画像検査、プラン更新といったネストされたツールを内包しており、collaboration名前空間には6つのサブツールが含まれる。
なお、これらのトークン数はAPIの課金単位ではなく、o200k_baseでローカル推定した値であることに注意が必要だ。
※編集部の考察:Copilot AgentやClaude Codeといった他のAIコーディングエージェントも同様のシステムプロンプト構造を持つとされるが、Codexのシステムプロンプトがこれほど大きい理由の一端は、ツール定義の粒度にある。サブツールをネストしてコマンド体系を一括定義する設計は、モデルの判断精度を高める反面、ベースラインのトークンコストが恒常的に高くなるというトレードオフを生む。
記録方法:ローカルプロキシで丸ごと捕捉
0xkatoはCodexのカスタムモデルプロバイダー機能を利用し、リクエストをローカルHTTPサーバーに向けた。サーバーはリクエストボディを保存し、固定のフェイクレスポンスを返す。実際のモデルは呼んでいない。
Codex client → local recorder → deterministic fake response
↑
request saved here
使用バージョンはCodex CLI 0.145.0、モデルはgpt-5.6-sol。
AGENTS.mdは全文がリクエストに乗る
プロジェクト指示ファイルAGENTS.mdは、リポジトリルートから起動ディレクトリまでの階層を走査して連結される。その内容は全文が最初のリクエストに含まれる。
合成マーカーを使ったサイズ測定の結果:
| 設定 | リクエストサイズ | ローカル推定 |
|---|---|---|
| ベースライン(AGENTS.mdなし) | 42,980バイト | 9,435トークン |
| 合成マーカー250個 | 48,927バイト | 11,965トークン |
| 合成マーカー1,000個 | 67,177バイト | 20,465トークン |
起動ディレクトリがどこかによって自動的に読み込まれる指示チェーンが決まる。子ディレクトリで起動した場合は、親のAGENTS.mdも含めた全階層分が送信される。2リクエストのトレースでは、250個のマーカーが両リクエストに存在していた。
AGENTS.mdはOpenAIの公式ドキュメントでも解説されているCodex固有の仕様で、プロジェクトごとの行動指針やコーディング規約をモデルに伝えるためのファイルだ。記述量が多いほどコンテキストを圧迫するため、内容の取捨選択が重要になる。
MCPツールの説明は遅延ロード
MCP(Model Context Protocol)サーバーを設定しても、初回リクエストにはカスタムツール名も説明も含まれない。代わりにexecインターフェースにMCP探索用のガイダンスが追加されるだけだ。MCPはAnthropicが提唱しAIエージェント間で広く採用されつつあるツール連携の標準仕様で、Codexも対応している。
ツール説明が実際にリクエストへ乗るのは、探索コマンドを実行した後のターンからだ。
| 設定 | 追加バイト | 追加トークン |
|---|---|---|
| 1サーバー、3ツール(各40マーカー) | +5,894バイト | +1,522トークン |
| 2サーバー、7ツール | +15,970バイト | +4,210トークン |
さらに5,000マーカーを持つツール説明を試したところ、切り捨て処理後も1,046マーカーが残り、次のリクエストが40,607バイト増加した。
バグ修正タスクで見るコンテキスト成長
Pythonの設定バグ修正タスクを10ターンで実行した際のリクエストサイズ推移:
| ターン | 直前のアクション | サイズ |
|---|---|---|
| 0 | 初期タスク | 44,189バイト |
| 5 | リグレッションテスト失敗 | 48,832バイト |
| 9 | diff確認完了 | 52,389バイト |
最終リクエストは初回より8,200バイト・約2,074トークン大きくなった。検索結果、ファイル内容、テスト結果、パッチ、diffがすべて蓄積されている。重複する出力はデデュープされない。ANSIエスケープシーケンスやPythonのスタックトレースもそのまま残る。
この「コンテキストの一方的な肥大化」こそが、この調査で最も実務的に重要な発見の一つだ。長時間のセッションほどトークンコストが加速度的に増加し、やがてコンパクションが発動する条件を作り出す。
ファイルと画像の扱い
リポジトリのファイルは自動送信されない。rg --filesでファイル名一覧を取得してもファイル名が追加されるだけで、内容は含まれない。明示的に読み込んだ後、初めてリクエストに乗る。.gitignoreに記載されたファイルも、明示的に読めばその内容が後続リクエストに含まれる点は注意が必要だ。
20,000行のログファイルは先頭と末尾のサンプルに切り捨てられたが、それでもリクエストは43,500バイトから87,561バイトに膨らんだ。
画像はdata:image/png;base64,…形式のデータURLとして送信される。大きな画像はCodexがリサイズ・再エンコードする。1,600×1,600 PNGに変換された画像のデータURLは71,666文字に達した。
コンパクション:履歴が別リクエストで圧縮される
コンテキストが一定サイズを超えると、Codexはコンパクションを実行する。カスタムプロバイダー経由では2段階になる。
- 蓄積した履歴全体とサマリープロンプトを送信(コンパクションリクエスト)
- サマリーを受け取り、元の会話を再構築(再開リクエスト)
強制トリガーで検証した結果:
- 初回リクエスト:42,030バイト・約9,378トークン
- コンパクションリクエスト:68,375バイト・約21,408トークン
- 再開リクエスト:42,646バイト・約9,500トークン
コンパクション後、ツール出力の生データは消え、モデルが生成したサマリーに置き換わる。元の詳細情報はサマリーの質に依存する形になる。コンパクション処理の実装はGitHubのソースコードで確認できる。
機械の境界を越えるもの・越えないもの
この実験が明らかにした境界線は明確だ。
最初のリクエストに含まれるもの:Codex指示文、ツール定義、AGENTS.mdチェーン、スキルメタデータ、環境コンテキスト、ユーザープロンプト。
アクション後に追加されるもの:読み込んだファイルの内容、コマンドの出力、MCP探索後のツール説明、添付した画像。
自動では送られないもの:未読のリポジトリファイル、.gitignore対象ファイル、.envファイル(ただし明示的に読めば送られる)。
この境界線を把握しておくことは、コスト管理だけでなくセキュリティの観点からも重要だ。.envファイルや機密ログを誤ってCodexに読ませた場合、その内容は以降のすべてのリクエストに乗り続けることになる。
詳細はWhat Codex Actually Sends to the Modelを参照していただきたい。