- 長時間実行エージェント製品において、プロンプトキャッシュは前回のラウンドトリップの計算を再利用してレイテンシとコストを大幅に削減する中核技術であり、Claude Codeの全アーキテクチャはこれを中心に設計されている
- キャッシュはプレフィックス一致方式で動作するため、静的コンテンツを前に、動的コンテンツを後ろに配置する順序設計がコストと性能を左右する
- セッション途中でツールやモデルを変更するとキャッシュが無効化されるため、ツール削除の代わりに軽量スタブと状態遷移用ツールを活用する回避設計が必須
- コンテキストウィンドウ超過時に行う**コンパクション(要約圧縮)**処理でも、親会話のキャッシュ済みプレフィックスを共有しなければコスト急増を防げない
- キャッシュヒット率は稼働率のように監視すべきであり、数パーセントのキャッシュミスでも深刻なコスト・遅延影響を及ぼすため、インシデントとして扱うべき
- 「Cache Rules Everything Around Me」 はエージェントにもそのまま当てはまる
- **プロンプトキャッシュ(prompt caching)**によって、Claude Codeのような長時間動作するエージェント製品が実現可能になる
- 前回のラウンドトリップの計算を再利用することで、latencyとcostを大きく削減できる
プロンプトキャッシュの仕組みとシステムプロンプト配置
- プロンプトキャッシュはプレフィックス(Prefix)一致方式で動作し、APIはリクエストの先頭から各cache_controlブレークポイントまでのすべての内容をキャッシュする
- リクエスト間で共有プレフィックスを最大化することが重要で、そのためには静的コンテンツを先、動的コンテンツを後に配置する必要がある
- Claude Codeの配置順序:
- 静的システムプロンプトおよびツール(グローバルキャッシュ)
- Claude.MD(プロジェクト内キャッシュ)
- セッションコンテキスト(セッション内キャッシュ)
- 会話メッセージ
- この順序は驚くほど壊れやすく、
静的システムプロンプトへの詳細タイムスタンプ挿入、
ツール順序の非決定的シャッフル、
ツールパラメータ更新
などによってキャッシュが無効化されうる
システムメッセージを活用した更新戦略
- 時刻情報の変化やユーザーのファイル変更のように、プロンプト内部の情報が古くなってしまう状況がある
- プロンプトを直接更新するとキャッシュミスにつながり、ユーザーコスト増加の可能性がある
- 代わりに次のターンでメッセージとして渡す方式を活用する
- Claude Codeは次のuser messageまたはtool resultに
<system-reminder>タグで更新情報を挿入する
- たとえば「もうWednesdayです」といった時刻更新を提供する
- このようにsystem messagesを使えばキャッシュを維持できる
セッション途中でモデルを変更してはいけない理由
- プロンプトキャッシュはモデルごとに固有であるため、キャッシュコスト計算は直感と異なることがある
- 例: Opusで100kトークンの会話中、簡単な質問のためにHaikuへ切り替えると、Haiku用キャッシュを最初から再構築する必要があるため、かえってOpusで答えるよりコストが高くなる
- モデル切り替えが必要な場合はサブエージェント方式が最善で、Opusが別モデルに「ハンドオフ」メッセージを準備して渡す形になる
- Claude CodeのExploreエージェントがHaikuを使う際にこの方式を適用している
セッション途中でツールを追加・削除してはいけない
- ツールセット変更はキャッシュ無効化の最も一般的な原因の一つ
- ツールはキャッシュ済みプレフィックスの一部なので、ツールを一つ追加または削除すると会話全体のキャッシュが無効化される
-
Plan Mode — キャッシュ中心設計
- 直感的なアプローチ: ユーザーがplan modeに入ったら読み取り専用ツールだけを残す → キャッシュ破壊
- 実際の設計: すべてのツールを常にリクエストに含め、EnterPlanModeとExitPlanModeをツールとして実装
- Plan modeに入るとシステムメッセージで指示を渡す: コードベース探索のみ実行し、ファイル編集は禁止、完了時にExitPlanModeを呼び出す
- ツール定義は決して変更されない
- 追加の利点: EnterPlanModeはモデルが直接呼び出せるツールなので、難しい問題を検知した際に自律的にplan modeへ入ることができ、キャッシュも壊れない
-
Tool Search — 削除ではなく遅延読み込み
- Claude Codeでは数十個のMCPツールが読み込まれることがあり、すべて含めるとコストが高く、削除するとキャッシュが壊れる
- 解決策: defer_loading方式として、ツール削除の代わりに名前だけを含む軽量スタブ(
defer_loading: true)を送る
- モデルは必要時にToolSearchツールを通じて完全なスキーマを読み込む
- 同じスタブが常に同じ順序で存在するため、キャッシュ済みプレフィックスが安定して維持される
- APIのtool search機能を通じてこれを簡略化できる
コンテキストのフォーキング — コンパクション(Compaction)
- コンテキストウィンドウを超えると、会話を要約して新しいセッションを開始するコンパクションを行う
- 直感的な実装(別のシステムプロンプトとツールなしで、別API呼び出しとして要約生成)は、メイン会話のキャッシュ済みプレフィックスとまったく一致しないため、すべての入力トークンに対して満額コストが発生する
-
Cache-Safe Forkingソリューション
- コンパクション実行時には親会話と同一のシステムプロンプト、ユーザーコンテキスト、システムコンテキスト、ツール定義を使う
- 親の会話メッセージを前に配置し、コンパクションプロンプトを末尾の新しいユーザーメッセージとして追加する
- API視点ではこのリクエストは親の最後のリクエストとほぼ同一に見えるため、キャッシュ済みプレフィックスを再利用でき、新規トークンはコンパクションプロンプトだけになる
- コンテキストウィンドウ内には、コンパクト化メッセージと要約出力トークンのための**「コンパクションバッファ」**を確保する必要がある
- このパターンを基に、AnthropicはAPIにコンパクション機能を直接組み込み、開発者がそのまま利用できるようにした
重要な教訓のまとめ
- プロンプトキャッシュはプレフィックス一致であり、プレフィックスのどこかで変更が起これば、その後のすべてのキャッシュが無効化される → この制約を中心にシステム全体を設計すべき
- システムプロンプトを変更する代わりに、会話中にシステムメッセージを挿入する方式がキャッシュ維持に有利
- 会話途中でツールやモデルを変更しないこと → 状態遷移はツールとしてモデル化し、ツール削除の代わりに遅延読み込みを使う
- キャッシュヒット率は稼働率のように監視すべきであり、数パーセントのキャッシュミス率でもコストと遅延に劇的な影響を与える
- フォーク処理(コンパクション、要約、スキル実行)は親のプレフィックスを共有してこそキャッシュヒットが可能
- エージェントを構築するなら、初日からプロンプトキャッシュ中心で設計すべき
5件のコメント
プロンプトエンジニアリングからコンテキストエンジニアリングへの転換が重要で、実務的には「関心の分離」が答えのようです。
ペルソナ、行動ルール、メモリをそれぞれ別のファイルで管理すると、context rot を減らすのに効果的です。モノリシックなプロンプトより必要なファイルだけを読み込むほうが、attention budget の面ではるかに有利ですから。だから OpenClaw(またはそれに類するフレームワーク)では、ペルソナ(SOUL.md)、行動ルール(AGENTS.md)、メモリ(MEMORY.md)をそれぞれ管理しているのだと思います。
ああ、だからOpusはあんなにトークン価格が高いんですね。
AntigravityとClaude Codeのコンテキスト管理の違いについてのレビューがあるのか気になります。
同じOpusモデルでも、明らかに違うはずですからね。 :)
本当にとても参考になる文章ですね
本当の読者:
Claude Codeのような「長時間実行されるエージェント」を作る人たち
(特にプロダクト/プラットフォームエンジニア、LLMインフラエンジニア)
誰が読むといちばん役に立つか?
✅ 1) AIエージェント製品を作るチーム
✅ 2) LLMのコスト/レイテンシを最適化するエンジニア
✅ 3) MCPツールを大量に組み込む人
逆に、一般ユーザーはほとんど見ないです。
「プロンプトをうまく書く方法」のような記事ではなく
「プロンプトをプロダクトアーキテクチャレベルでどう扱うべきか」
一言でまとめると
LLMを「チャット」ではなく「プロダクションシステム」として作る人のための記事です.
Dockerレイヤーの最適化と大差ないですね