技術約10分で読めます

Codexの子エージェントの時間とトークンをログから集計するビューアを作った

いけさん目次

前回、Codexのオーケストレーションを組み直して使用量を抑えた。
それでも1つの処理に何時間もかかることがあった。

どこで何が動いているかは、PC上のプロセスを見れば大まかには分かる。
ただ、オーケストレーションで動いている子エージェントが何をしているかまでは、プロセスからは取れなかった。
一部のリポジトリでは、子が起動・終了するたびにMarkdownファイルへ状況を記録させて追っていたが、指示頼みだと記録が抜けたりして、全リポジトリで運用するのは無理がある。

そこでエージェント自身に記録させるのをやめて、CodexとClaude Codeが残しているセッションログから、子の状態と時間を組み立てるビューアを作った。
ログを追っていくとトークンの量も取れたので、30日分をまとめて集計した。

環境

項目内容
マシンMac mini(M4)
Codex CLI0.154.0
Claude Code2.1.274
ビューアSwiftUIの画面と、ログを集計するPythonスクリプト(標準ライブラリのみ)
集計期間2026年8月19日〜9月18日の30日間、257セッション(子のいるもの129)

子エージェントの実体はスレッド

最初は pslsof でプロセスを探し、作業フォルダとセッションログの更新時刻から最後の活動時刻を出す一覧を作っていた。
これで「どこでどのタスクが動いているか」は分かる。
ただ、Codexにオーケストレーションさせている最中でも、codexのプロセスは1つしか見えなかった。

lsof でそのプロセスが開いているファイルを確認すると、~/.codex/sessions/ の下にあるセッションログ(rollout-*.jsonl)を6本同時に開いていた。
子は別のプロセスではなく同じプロセスの中のスレッドで、スレッドごとにログ(rollout)を1本書いている。
各rolloutの1行目(session_meta)に親子関係が入っていた。

項目意味
parent_thread_id親のスレッドID親子をつなぐ
agent_path/root/review_worker親が付けた子の役割名
agent_nicknamePasteurCodexが付ける子の呼び名
source.subagent.thread_spawn.depth1親から数えた深さ

子が何をしたかも、同じrolloutに残っている。
実行したコマンド、apply_patch で編集したファイル、wait_agent での子待ち、task_complete の完了報告と所要時間が平文で入っていた。
親が子に渡した指示本文(spawn_agentsend_messagemessage)だけは、平文では残っていなかった。

Claude Codeの子(Agentツールで起動したサブエージェント)は、会話ログと同じ場所の <セッションID>/subagents/agent-*.jsonl に書かれている。
隣の .meta.json に、起動時の説明、エージェントの種類、モデル、親の呼び出しID(toolUseId)が入っていた。

ログから時間を分類する

どちらのログも、全イベントに時刻が付いている。
イベントを順に追ってスレッドの状態を遷移させていくと、各状態の滞在時間を集計できる。
ビューアを開いていなかった間の分も、あとから同じように組み立てられる。

状態始まるイベント中身
思考ターン開始、ツールの結果が返った時モデルが次の一手を出すまで。APIの待ち時間を含む
ツールツールを呼んだ時結果が返るまで
子待ちwait_agentsleep、Claude Codeの前面の子を呼んだ時待っている間に子が作業していた時間
その他の待ち同上待っていたが、作業中の子がいなかった時間
入力待ちtask_completeturn_abortedend_turn、中断次の入力が来るまで
flowchart TD
  A[入力を受ける] --> B[思考]
  B --> C[ツール]
  C --> B
  B --> D[子待ち]
  D --> B
  B --> E[入力待ち]
  E --> A

ターンを終えて止まっている間に子が作業していた時間も、子待ちに数えている。
Claude Codeのバックグラウンドの子のように、親が待つ呼び出しをせずに止まる場合があるため。
内訳の合計はプロセスの稼働時間と一致し、30日分257セッションで合計がずれたものはなかった。

rolloutは1本で17MB規模になることもあり、3秒ごとにファイル全体をパースし直すと重くて耐えられない。
処理済みのバイト位置と積算値を保持しておき、追記された分だけを差分処理している。

数え方の調整

最初に組んだ数え方は、実データに当ててみるといくつかズレがあった。

最初の数え方出た数字実際に起きていたこと直し方
子待ちは wait_agent の間だけ子21体のセッションで親の実活動99%親は sleep 49回と list_agents 76回で子を待っていたsleep も待ちに数える。直した後は実活動82%、子待ち13%
Claude Codeの子は end_turn で完了子の完了率50%、思考1回1時間34分直近の子300本のうち167本は end_turn を書かず、文章だけの応答で終わっていた最後が文章だけの応答なら完了とみなす。完了率100%
画面は開いているrolloutだけを見る親の「その他の待ち」1時間19分Codexは完了した子のrolloutを閉じる同じセッションの閉じた子のrolloutも集計に含める。その他の待ちはほぼ0
Claude Codeのトークンは応答ごとに1回数える出力が少なく出る1つの応答が複数行に分かれて記録され、複数行に分かれた1,386件のうち604件は行を追うごとに値が増えていた同じ応答は最後の値で数える

