PoC では動いたのに、本番に載せた瞬間コストが想定の 3 倍に膨らみ、レイテンシも安定しない——Claude API を業務組込みで使い始めた開発現場で繰り返し起きるパターンです。原因の多くは prompt caching の設計不備 にあります。
ドキュメント通りに cache_control ブロックを付けたはずなのに、cache_read_input_tokens が一向に増えない。よく見るとシステムプロンプトの末尾に現在時刻が混ざっていたり、ツール定義の JSON キー順が毎回違ったりします。prefix match の不変条件を踏み外す Silent Invalidator が随所に潜んでいます。
本記事では、Anthropic の prompt caching を本番運用で活かすために最初に下しておくべき 3 つの設計判断を、実装者の視点から整理します。コスト試算と運用観点も併せて示します。
本記事の結論:prefix match を守りきる 3 つの意思決定
prompt caching を本番で機能させる鍵は、prefix match という不変条件を設計段階で守りきること に尽きます。具体的には、(1) どこにキャッシュ境界(cache_control)を置くか、(2) prefix を不変に保つために何をプロンプトの外へ追い出すか、(3) cache_creation と cache_read のメトリクスをどう監視するか、の 3 点を意思決定として明文化することが出発点になります。
キャッシュは「効くと安い」のではなく、効かせる前提で設計しないと逆にコストを押し上げる 性質を持ちます。cache_creation_input_tokens は通常の入力トークンより単価が高く、ヒットしないキャッシュ書き込みが連発すると無キャッシュ運用より高くつくケースすらあります。
月間 1,000 万トークンを処理するワークロードを試算すると、prefix が安定して 80% ヒットする設計に切り替えるだけで、API コストが半分以下に落ちる例は珍しくありません。技術判断であると同時に、経営インパクトの大きい設計判断として扱うべきテーマです。
prompt caching は「効いたらラッキー」ではなく、効かせるための前提条件を満たした設計に対してのみ恩恵を返す仕組みです。
prompt caching が効かない本当の理由は prefix match の崩壊にある
「cache_control を付ければキャッシュが効く」は、本番運用では半分しか正しくありません。Anthropic の prompt caching は、cache_control ブロックより前のトークン列が完全一致したときだけヒットする prefix match 方式です。評価順は tools → system → messages の順で、途中で 1 トークンでもズレるとそれ以降は全て cache miss になります。
挙動は API レスポンスの usage フィールドから観察できます。
フィールド | 意味 | 課金 |
|---|---|---|
| 今回キャッシュに書き込んだトークン数 | 通常入力の約 1.25 倍 |
| キャッシュから読み出したトークン数 | 通常入力の約 1/10 |
| キャッシュ非対象の入力トークン数 | 通常単価 |
理想は cache_read_input_tokens だけが伸び続ける状態です。ところが現場では、cache_creation_input_tokens が毎回ほぼ同じ値を返し続ける——つまり毎回作り直してヒットしていない——というパターンに頻繁に出会います。
この時、ドキュメントを読み返しても「prefix match」という単語は淡々と書かれているだけで、見落とされやすい不変条件として強調されていません。prefix の先頭から 1 トークンでも違えば、その後ろ全てが無効化される という挙動は、設計段階で明文化しておかないと開発者間で共有されないことが多い前提です。tools の JSON 表現、system プロンプトの組み立て関数、参照ドキュメントの埋め込み順——いずれもが prefix の一部であり、いずれも壊しうる箇所です。
設計判断 1:キャッシュ境界をどこに置くか
cache_control は 1 リクエスト内で最大 4 ブロックまで指定できます。境界の候補は、tools 定義の末尾、system プロンプトの末尾、長い参照ドキュメントの末尾、会話履歴の途中、などです。どこに境界を引くかは、ヒット率と書き込みコストのトレードオフ を意識して決める必要があります。
押さえておくべき制約として、prefix が短すぎる場合はそもそもキャッシュされません。Sonnet / Opus は 1,024 トークン以上、Haiku は 2,048 トークン以上 が最小要件です。「system プロンプトに 300 トークンだけ書いて cache_control を付ける」設計は無効化されることになります。
境界設計の基本パターンは次の通りです。
パターン | 境界位置 | 想定ユースケース |
|---|---|---|
単一境界 | system 末尾 | 固定指示のみで参照資料が小さい |
2 段境界 | tools 末尾 + 参照資料末尾 | RAG や長文ドキュメント参照 |
3 段境界 | tools + system + 会話履歴 | マルチターンのエージェント |
会話履歴の途中に境界を置く構成は強力ですが、履歴の編集や要約圧縮を行うたびに prefix が崩れる ため、履歴操作のロジックと一緒に設計する必要があります。「履歴は append only に保つ」「圧縮は古い側だけで行い、新しい側の cache_control 位置は触らない」といった運用上の制約が、境界設計とセットで決まります。
設計判断 2:Silent Invalidator をプロンプトの外に追い出す
prefix match を静かに壊す要素を、私たちは Silent Invalidator と呼んでいます。コードレビューでも気付かれず、ログでも目立たず、ただ静かにヒット率を 0% に押し下げます。典型は次の 5 パターンです。
- システムプロンプト内の現在時刻・日付埋め込み:
現在時刻: 2025-11-14 10:23:45を冒頭に挟むと、prefix の先頭から毎回崩れます - リクエストごとに変わる UUID やセッション ID:トレース目的で埋め込んだ ID が prefix を破壊します
JSON.stringifyのキー順非決定性:ツール定義スキーマを動的に組み立てると、Node のバージョンや実装で順序が変わることがあります- ツール定義の動的生成順序:feature flag に応じて tool を出し入れする実装で、配列順が安定しないケース
- ユーザー属性情報の prefix への混入:ユーザー名・組織名を system プロンプトに差し込む設計
対策は単純で、変動するものを cache_control より後ろに追い出す だけです。時刻や ID は user メッセージ側(境界より後)に置く、ユーザー属性は最初の user turn に含める、tools 定義はシリアライズ関数を sort_keys=true 相当で固定化する。動的に必要な情報は tool 呼び出しの結果として後段で取得する設計に寄せると、prefix は自然と不変になります。
# NG: prefix が毎回変わる
system = f"現在時刻: {datetime.now()}\n以下のルールに従って..."
# OK: 時刻は user 側へ
system = "以下のルールに従って..." # 不変
user = f"[context] 現在時刻: {datetime.now()}\n{user_input}"
「便利だから system に書きたくなる情報こそ、prefix の毒になる」という視点を、設計レビューのチェック項目に加えておくと事故が減ります。
設計判断 3:cache_creation と cache_read を監視可能にする
本番運用では response.usage の cache_creation_input_tokens と cache_read_input_tokens を必ずメトリクス化します。最低限ダッシュボードに出すべき指標は次の 3 つです。
- ヒット率 =
cache_read / (cache_read + cache_creation + input_tokens) - 書き込み率 =
cache_creation / 総入力トークン - 無キャッシュ比率 =
input_tokens / 総入力トークン
ヒット率を SLO として 70〜80% 以上 に置き、デプロイのたびに低下していないか監視する運用が現実的です。プロンプトの軽微な変更でヒット率が一晩で 0% に落ちる事故は、メトリクスを見ていないと数日〜数週間気付けません。
TTL は標準 5 分と、ベータの 1 時間が選べます。1 時間 TTL は書き込み単価が高い代わりに長時間維持されるため、呼び出し頻度が低くロングテール型のワークロード に向きます。逆に秒間多数のリクエストが連続するケースでは 5 分 TTL でも十分です。
損益分岐の感覚として、cache_creation が通常入力の約 1.25 倍、cache_read が約 1/10 とすると、書き込んだキャッシュが 2 回以上読まれて初めて元が取れる 計算になります。ヒットしないキャッシュ書き込みが多発する状態は、無キャッシュ運用より割高です。「キャッシュを付けたから安心」ではなく、読まれているかを毎週確認する ことを運用の所作にする必要があります。
ある SaaS 企業で観測した 55% コスト削減と TTFT 1.8 秒 → 0.6 秒
ある SaaS 企業の AI アシスタント機能で実際に観測したケースを紹介します。月間 API 利用料が想定より 2.4 倍に膨らみ、原因調査を依頼されました。
調査の結果、システムプロンプトの冒頭に 現在時刻:2025-11-14 10:23:45 という文字列がリクエストごとに埋め込まれていました。cache_control はシステムプロンプト末尾に設置されていましたが、prefix の先頭 1 トークン目から毎回異なるため、4,200 トークンあるシステムプロンプト全体が毎回 cache miss となり、cache_creation として課金されていました。書き込み単価は通常入力より高いため、無キャッシュ運用より高い API コストを払い続けていたことになります。
対応は単純でした。時刻情報を system プロンプトから外し、必要なときだけ user メッセージ側に含める設計に変更します。同時に tools 定義の JSON シリアライズ順を固定化し、参照ドキュメントブロックの末尾に 2 つ目の cache_control を追加しました。
結果として、デプロイ翌週には cache_read_input_tokens の比率が全入力の 78% まで上昇し、月額 API コストが約 55% 減少 しました。レイテンシ面でも、初回トークン到達時間(TTFT)の中央値が 1.8 秒から 0.6 秒 へ改善しています。3 ヶ月かけて作り込んだロジック変更ではなく、prefix 設計を 2 日で見直しただけの効果です。
この種の改善は、コードレビューでは見つからず、実運用で usage メトリクスを観察し続けて初めて顕在化します。設計判断の段階で意識していれば回避できた損失でもありました。生成 AI 実装で最初に躓くポイントについては 生成 AI の業務実装で、最初に必ず躓く 3 つの設計判断 でも整理しています。
生成 AI 本番運用の設計レビューについて
prompt caching の設計判断は、API コストとレイテンシの両方に直結するため、PoC 段階では見えにくく、本番運用に乗せた途端に経営インパクトとして顕在化します。私たちは、生成 AI を本番運用に組み込む段階での設計レビューや、コスト・レイテンシ改善の伴走支援を行っています。
prefix 設計、Silent Invalidator の洗い出し、usage メトリクスの監視運用までを含めた相談は、サービスページから受け付けています。「キャッシュが効いていない気がするが、どこから手を付ければよいかわからない」という段階でも構いません。usage ログを一緒に眺めるところから始めるのが最短ルートになることが多いです。
関連記事: