EP.2でhooksを使ってClaudeが読むものを減らした。テスト結果は失敗だけ出力し、ビルドログはエラーだけ抽出した。
どれくらい減ったのか測定することにした。
まず測定
Claude Codeはすべての会話をJSONLファイルに記録する。~/.claude/projects/の下にプロジェクトごとに蓄積され、各項目にトークン使用量が入っている。これを読んでコストを計算するスクリプト(analyze-tokens.js)を作った。日付別セッションファイルと照合してターン数、cache_read/write/outputをそれぞれ集計しコストに換算する。
作る過程でバグを1つ見つけた。セッション記録hookがe.usageを参照していたが、JSONLの構造はe.message.usageだった。セッションファイルにトークンが記録されていなかったのだ。分析器はJSONLを直接読むように迂回した。
以下は3月27日1日分のコストだ。
総コストの58%は読み込み
| 項目 | コスト | 割合 |
|---|---|---|
| cache_read | $19.22 | 58% |
| cache_write | ~$9.62 | 29% |
| output | ~$4.33 | 13% |
| 合計 | $33.17 |
cache_readが58%。Claudeが発生させたコストの半分以上が「読み込み」という意味だ。EP.1で「Claudeが消費するトークンの大部分は推論ではなく読み込み」と書いたが、数字で見ると確実だ。
何を読んでいるのか
Claudeが毎ターン読むものを追跡した。
| 項目 | ロード時点 | 規模 |
|---|---|---|
| プロジェクトCLAUDE.md | 毎ターン | 155〜214行 |
| ルートCLAUDE.md | 毎ターン | 10行 |
| MEMORY.md | 毎ターン | 23行以内 |
| 会話履歴 | 毎ターン | 累積 |
意外な発見があった。SKILL.md(469行)とエージェントドキュメント(1,233行)は毎ターンロードされない。スキルが呼ばれた時だけ、エージェントが実行された時だけロードされる。すでにlazy loadingだ。
毎ターン固定でロードされるのはCLAUDE.mdとMEMORY.mdだけだ。
CLAUDE.mdをrules/に分離
CLAUDE.mdが155〜214行だからといって全部が毎ターン必要なわけではない。成果物パステーブルは成果物を書く時だけ必要で、ゲームドメインルールはゲーム関連作業の時だけ必要だ。
Claude Codeには.claude/rules/ディレクトリがある。ここにファイルを作りpaths:フロントマターを付けると、該当パスにアクセスした時だけロードされる。
---
paths: tasks/**
---
成果物パステーブル、共通ヘッダーブロック、役割別ルール...
tasks/フォルダを触る時だけこのルールがロードされる。一般的な会話ではロードされない。
1つのプロジェクトで先に試験適用した。成果物関連セクション45行をartifact-rules.mdに分離し、CLAUDE.mdから該当部分を削除した。動いた。残りにも一括適用した。
| プロジェクト | CLAUDE.md(前→後) | 削減率 | 分離ファイル |
|---|---|---|---|
| A(ECサイト) | 155行→96行 | 38% | artifact-rules.md |
| B(教育プラットフォーム) | 207行→140行 | 32% | artifact-rules.md、edu-rules.md |
| C(ゲームプラットフォーム) | 214行→114行 | 47% | artifact-rules.md、game-domain.md |
Cが47%も減った。ゲームプラットフォームなのでドメインルール(46行)が毎ターンロードされていたが、今はゲーム関連ファイルを触る時だけロードされる。
ところがですよ
数値は悪くない。32〜47%軽量化。見た目はいい。
しかし正直に言えば、これは「CLAUDE.md 155〜214行のうち32〜47%」だ。ターンあたり数百トークン程度の削減だ。cache_read $19.22の大部分はCLAUDE.mdの200行ではなく会話履歴の累積が原因だ。1セッションで20ターン回すと、20ターン目には前の19ターン全体がcache_readとしてロードされる。
1日のコストの半分以上を占めたプロジェクトがあった。他のプロジェクトより3倍以上高かった。理由は単純だ。1セッションで17ターン回した。後半のターンでcache_readが急増したのだ。
CLAUDE.mdをいくらダイエットさせても、会話履歴が蓄積する構造を変えなければcache_readの大部分はそのままだ。EP.1でエージェント数を減らし、EP.2でhooksで出力を減らし、EP.3で固定ドキュメントを減らした。全部意味のある作業だが、本当の大きな穴はまだ開いている。
切るか、続けるか、それが問題だ
20ターンを超えたら新しいセッションを始める方がcache_read的には効率的だ。しかし新しいセッションを始めるとcache_writeが再発生する。CLAUDE.md、MEMORY.md、システムプロンプトを最初から書き直す。
cache_readを減らすためにセッションを切るとcache_writeが増える。cache_writeを減らすためにセッションを続けるとcache_readが増える。どちらにしてもコストがかかる。
答えはまだわからない。20ターンくらいが損益分岐点という感覚はあるが、作業タイプによって異なる。会話の多い設計作業は早く切る方がいいし、連続コーディングは続ける方がいい。
これがEP.3の本当の発見だ。減らせるものは減らした。残ったのは「どう使うか」の領域だ。
このシリーズの他の記事
参考