Codexの子のツール時間が短すぎる理由も調べた。
Codexは長いコマンドをバックグラウンドで走らせ、出力を確認する呼び出し(write_stdin)を繰り返している。
gpt-6-astraの子2体で集計したところ、出力を確認する呼び出しの前の時間は全体の1%未満だった。
思考の時間は、ほぼモデルが次の一手を出すまでの待ち時間だった。

1つのセッションの内訳

Codex(親はgpt-5.6-sol medium)で、業務リポジトリの未処理の課題をまとめて処理させたセッションを確認した。

現在タブ。公開用の表示でフォルダ名・タイトル・コマンドを伏せている

現在タブでは、作業フォルダごとにエージェントを並べ、Codexの親の下に子を表示する。
親の行には子の状態(完了・中断)ごとの数や、中断のまま残っている子などの要確認項目をまとめた。
その下の帯グラフは起動からの時間の内訳(思考・ツール・子待ち・入力待ち)を示し、右端にトークン量を並べている。
スクリーンショットは公開用の表示に切り替え、フォルダ名、タイトル、子の名前、コマンドとファイル名、完了報告を伏せた。

取得時点のこのセッションの集計は次のとおり。

項目
総時間6時間33分
実活動(思考+ツール)1時間5分(17%)
子待ち4時間28分(68%)
入力待ち59分(15%)
10体(完了8、中断2)、同時に動いた子は最大2体
入力トークン193.8M(キャッシュ98%)
出力トークン668k(親129k、子538k)

親は総時間(6時間33分)の約7割にあたる4時間28分を、子の完了待ちに費やしていた。
入力トークンは193.8M(約1.9億トークン)で、その98%はキャッシュヒットだった。出力トークンは全体で668k(約67万トークン)で、その8割を子が占めていた。
子の側をモデルごとに分けた内訳は次のようになった。

子のモデル子の数呼び出し回数思考1回思考ツール出力トークン
gpt-5.6-luna max420612秒40分1分104k
gpt-5.6-sol high348510秒80分11分187k
gpt-6-astra high351519秒161分12分247k

gpt-6-astra highの3体は、呼び出し回数がsol highとほぼ同じで、思考1回が約2倍だった。
子の思考合計281分のうち、161分をastraが占めていた。

30日分の履歴

履歴タブは、終わったセッションも含めてログから集計し、親の構成ごと、子のモデルごとにまとめる。
集計はログとは別に保存しているため、元データが消えたあとも残る。

履歴タブの比較表

セッション一覧には、セッションごとの時間の内訳、トークン、子の完了・中断、子のモデルの内訳を表示している。

履歴タブのセッション一覧

親の構成ごと

直近30日の子のいるセッションのうち、同じ構成が2件以上あったもの。
割合は各セッションの割合の平均ではなく、時間の合計どうしの比で出している。

親の構成件数総時間(中央値)実活動子待ち入力待ち子/件子の完了率入力/件(中央値)キャッシュ出力/件(中央値)
codex gpt-5.6-sol xhigh731時間16分31%19%50%11.995%33.5M97%151k
claude claude-fable-5-1136時間18分28%3%70%27.3100%40.0M93%277k
claude claude-fable-5116時間23分24%6%70%17.099%54.2M96%627k
codex gpt-6-astra low81時間43分39%33%24%15.097%30.9M98%164k
codex gpt-5.6-sol medium76時間33分15%27%58%12.688%65.5M97%205k
claude claude-opus-5623時間14分5%3%91%27.599%42.5M97%258k
claude claude-sonnet-525分30%70%0%1.0100%809k78%27k
codex gpt-6-astra medium27時間5分63%2%34%15.0100%202.1M98%689k
codex gpt-5.6-sol high216時間43分17%58%11%152.094%423.5M97%1.8M
codex gpt-6-astra xhigh212時間51分8%25%68%29.598%189.7M96%1.0M

入力待ちには、指示を投げたあとに人間が放置していた時間がそのまま含まれる。
この入力待ちを除いて計算すると、Codex(96件)の親は実活動53%・子待ち45%とほぼ半々を子の待機に費やしていた。
対してClaude Code(33件)は実活動85%・子待ち15%で、親自身が動いている時間が大半を占める。

子が3体以上いたセッションで同時に動いた子の最大数は、Codexが中央値4体(69件中25件は2体以下)、Claude Codeが中央値7体だった。

