研究紹介
Claude Code の /compact は 1 回いくらかかるのか(実測)
· 読了まで約10分
目次
/compact のコストを調べると、たいてい同じ Reddit のスレッドに行き着く。そこには自信たっぷりの答えが 2 つ並んでいる。片方は「セッションはサーバ側の KV キャッシュに乗っているので、払うのは要約を作る分だけ」。もう片方は「compact はセッション全体を読み直すので、毎朝でかい非キャッシュ読みを払っている」。さらに別の返信が「そもそも新しいセッションを立てたほうが安かったのでは」と続く。
誰も数字を出していない。公式ドキュメントも同様で、コストのページには「コンテキストが大きいほどトークンを使う」とあり、プラットフォーム側の compaction のページには「追加の compaction コストがかかる」と書いてある。どちらも正しいが、知りたいことには答えていない。
なので測った。
実験の組み方
Claude Code 2.1.246、--model sonnet(claude-sonnet-5)、effort は既定のまま。題材は生成した TypeScript のコードベースで、30 ファイル・約 256KB、各ファイルが 24 個のよく似た関数を export している。中身に面白みは無い。コンテキストを埋めるためのバラストで、サイズだけが分かっていればいい。
各ランは、セッション ID を固定したヘッドレスセッションとして作る。
claude -p --session-id "$SID" --model sonnet \
"Read all 30 files in src/ with the Read tool, one call per file. \
Then print one line per file: filename, number of exported functions."
compact は同じセッションを resume して打つ。
claude -r "$SID" -p "/compact"
計測は Claude Code 自身の OpenTelemetry メトリクスをコンソールに吐かせて拾う。
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=console
export OTEL_METRIC_EXPORT_INTERVAL=2000
これで claude_code.token.usage が type(input / cacheCreation / cacheRead / output)ごとに出る。ありがたいことに query_source という属性も付く。どちらも累積カウンタなので、(model, query_source, type) ごとの最終値を採った。
最初はここから始めていない。~/.claude/projects/<project>/<session>.jsonl を解析していた。コスト集計ツールがどれも読んでいる、あの transcript のことだ。そこで最初の発見にぶつかった。
出てきた数字
| ラン | 内容 | input(非キャッシュ) | cacheWrite | cacheRead | output | 合計 | コスト |
|---|---|---|---|---|---|---|---|
| A1 | 30 ファイル読み込み | 6 | 152,125 | 102,429 | 4,171 | 258,731 | $0.671 |
| A2 | 46 秒後に /compact | 126,464 | 15,704 | 30,287 | 4,977 | 177,432 | $0.348 |
| A2b | 同じ読み込み(再現) | 6 | 153,054 | 102,513 | 4,243 | 259,816 | $0.675 |
| A2b | 43 秒後に /compact | 126,464 | 16,169 | 30,287 | 4,490 | 177,410 | $0.344 |
| B1 | 同じ読み込み | 6 | 140,637 | 113,997 | 4,226 | 258,866 | $0.628 |
| B2 | 7 分放置してから /compact | 126,464 | 15,746 | 30,287 | 7,138 | 179,635 | $0.370 |
この表から 3 つのことが出てくる。どれも tips 記事に書いてあることではない。
1. compact のコストは transcript に残らない
セッションの JSONL に compact 自体は記録されている。isCompactSummary: true の user エントリがあり、ラン A では 4,655 文字の要約がまるごと入っていた。無いのは、その要約を生成したリクエストの usage のほうだ。ファイルを 1 行ずつ歩いて requestId で重複を潰すと、リクエストは 4 件しか出てこない。全部ふつうの会話ターンで、compact のものは 1 件も無い。
テレメトリ側では同じ処理がはっきり見える。query_source に "auxiliary" が立つだけの違いで、中身は実在のモデルに対する実在の API 呼び出しだ。transcript に降りてこないだけである。
JSONL を読んで支出を追うツールを使っているなら、compact はそのツールから見えていない。この実験では総額の 3 分の 1 が丸ごと抜け落ちる計算になる。
2. compact はキャッシュを一切使わない
compact した 3 ランの input 列を見てほしい。126,464 トークン。3 回とも、1 トークンの違いもなく同じ値。cacheRead も 3 回とも 30,287 で、これはシステムプロンプトとツール定義の分であって、会話本体ではない。
A2 はセッションが終わった 46 秒後に compact している。キャッシュはこれ以上ないほど温かい。B2 は 7 分放置してから compact していて、プロンプトキャッシュの TTL である 5 分を越えている。それで input が同じ値になる。近い値ではなく、同じ値だ。
これで、この話題でいちばん支持を集めているアドバイスが消える。「キャッシュがコンテキストを保持しているうちに compact しろ、さもないと満額払うことになる」というのは、動いていない仕組みを前提にしている。compact のリクエストは毎回まっさらな入力として組み立て直される。捕まえるべき温かい経路は最初から存在しない。
3 ランで違ったのは要約の長さ(output が 4,490 / 4,977 / 7,138)だけで、コストが完全に一致しなかった理由もそれしかない。
3. compact 1 回は、窓を埋めたコストの半分
30 ファイルを読ませるのに $0.671 かかった。その結果を compact するのに $0.348 かかった。この比が持ち帰る価値のある数字で、compact 1 回は、コンテキストを埋めるのにかかった額のおよそ半分にあたる。窓の中身を非キャッシュの入力として読み直すのに対し、最初に埋めたときは大部分がキャッシュ割引を受けているからだ。
ここから、compact のコストは捨てられる量ではなく、打った時点で窓がどれだけ埋まっているかに比例することも分かる。満杯に近い窓を compact するのがいちばん高いケースで、そして満杯に近い窓というのは、まさに compact に手が伸びる場面である。
compact して続けるか、新しいセッションを立てるか
実務で効くのはこの比較なので、4 通り回した。compact のあとに、2 ファイルを読まないと答えられない小さな質問をする。同じ質問を新しいセッションでもする。それぞれをキャッシュが温かい状態と、7 分放置した状態でやる。
| キャッシュ | トークン | コスト | |
|---|---|---|---|
| compact 済みセッションで続ける | 温かい | 67,523 | $0.156 |
| compact 済みセッションで続ける | 冷えている | 137,757 | $0.179 |
| 新しいセッションで同じ質問 | 温かい | 171,176 | $0.087 |
| 新しいセッションで同じ質問 | 冷えている | 170,890 | $0.089 |
どちらの条件でも新しいセッションのほうが半額で済む。しかもトークンは 2〜3 倍使っている。書き間違いではない。この実験でいちばん直感に反する結果がこれだった。
理由は内訳にある。compact 済みセッションのターンは cacheWrite が 38,711 に対して cacheRead が 98,603。compact の直後はコンテキストが総入れ替えになるので、キャッシュから読めるものが無く、全部を書き込むしかない。しかも書き込みは基本入力単価の 1.25 倍だ。
新しいセッションのほうは cacheRead が 157,198 で cacheWrite は 13,176 しかない。キャッシュ読みの単価は基本の 10 分の 1 で、7 分置いてもここはほとんど変わらなかった。TTL が効くのはセッションの最初のターンだけで、そのあとはセッションが自分のキャッシュを読み続けるからである。
compact 自体のコストを足し戻すと、2 つの経路は勝負にならない。
- compact してから続ける: $0.348 + $0.179 = $0.527
- 新しいセッションを立てる: $0.089
この実験を通して、トークン数とドルは一貫して逆を向いている。ステータスラインのコンテキスト残量を見ながら節約しているつもりなら、見ている数字が違う。
この実験で決着がついていないこと
使った質問はファイルを読めば答えが出る種類のもので、前の会話を覚えている必要がない。これは新しいセッションに有利な条件設定だ。読み直せば必要なものを全部組み直せてしまう。ここが結果の正直な境界線で、compact が買っているのは、安く再取得できない文脈のほうだ。私の質問には、それが一つも含まれていなかった。
すでに下した判断、検討して捨てた案、1 時間かけて追い込んだバグの形。どれもファイルには書かれていない。それを持ち越すために $0.35 払うのは、組み直すより安い。
数字が否定したのは、compact がコスト最適化になるという考えのほうである。そうではない。compact は継続性を買う操作で、それには値段が付いている。
測れていない範囲
- Sonnet 5 だけで測った。Opus は単価の構造が違うので、非キャッシュの読み直しとキャッシュ経路の比は動く。ただし仕組みそのものは変わらない
- コンテキストのサイズは 1 種類(会話部分で約 126k)。compact のコストが窓の充填量に比例するというのは仕組みからの帰結で、サイズを振って測った結果ではない。直線の上の 1 点しか押さえていない
- 手で打つ
/compactだけ。閾値で自動的に走る auto-compaction は測っていない。まとめ方が違う可能性がある - ヘッドレスの
claude -pセッション。長時間の対話セッションでは積み上がり方が違うかもしれない。ただし compact のリクエスト自体の組み立ては同じはず - 金額はテレメトリが定価から計算した値で、サブスクリプションで使っているなら請求額そのものではない。比として見るぶんには使えるが、請求書ではない
- compact は 3 回しか回していない。
inputが 3 回とも同じ値だったので試行回数のわりに信用できる数字だが、要約の長さは 6 割ぶれた
自分の運用をどう変えたか
/compact を後片付けの操作だと思うのをやめた。窓を埋めたコストの半分がかかり、打つタイミングを工夫しても安くならず、そして transcript ベースの集計ではそもそも見えていなかった。
これからやる作業が、セッションがすでに持っている文脈を必要とするなら、compact して代金を払う。次のタスクが切り離せるなら——別のファイル、別のバグ、2 文の説明を添えて同僚に渡せる程度のもの——新しいセッションを立てる。その 2 文は、compact 代よりずっと安い。
一般に言われていることで今後はっきり捨てるのは、「キャッシュが温かいうちに落ちるようセッションの終わりに compact する」というやり方だ。3 ラン、input は同じ値、あいだに TTL の 5 分をまたいでいる。捕まえるべき温かい経路は無い。