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.2258%
cache_write~$9.6229%
output~$4.3313%
合計$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.mdedu-rules.md
C(ゲームプラットフォーム)214行→114行47%artifact-rules.mdgame-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の本当の発見だ。減らせるものは減らした。残ったのは「どう使うか」の領域だ。

このシリーズの他の記事


参考