子のモデルごと

子が5体以上いたモデル。
表の列は次の意味で出している。

意味
実働思考+ツール。子1体ごとの中央値
ツール比率実働のうち、ツールの結果を待っていた時間
思考1回思考の合計を呼び出し回数で割った値
入力/子子1体あたりの入力トークン。キャッシュヒット分を含む
キャッシュ外の入力/子入力からキャッシュヒット分を除いた分
出力/思考1分思考1分あたりの出力トークン。推論の分を含む
子のモデル子の数完了実働(中央値)ツール比率思考1回入力/子キャッシュキャッシュ外の入力/子出力/子出力/思考1分
claude claude-sonnet-5690100%5分0%1分389k79%81k17k3k
codex gpt-5.6-luna medium48797%3分10%10秒1.7M95%76k9k2k
codex gpt-5.6-sol medium42496%5分4%13秒4.4M97%119k18k2k
codex gpt-5.6-sol xhigh28992%4分5%13秒2.8M96%113k15k2k
codex gpt-5.6-luna max9091%9分3%17秒5.2M96%220k34k2k
codex gpt-5.6-terra medium7299%5分6%13秒2.1M96%77k12k1k
codex gpt-5.6-sol high3488%12分5%14秒9.0M97%232k36k2k
codex gpt-5.6-luna xhigh3087%13分4%13秒10.9M97%304k55k3k
codex gpt-6-astra xhigh13100%11分1%34秒2.3M95%111k22k1k
claude claude-opus-51292%2分1%36秒365k79%76k2k678
codex gpt-6-astra high978%8分6%19秒8.0M98%186k32k1k
codex gpt-6-astra medium8100%2分1%32秒2.2M95%119k18k2k
claude claude-fable-57100%10分21%7秒902k69%280k40k5k
codex gpt-5.6-luna low6100%56秒2%7秒211k92%17k2k2k
claude claude-haiku-4-5-202510015100%1分32%3秒515k89%55k5k6k
codex gpt-5.6-terra high580%3分3%17秒824k93%60k11k1k

子の実働のうちツール実行は、Codexで5%、Claude Codeで1%だった。
残りの95%以上はモデルの応答待ちで、実際のコマンド実行やファイル操作に費やされた時間はごくわずかだった。
思考1回は、gpt-5.6のsolとlunaとterraが7〜17秒、gpt-6-astraが19〜34秒だった。

処理トークン

エージェントは呼び出しのたびに、それまでの会話を入力として送り直す。
そのため入力トークンは大きくなるが、大半はキャッシュヒットになる。

項目Codex(96件)Claude Code(33件)
入力トークンの合計8,507.1M1,905.8M
うちキャッシュ97%95%
出力トークンの合計35.3M17.6M
出力のうち子の分68%67%

30日分の入力トークンはCodexが8,507.1M(約85億トークン)、Claude Codeが1,905.8M(約19億トークン)で、どちらも95%以上がキャッシュだった。
出力トークンはCodexで35.3M(約3,530万トークン)、Claude Codeで17.6M(約1,760万トークン)で、その約3分の2(67〜68%)を子が占めていた。

Codexの子の入力は1体あたり0.2M〜10.9Mで、キャッシュ外の入力は17k〜304kだった。
出力は1体あたり2k〜55kで、思考1分あたりの出力はCodexのどのモデルも1k〜3kだった。
gpt-6-astraの思考1分あたりの出力は1k〜2kで、下限に近い水準だった。

トークン量の数え方はランタイムで違う。

ランタイム数え方
Codexスレッドごとの累計(token_counttotal_token_usage)の最後の値。コンテキスト圧縮(compact)を挟んでも減らず、1回ごとの使用量の合計と一致した。親の値に子の分は含まれない
Claude Code応答ごとの usage。入力にはキャッシュ作成とヒット分を含める

この数字の限界

今回の集計値を見る上での前提と制約をまとめておく。

限界内容
比較の条件同じ作業を構成違いで流した比較ではない。作業内容も人が離れていた時間もセッションごとに違い、gpt-6-astraを親にしたセッションは構成ごとに2〜8件しかない
思考の中身APIの待ち時間や順番待ちを含み、モデルの推論そのものとは分けられない
Codexのツール比率子がツールの結果を待って止まっていた時間の割合で、バックグラウンドで走っていたコマンドの実行時間は含まない
ログの形式どちらも公開された仕様ではなく、手元のCodex CLI 0.154.0とClaude Code 2.1.274の記録で確かめただけ。2026年1月のCodex 0.78.0のログはターンの開始・終了を記録しておらず、内訳を出せなかった
トークン量ログ同士の整合までしか確かめておらず、請求額とは照合していない