本記事は、Claude Codeの開発に携わるAnthropicのThariq Shihipar(@trq212)氏がXに投稿した内容をもとに作成しています。原文はこちらをご参照ください → https://x.com/trq212
そもそも「プロンプトキャッシング」って何?
Claude Codeのような AIを使うとき、「トークン」と呼ばれる単位でコストがかかります。簡単に言うと、AIに送る文章や指示が長くなるほど料金が高くなる仕組みです。
Claude Codeのような長時間使うエージェント(自律的に動くAI)では、毎回のやり取りに「システムプロンプト(AIへの初期設定)」「ツールの定義」「これまでの会話履歴」などを送り直す必要があります。そのまま素直に送ると、コストが膨らみます。
プロンプトキャッシングとは、「一度処理した内容を保存しておき、次回は再利用することでコストと時間を削減する」仕組みです。
図書館で例えると分かりやすいです。毎日「図書館の利用ルールを教えてください」と聞かれる司書が、説明カードを一枚作っておけば毎回ゼロから話す必要がなくなりますよね。それと同じ発想です。
キャッシングの効果
Anthropicの公式データによると:
- キャッシュが「ヒット」した場合、入力トークンのコストが最大90%削減
- 応答速度も大幅に向上
- Claude Codeのサブスクリプションプランの使用上限緩和にも貢献
このためAnthropicのClaude Codeチームは、キャッシュヒット率が下がったときを「インシデント(重大障害)」として扱い、アラートを飛ばして即対応しているそうです。
キャッシングの基本ルール:「前から一致している部分だけ再利用できる」
プロンプトキャッシングで最も大切な原則は「前方一致(プレフィックスマッチ)」です。
会話の先頭から見て、「前のリクエストと同じ部分」が続く限りキャッシュが効きます。途中で何かが変わった瞬間、そこから後ろは全部再計算になります。
だから「変わりにくいものを前に、変わりやすいものを後ろに」という順序が基本です。
Claude Codeでの実際の構造はこうなっています。
先頭(キャッシュが最も効く)
↓ システムプロンプト・ツールの定義(全セッション共通)
↓ CLAUDE.md(プロジェクトの設定)
↓ セッションの状態
↓ 会話メッセージ(毎回変わる)
末尾(キャッシュが効かない)
この順序を守るだけで、多くのキャッシュが自動的に機能します。
「キャッシュが壊れる」落とし穴
開発チームが実際に経験した「やってしまった事例」を紹介します。どれも一見合理的に見える操作なのに、キャッシュを壊してしまいます。
落とし穴1:システムプロンプトに現在時刻を入れた
システムプロンプトに「現在時刻:2024-01-15 14:30:22」のような詳細なタイムスタンプを入れると、時刻が変わるたびにキャッシュが無効になります。
正しい方法: 時刻などの動的な情報は、次のメッセージの中で<system-reminder>タグを使って追記する。システムプロンプト本体は変えない。
落とし穴2:ツールの定義順をランダムにした
ツールの定義リストを毎回違う順番で送っていたら、内容が同じでもキャッシュがヒットしなくなります。
正しい方法: ツールは常に同じ順番で送る。
落とし穴3:セッション途中でツールを追加・削除した
これが最もよくある失敗です。「今の作業に関係ないツールは送らなくていいのでは」と思って途中でツールセットを変えると、キャッシュが全部無効になります。
正しい方法: セッション中はツールセットを固定する。
計画モードのキャッシュ設計:制約を逆手にとった発想
Claude Codeには「計画モード」という機能があります。AIがコードを書かずに作業計画だけを考えるモードです。
最初の設計案は「計画モードに入ったとき、書き込み系ツールを外して読み取り専用ツールだけにする」というものでした。合理的に聞こえますが、これをやるとキャッシュが壊れます。
そこでClaude Codeが採用した設計がこれです。
ツールセットは一切変えない。計画モードへの切り替えを「EnterPlanModeというツールを呼ぶ」で表現する。
モードが変わったことはメッセージで通知するだけです。ツールの定義はいつも同じ。これでキャッシュが保たれます。
さらにうれしい副産物もありました。EnterPlanModeがツールとして存在するので、AIが「これは複雑な問題だ」と判断したとき、自分から計画モードに入れるようになったのです。キャッシュを守るための設計が、AIの自律性を高めました。
ツール検索の工夫:「削除」ではなく「延期」で対処する
MCPツール(外部サービスと接続するツール)が数十個あるとき、全部を毎回送るのは重くなります。でも削除するとキャッシュが壊れます。
Claude Codeの解決策は「defer_loading(遅延読み込み)」という仕組みです。
完全な定義を送る代わりに「名前だけのスタブ(仮の枠)」を送っておきます。AIが実際に使いたくなったとき、ToolSearchというツールを呼んで詳細を取り込みます。
常にキャッシュに含まれる → ツールの名前(スタブ)
必要になったときだけ読み込む → ツールの完全な定義
ツールを「削除する」のではなく「一時停止する」発想です。
コンテキスト圧縮(Compaction)のキャッシュ問題
会話が長くなって上限(コンテキストウィンドウ)に近づくと、Claude Codeは「圧縮」を行います。これまでの会話を要約して、新鮮な状態でセッションを続ける処理です。
問題は、この圧縮をどう実装するかです。
単純な実装(NG): 別のAPIコールを立ち上げ、違うシステムプロンプトで要約させる。
→ 親会話のキャッシュがまったく使えない。膨大なトークンを全額払うことになる。
Claude Codeの実装(OK): 圧縮でも親会話とまったく同じシステムプロンプト・ツール定義を使う。親の会話履歴の最後に「ここまでを要約して」というメッセージを追加するだけ。
親会話の先頭から → キャッシュヒット(再利用)
↓ 同じシステムプロンプト
↓ 同じツール定義
↓ 同じ会話履歴
追加分 → 「要約してください」の指示(新規トークンはここだけ)
これだけでキャッシュを最大活用しながら圧縮できます。この仕組みは今ではAnthropicのAPIに標準搭載されており、自分でアプリを作る場合も利用できます。
モデルを途中で変えると割高になる意外な理由
「簡単な質問はHaikuで安く処理しよう」という考えは一見合理的ですが、コスト計算が逆転することがあります。
キャッシュはモデルごとに独立しています。10万トークンの会話をOpus(高性能モデル)で積み上げていたのに、途中でHaiku(軽量モデル)に切り替えると、Haikuのキャッシュをゼロから作り直すことになります。
モデルを切り替えたい場合は、「サブエージェント(別タスクを担当するAI)」として呼び出す方法が正解です。Opusが「この部分はHaikuに任せる」という引き継ぎメッセージを作り、サブエージェントとして起動させます。
まとめ:プロンプトキャッシングの設計原則
| 原則 | やること |
|---|---|
| 静的なものを前に置く | システムプロンプト・ツール定義を先頭に固定 |
| 動的情報はメッセージで渡す | 時刻・状態変化はシステムプロンプトに入れない |
| セッション中はツールを変えない | 追加も削除もしない |
| モデル切り替えはサブエージェントで | 途中の切り替えはキャッシュを壊す |
| キャッシュヒット率を監視する | 低下したら即調査・修正 |
「キャッシュルールズエブリシングアラウンドミー(Cacheがすべてを支配する)」とThariq氏は表現しています。最初から正しく設計しておけば、ほとんどのキャッシングは自動的に機能します。
コメント