SolとTerraで同じタスクを走らせて、消費量を測る
先日、Codexのモデルラインナップを整理した記事を書きました。その末尾に「次は同じタスクをSolとTerraで走らせて、allowanceの減り方と結果の差を見たい」と書いたので、実際に測りました。
先に結果を書いておくと、3タスクのうち2つは両モデルの差分がバイト単位で完全に一致しました。そして測ろうとしていたallowanceは、この規模では分解能が足りず測れませんでした。どちらも想定と違いました。
前提として、私は普段Claude Codeを使っていて、Codexは比較検証で触る程度です。プランはPlusです。
測り方
codex exec を使って非対話で走らせます。条件を揃えるために付けたオプションが以下です。
codex exec --json --ignore-user-config \
-c approval_policy="never" \
-c model_reasoning_effort="medium" \
-m gpt-5.6-terra -s workspace-write "$(cat prompt.txt)"
--ignore-user-config が効きました。これを付けると ~/.codex/config.toml を読まなくなります。私の環境ではMCPサーバが3つ(context7、node_repl、computer-use)と、プラグインが複数、web_search = "live"、それにターン終了時のnotifyフックまで入っていました。
これらが片方の実行にだけ効いていたら比較になりません。1行で全部落とせるのは助かりました。
タスクごとにフィクスチャを丸ごとコピーして、それぞれ独立したディレクトリで実行します。会話履歴も持ち越しません。
題材は自作の小さなNodeプロジェクトです。受注金額を計算するライブラリという設定で、docs/spec.md に仕様を書いてあります。テストは node --test で、依存パッケージはゼロです。
用意したタスクは3つです。
- A(テスト修復): 明細の丸めが
Math.floorからMath.roundに変わった回帰。テストが3件落ちている状態から通す - B(機械的な追加): 仕様書に従って、3つのハンドラ関数に入力検証を追加する。検証関数自体は用意済み
- C(原因調査): 症状だけ伝える。テストは全部通っている状態で、実装が仕様と食い違っている
Cだけ少し工夫しました。リポジトリ内のテストはバグのある実装に合わせて書かれているので、npm test を実行しても何も分かりません。仕様書を読んで実装との矛盾に気づく必要があります。
採点用のテストはリポジトリの外に置いておき、実行後に当てました。
プロンプトはファイルに書いて固定しています。Cはこれです。
運用から不具合の報告が来ています。請求書に印字している明細行の金額を足し合わせた値と、システムが出す注文合計が一致しないことがある、という内容です。ずれる金額は数円程度です。
npm testは現在すべて通っています。原因を特定して修正してください。テストの修正が必要であれば直して構いません。
最初に詰まったところ
疎通確認で gpt-5.6-terra を指定したところ、400が返ってきました。
{"type":"error","status":400,"error":{"type":"invalid_request_error",
"message":"The 'gpt-5.6-terra' model requires a newer version of Codex.
Please upgrade to the latest app or CLI and try again."}}
手元のCLIが0.142.2で、Homebrewには0.147.0が来ていました。
前の記事でモデル名の一覧をドキュメントから書き写したのですが、そのモデルが自分の環境で使えるかどうかは確認していなかったことになります。ドキュメントを読んで整理した気になっていた、という話でもあります。
更新したら通るようになりました。ここで15分ほど使っています。
新しいモデルを試すときは、モデルIDだけでなくCodex CLI自体のバージョンも確認しておいたほうがよさそうです。モデルが存在していても、古いCLIからは利用できない場合があります。
allowanceは測れませんでした
これが今回いちばんの想定違いです。
Codex CLIには /status があって、対話セッション中なら残量が見えます。ただ codex exec は非対話なので使えません。
代わりに ~/.codex/sessions/ 配下のセッションファイルを見たところ、リクエストごとにレート制限の情報が記録されていました。
"rate_limits":{"limit_id":"codex","primary":{"used_percent":0.0,
"window_minutes":10080,"resets_at":1786926628},"secondary":null,
"plan_type":"plus"}
window_minutes が10080、つまり週次のウィンドウです。これで測れると思いました。
実際に6実行を挟んで前後を比べた結果がこれです。
| 時点 | used_percent |
|---|---|
| 実行前 | 0.0 |
| A-terra 〜 C-terra | 0.0 |
| C-sol の途中 | 1.0 |
| 実行後 | 1.0 |
刻みが1%でした。
6実行かけて0%から1%に動いただけで、しかもどの実行で増えたのかは分かりません。C-solの途中で切り替わっていますが、それは単に累積がそこで境界を越えただけで、C-solの消費が大きいという意味にはなりません。
前の記事で「1回の実行で出た数字を根拠にするのはやめておきます」と書きましたが、根拠にする以前に数字が出ませんでした。
週次ウィンドウを1%動かすには、この規模の実行を数百回積む必要がありそうです。
allowanceそのものは測定できなかったため、JSONLの
turn.completed に入っているトークン数を代わりに比較します。allowanceそのものではありませんが、少なくともモデル間の消費傾向を見る材料にはなります。
結果
6実行すべて成功し、テストも全部通りました。手戻り(追加で指示を出すこと)はゼロです。
| 実行 | 所要 | 入力計 | 非キャッシュ入力 | 出力 | うち推論 | コマンド実行 |
|---|---|---|---|---|---|---|
| A-terra | 26s | 98,350 | 8,750 | 824 | 26 | 8 |
| A-sol | 38s | 117,818 | 16,186 | 1,270 | 101 | 10 |
| B-terra | 23s | 63,121 | 7,825 | 666 | 13 | 4 |
| B-sol | 33s | 80,372 | 7,924 | 919 | 73 | 6 |
| C-terra | 52s | 135,882 | 11,978 | 2,166 | 211 | 10 |
| C-sol | 83s | 120,065 | 20,481 | 3,286 | 944 | 10 |
合計するとこうなります。
| モデル | 所要 | 入力計 | 非キャッシュ入力 | 出力 | うち推論 |
|---|---|---|---|---|---|
| Terra | 101s | 297,353 | 28,553 | 3,656 | 250 |
| Sol | 154s | 318,255 | 44,591 | 5,475 | 1,118 |
Solのほうが所要時間で約1.5倍、非キャッシュ入力で約1.6倍、出力で約1.5倍、推論トークンで約4.5倍でした。
差分が完全に一致しました
タスクAとBは、両モデルの git diff がバイト単位で一致しました。
タスクAは Math.round を Math.floor に戻す1行です。これは正解が一意なので、一致してもおかしくありません。
驚いたのはタスクBです。3箇所に検証を追加する作業ですが、追加した位置、空行を1行入れるかどうか、addLine で数量と単価をどの順で検証するか、まで同じでした。
export function addLine(cart, line) {
+ assertQuantity(line.quantity);
+ assertUnitPrice(line.unitPrice);
+
return {
手続きが決まっている作業では、上位モデルを使う理由が見当たらない、という結果になります。
ここはTerraのほうが所要時間で3割、出力トークンで4割少ないので、素直にTerraを選ぶべき場面だと思いました。
調査タスクだけ、やり方が分かれました
タスクCは両方とも原因を特定し、採点用テストにも合格しました。ただし直し方が違いました。
Terraの修正
Terraは仕様どおりの計算に戻すという直し方です。
export function lineTotal(line) {
return Math.floor(line.unitPrice * line.quantity * (1 + TAX_RATE));
}
Solの修正
Solは浮動小数の掛け算そのものをやめました。
const TAX_DENOMINATOR = 10;
export const TAX_RATE = 1 / TAX_DENOMINATOR;
export function lineTotal(line) {
const base = line.unitPrice * line.quantity;
const tax = Math.floor(base / TAX_DENOMINATOR);
return base + tax;
}
「数円ずれる」という報告に対して、丸めの順序だけでなく浮動小数そのものを疑った、という読み方ができます。
追加したテストも違っていて、Terraは pricing の層で不変条件を書き、Solは checkout の層、つまり報告された症状と同じ粒度でテストを足していました。
ここまで見て、Solのほうが筋が良いと書きたくなったのですが、念のため確認しました。
0円から200万円まで総当たりで両実装を比較したところ、結果が食い違う入力は1件もありませんでした。
mismatch count (base 0..2,000,000): 0
1.1 は倍精度では真の値よりわずかに大きい側に丸められるので、この範囲では Math.floor が下振れしません。
つまりSolの整数演算は、より防御的ではあるものの、この題材では出力が1円も変わりませんでした。
推論トークンをTerraの約4.5倍使って到達したのは、「より安全に見えるが、今回の入力範囲では結果が同じコード」でした。Solの考え方が無意味という話ではありませんが、この題材では実際の出力差にはつながりませんでした。
意図した差ではなかったので、ここは正直に書いておきます。
もっと桁の大きい金額や別の税率が絡む設計なら評価は変わったかもしれません。ただ、少なくとも今回の題材では差になっていません。
予想の答え合わせ
前の記事に見立てを書いておいたので、突き合わせます。
| 予想 | 結果 |
|---|---|
| A・Bでは有意な差は出ない | 当たり。差分が完全一致という強い形で |
| CではSolのほうが手戻りが少ない | 外れ。両方とも手戻りゼロ |
| allowance消費はTerraのほうが明確に少ない | トークンでは当たり。ただしallowance自体は測れず |
| 所要時間はTerraが単純に速いとは限らない | 外れ。全タスクでTerraが速かった |
外れた2つは、どちらも「Solが必要になる難易度に届いていなかった」で説明がつきます。
手戻りが発生しないタスクでは、手戻りの少なさで差はつきません。前の記事で「差が出なかった場合はタスクが簡単すぎた可能性を最初に疑う」と書いたので、その通りになりました。
この結果の限界
同じ条件で1回ずつしか回していないので、ぶれ幅は取れていません。特にタスクCの所要時間(52s対83s)は、もう一度回せば逆転する可能性があると思っています。
題材が小さいのも効いています。ファイル7つ、テスト7件のプロジェクトなので、コンテキストの積み方で差がつく余地がありません。
前の記事で「大きなリポジトリ、長い会話履歴が重なると消費が一気に増える」という報告に触れましたが、今回はその条件を作れていません。
実行順も固定です。各タスクでTerra、Solの順に走らせています。プロンプトキャッシュはモデルごとに別だと理解していますが、順序を入れ替えた確認まではしていません。
「Terraは常にSolより速い」「Solは4.5倍の推論を使う」と一般化できる実験ではありません。小規模な3タスクを各1回ずつ実行した結果です。今回は傾向を見るための第一回目の測定、と捉えるのがよさそうです。
所感
比較の設計を書き出したときは、条件を揃えるところが山場だと思っていました。実際に効いたのは --ignore-user-config の1行で、そこは拍子抜けするほど簡単でした。
詰まったのは別のところ、CLIのバージョンと、測定指標の分解能です。どちらも走らせてみるまで気づけませんでした。
内容としていちばん収穫だったのは、タスクBの完全一致です。
「上位モデルのほうが良い結果を出す」という前提で使い分けを考えていたのですが、正解が決まっている作業では出力が同一になり得る。それなら1.5倍の時間とトークンを払う理由はありません。
前の記事で「迷ったらTerraから」と書いたのは、実測でも支持される整理でした。
一方で、Solに払ったコストが無駄だったとも言い切れないと感じています。
タスクCで浮動小数まで疑ったのは、報告された症状に対する筋としては妥当です。今回はたまたま結果が同じでしたが、桁が大きい金額を扱う実務なら、その差が効く場面はありそうです。
「同じ結果に着地したが、着地の仕方が違う」を、どう評価に織り込むかは決めきれていません。
次は題材を大きくし、ファイル数と履歴の長さを増やしたときに差がどう変わるかを見たいと思っています。同条件で複数回実行してぶれ幅も取り、SolとTerraの差がどの難易度から現れるのかを確認する予定です。
それと、前の記事で触れた gpt-5.4 系のCodexからの引退は8月31日です。今回CLIのバージョンで引っかかったこともあり、設定ファイルにモデルIDを固定しているプロジェクトの確認は早めに済ませておきます。
参考
- Models | ChatGPT Learn
- Pricing | ChatGPT Learn
- Feature: Expose full usage/limits data in CLI (/status command) – openai/codex
ここまで読んでいただき、ありがとうございます。もしこの記事の技術や考え方に少しでも興味を持っていただけたら、ネクストのエンジニアと気軽に話してみませんか。
- 選考ではありません
- 履歴書不要
- 技術の話が中心
- 所要時間30分程度
- オンラインOK