研究紹介
Claude Code の effort level を 45 回測ったら、答えはほぼ変わらなかった
· 読了まで約9分
目次
claude code effort levels で検索すると、出てくる説明はどれも同じ形をしている。low は機械的な作業向け、max は難しい問題向け、上げれば答えがよくなる。公式ドキュメントは設定の仕様を説明していて、その周りの記事は「どの段階がどれくらい賢いか」を並べている。
同じタスクを 5 段階で回して、返ってきたものを見せている記事が無い。
そこで測った。3 タスク × 5 段階 × 3 試行の 45 セッション、モデルは Opus 5、採点はコードを実際に実行して作った正解と突き合わせる。答えはほとんど動かなかった。動いたのはそれ以外だった。
測り方
effort はコマンドラインのフラグで渡せるので、設定を触らずに比較できる。
claude -p "$PROMPT" --effort low --output-format stream-json --verbose
1 回ごとに新しいセッションを立てるので、前の回の内容は持ち越さない。数字は JSON ストリームの result イベントから取る。所要時間の duration_ms、ターン数の num_turns、出力トークンの usage.output_tokens、そして usage.output_tokens_details.thinking_tokens —— effort の効き目が実際に出るのは最後のこれ。
タスクは深さの違う 3 つを用意した。
- T1 — 17 行の CSV を Markdown の表にする。答えは 1 つに決まり、考えることが無い
- T2 — 41 ファイル・200 個のエクスポート関数から、
""を渡すと例外を投げるものを全部挙げる - T3 — 同じ問いを、流し読みでは解けないように作ったコードベースに対して投げる
T2 は grep では解けない。危険な処理は helpers.ts 側にもあるので呼び出し側だけ見ても分からず、しかもガードはランダムに入っていて、効くものと効かないものが混ざっている。
export function mod004Handler2(input: string): string {
if (!input) return "none"; // "" を弾く。安全
return parseTag(input);
}
export function mod017Service2(input: string): string {
if (input === null) return "none"; // "" は null ではない。素通しする
return pickId(input); // "".match(/id=(\d+)/) は null -> null[1] で例外
}
T3 はもう一段深い。helper が 3 段のチェーンになっていて、降りていく途中で引数が書き換わる。
// helpers-b.ts
export function decorateTag(raw: string): string {
return takeSecond(raw + ",fallback"); // "" が ",fallback" になる -> 例外にならない
}
export function normalizeTag(raw: string): string {
return takeSecond(raw.trim()); // "" のまま -> takeSecond が例外を投げる
}
見た目が同じ 2 つの呼び出しが、3 段先で逆の答えになる。200 個のうち 86 個が深さ 3、78 個が深さ 2 で、167 個はどこかで引数が変換される。
正解は自分の生成スクリプトを信じずに作った。検証スクリプトが型注釈を落として生成物を Node に読み込ませ、200 個の関数を "" で呼んで、実際に例外を投げたものを記録する。生成器の主張と実行時の挙動は、2 つのコードベース合わせて 400 関数すべてで一致した。もう 1 本のスクリプトは各回のツール呼び出しを走査して、正解ファイルや ~/.claude/projects/ に残る過去セッションのログを覗いていないかを見る。45 回すべてで該当ゼロ。
45 回の結果
T1 — CSV から Markdown の表(答えが 1 つに決まる)
| effort | n | 秒 | ツール呼び出し | 思考 tok | 出力 tok | 正答 |
|---|---|---|---|---|---|---|
| low | 3 | 7.3 | 1.0 | 0 | 498 | 100% |
| medium | 3 | 7.0 | 1.0 | 0 | 498 | 100% |
| high | 3 | 6.5 | 1.0 | 0 | 498 | 100% |
| xhigh | 3 | 11.8 | 2.0 | 0 | 594 | 100% |
| max | 3 | 9.1 | 1.7 | 0 | 561 | 100% |
T2 — 200 関数・間接参照が 1 段
| effort | n | 秒 | ツール呼び出し | 思考 tok | 出力 tok | 再現率 |
|---|---|---|---|---|---|---|
| low | 3 | 44.2 | 5.7 | 2,088 | 3,148 | 99.5% |
| medium | 3 | 80.5 | 20.7 | 3,931 | 6,549 | 100% |
| high | 3 | 107.1 | 45.3 | 5,795 | 10,052 | 100% |
| xhigh | 3 | 107.1 | 44.7 | 6,238 | 10,316 | 100% |
| max | 3 | 136.9 | 46.0 | 10,007 | 14,372 | 100% |
T3 — 同じ問い・helper が 3 段チェーン
| effort | n | 秒 | ツール呼び出し | 思考 tok | 出力 tok | 再現率 |
|---|---|---|---|---|---|---|
| low | 3 | 80.3 | 7.3 | 5,602 | 7,062 | 100% |
| medium | 3 | 120.3 | 22.7 | 8,312 | 11,245 | 100% |
| high | 3 | 147.4 | 23.7 | 11,487 | 14,430 | 100% |
| xhigh | 3 | 187.8 | 35.3 | 14,730 | 18,497 | 100% |
| max | 3 | 199.7 | 46.0 | 16,246 | 20,691 | 100% |
適合率はどの段階でも 100% だった。例外を投げない関数を挙げてしまう機会は 45 回で 1,995 回あったが、誤検出は 1 件も無い。実験全体で間違いは 1 個 —— T2 の low が 1 回だけ落とした 1 関数だけ。
効いたところ、効かなかったところ
考えることが無いタスクでは effort は何もしない。T1 は 5 段階すべてで思考トークンが 0 だった。low・medium・high は出力トークンまで完全に一致していて、3 試行とも 498。xhigh と max だけ Read を 1 回増やして自分の答えを検算するが、結果は同じものが出てくる。
effort が動かしていたのは探索のやり方だった。low は T2 を一息に読む。
Bash: cat mod0*.ts mod1*.ts
high は同じファイルを 1 つずつ開き、そのあと awk で区切りを入れて表示し直してもう一度眺める。ツール呼び出しは low の 5.7 回に対して 45 回前後。丁寧になっているのは見ていて分かる。ただしこのタスクでは、丁寧さが答えを変えていない。low の時点で答えは出ているからだ。
唯一の見落としは、いかにもな型だった。low が落とした関数はこれ。
export function mod017Service2(input: string): string {
if (input === null) return "none"; // "" は null ではないので素通しする
return pickId(input); // 例外を投げる
}
ガードがあるのを見て、そのまま安全だと判断している。追加の推論が拾うはずの誤りそのもので、実際 1 段上げれば消えた —— medium 以上はこの関数を 12 回すべてで正しく挙げている。effort が無意味なわけではない。ただ、この形の問題では、上げて埋まる差が 1,995 個中 1 個だったというだけだ。
外した予想
T2 は天井を探すつもりで作ったのに、low が 100% を出した。タスクが簡単すぎたのだと考えて T3 を作った —— 3 段チェーン、途中での引数の書き換え、半分しか効かないガード。low はそれも 3 回とも 100% だった。
effort が効くタスクを 2 回作ろうとして 2 回とも外したことになる。200 関数の到達可能性を追う作業は、Opus 5 にとっては「もっと考える必要がある問題」の側に入っていないらしい。必要なのはファイルを読むことで、読み終えた時点で low はもう答えを持っている。max が余分に使う 11,000 の思考トークンには、見つけるものが残っていない。
きれいな階段になると思っていたのも外れた。xhigh は high と max の中間に来ない。T2 では high と 0.1 秒差で並び(107.1 対 107.1)、T3 では max のほうに寄る。この 2 段を分けているものが何なのかは、今回の計測には出てこなかった。
測っていないこと
- どのタスクも正解が 1 つに決まる。設計の判断、リファクタの方針、筋の通る選択肢から選ぶ種類の仕事は測っていない。effort が効くとしたらそちら側だろう
- Opus 5 だけで測った。Sonnet や Haiku なら天井の位置は違うはず
- 各 3 試行。T2 の low の 99.5% は 3 回中 1 回の見落としなので、頻度としての精度は低い
- 1 回きりの
claude -pセッション。対話を重ねる使い方では別の結果になりうる - コストは途中で載せるのをやめた。同じ条件の 2 回でもプロンプトキャッシュのヒット率で 3 割動いてしまい、段階の比較に使えない。素直なのはトークン数のほう
結局どう設定するか
既定のまま触らないでいい。答えを検証できる仕事では、low と max の差は 1,995 個中 1 個で、その 1 個に所要時間 2.5〜3 倍と思考トークン最大 4.8 倍を払うことになる。
上げる理由があるとすれば「答えがよくなるから」ではない。low はときどきガードを額面どおりに受け取る、という一点で、そこは 1 段上げれば今回は毎回直った。手で確かめられないコードベースについて質問するなら、medium は安い保険になる。medium から先は、この種の問いに関しては待ち時間を買って、同じファイルを 2 回読むところを眺めることになる。
この結論が外れるとすれば、今回測らなかったほう —— 正解が 1 つに決まらない仕事の側だろう。max が medium に安定して勝つタスクを持っている人がいるなら、測るべきはそちらで、今回のこれではない。