アンソトロピックのプロンプトキャッシュでRCAコストを90%削減した話

Dev.to / 2026/5/9

💬 オピニオンDeveloper Stack & InfrastructureSignals & Early TrendsTools & Practical UsageModels & Research

要点

  • 本番環境のLLMコストは、インシデントのあらゆる出来事で同じモデル呼び出しを行うと、デモ段階では小さく見えても短期間で増大し、売上に対する比率が無視できなくなり得ます。
  • アンソトロピックのプロンプトキャッシングは、プロンプトの一部を再利用することで1リクエストあたりの入力コストを大幅に下げられ、著者のケースでは90%超のキャッシュヒット率でRCAコストを90%削減できたと述べています。
  • 価格表から、キャッシュリードはベース入力より10分の1の費用である一方、キャッシュライトはベース入力より25%高くなるため、キャッシュTTL(説明では5分)内に同じプロンプトが複数回繰り返される場合にのみ投資が回収できます。
  • 「毎回ワンショットでコールドキャッシュ」になる呼び出しパターンは逆効果になり得るため、RCA呼び出しを一定部分がリクエスト間で同一になるよう設計することが重要だとしています。
  • 記事ではRCAフローにおいてキャッシュ可能になりやすい要素(システムプロンプト、応答のスキーマ、反復される指示や前置きなど)を挙げ、キャッシングを機能させる考え方を説明しています。

もともとは theculprit.ai/blog/anthropic-prompt-caching-90-percent に掲載されました。

本番環境でのLLMコストは、デモの請求書が示唆するよりもずっと速いペースで増えていきます。

問題の形はこうです。つまり、毎回の意味のあるイベントごとにClaudeを呼び出す機能を出荷します。最初の月は請求額が誤差の範囲で、誰も見ません。2か月目、ある顧客のトラフィックが増え始めて、項目が突然売上の5%になります。3か月目、あなたの経理担当が「これは本当にコストのトレンドなのか、それとも一度きりの急騰なのか」という丁寧なSlackを送ります。そして、エンジニアリングチーム全員が、請求額が誤差の範囲だった8週間前に自分たちが下した設計判断を説明し、防御しなければなりません。

これを減らせます。モデルの呼び出し方を賢くするのではなく、呼び出しの中で一定の部分を賢く扱うのです。Anthropicのプロンプトキャッシュでは、今回のケースでは、1-RCA入力コストをフルレートから1/10にできます(キャッシュヒット率が90%+の場合)。これは机上の話ではなく、本番で計測した結果です。ここでは計算をシンプルに順を追って説明するので、あなたのパイプラインでも同じ数値を出してみてください。

料金体系

Anthropicは、モデルごとに4つの価格ポイントを公開しています。Claude Haiku 4.5(インシデントの根本原因分析のデフォルトとして私たちが実行しているモデル)では、それらの価格は次のとおりです(AnthropicのAPIドキュメントで確認):

トークンカテゴリ Haiku 4.5
ベース入力 トークン100万枚あたり$1.00
キャッシュ書き込み(5分のTTL) トークン100万枚あたり$1.25
キャッシュ読み取り トークン100万枚あたり$0.10
出力 トークン100万枚あたり$5.00

この表から読み取るべきことが2つあります。

  1. キャッシュ読み取りは、ベース入力の10分の1の価格です。 リクエストボディ内の同じトークンでも、コストは10%です。ただしそれらをキャッシュに入れられる場合に限ります。
  2. キャッシュ書き込みは、ベース入力より25%高価です。 キャッシュされたセグメントを初めて送るときは、次のリクエストで割引を受けられるように、少し上乗せしたプレミアムを支払います。計算が元を取るのは、5分のTTLウィンドウ内で同じキャッシュセグメントを平均して約1.25回以上呼び出せる場合だけです。

この2つ目のポイントを見落とすチームがほとんどです。呼び出しパターンが「毎回ワンショットで常にコールドキャッシュ」の場合、プロンプトキャッシュは逆にわずかに損をします。勝ち筋は、呼び出しをまたいで再利用できる構造を持つことです。

実際にRCA呼び出しでキャッシュ可能なもの

典型的なRCAの呼び出しには、トークン源が5つあります。

  1. システムプロンプト。 ロールを定義します(例:「あなたはSREとしてインシデントを分析している」)、レスポンスのJSONスキーマ、そしてガードレールです。すべてのテナントすべての呼び出しで同一です。スキーマの厳密さにもよりますが、だいたい800〜1500トークン程度です。
  2. 検索コンテキスト(「同じサービスで、同様に解決した過去のインシデントを3件提示します」)。Batch実行の数分間、1つのテナント+サービス内では静的です。検索がどれだけ攻めているかにもよりますが、だいたい400〜800トークン程度です。
  3. インシデントごとのイベント(「14:32:01にevent 1: ConnectionPoolExhausted...、14:32:04にevent 2: ...」)。分析対象のインシデントに固有で、インシデント間でキャッシュできません。通常1500〜3000トークンです。
  4. インシデントごとのメタデータ(インシデントID、サービスID、重大度)。小さいですが一意です。
  5. 出力トークン。 モデルの応答。コストは出力レートで固定され、キャッシュは適用されません。

ソース1と2はキャッシュ可能です。ソース3と4はキャッシュできません。ソース5は無関係です。

私たちの配布(分布)では、ソース1+2は、典型的なRCA呼び出しにおける入力トークンのだいたい70〜80%を占めます。これらを1,000,000トークンあたり0.10でキャッシュし、残り20〜30%はフルレートで支払います。その結果、入力コストは素朴なベースライン比で約60〜70%下がります。「90%」という見出しの数値は、キャッシュヒットを測っていて、総コストではないこと、またキャッシュされた部分における実際の節約は本当に90%であることにより繰り上げられています。

2セグメントのトリック

AnthropicのAPIは、system配列の中で、セグメントごとにcache_controlマーカーを受け取ります。各マーカーは独立した区切りであり、キャッシュにはマーカーまでのトークンが最大保存されます。セグメントが2つある場合、APIはそれぞれを別々にキャッシュします。

// 概念的な形 — 実際に私たちが実行しているコードは rca-prompt.ts を参照。
const system = [
  {
    type: 'text',
    text: SYSTEM_PROMPT,                    // ~1200トークン、どこでも同一
    cache_control: { type: 'ephemeral' },
  },
  {
    type: 'text',
    text: priorIncidentsContext,            // ~600トークン、テナントごと・サービスごと
    cache_control: { type: 'ephemeral' },
  },
];

なぜ1つではなく2つのセグメントなのか?それら2つの部品で、キャッシュのライフタイムが異なるからです。

システムプロンプトはほとんど変わりません。RCAの呼び出しは、テナントをまたいで行われるすべてでキャッシュヒットします。最初の呼び出し以降は、キャッシュ読み取りはほぼ毎回発生します。

検索コンテキスト(このサービスの過去の類似インシデント)は、そのサービスで新しいインシデントが解決されてトップ-Kが更新されるたびに変わります。同一のテナント+サービスに対する単一のBatch実行の中では、繰り返しがキャッシュヒットになります。テナントをまたぐと、そうはなりません。

もし両方を1つのセグメントに詰め込むと、テナントAの検索コンテキストが変わった瞬間に、テナントBのヒット率も下がります。なぜなら、1つに結合されたセグメントは異なる形でハッシュされるからです。2つのセグメントにすると、それぞれが独立したキャッシュのライフタイムを持つため、テナントAの変動(churn)がテナントBを罰しません。

順番も重要です。Anthropicは各マーカーまでを最大キャッシュするため、より静的なセグメントを最初に置く必要があります。テナントごとの検索を先に置いて、静的なシステムプロンプトを後に置くと、静的プロンプトのキャッシュキーは、上にあるテナントごとの内容を含むようになります。つまり、最もキャッシュしやすいセグメントをテナント間でキャッシュ不能にしてしまうことになります。

キャッシュを殺すもの

頻度のおおよその順に:

5分のephemeral TTL。 キャッシュされたセグメントは、最後に書き込まれてから5分で期限切れになります。呼び出しパターンがバースト(RCAの呼び出しがインシデントの周辺に集中して、その後1時間静かになる)なら、長い無通信期間によってすべてのキャッシュセグメントが期限切れになり、次のバッチではキャッシュ書き込み(ベースレートより少し高い)を支払うことになります。可能なら呼び出しを分散させてください。難しければ、無通信期間の後の最初の数回はフルの負担になると割り切りましょう。

ホワイトスペースのドリフト。 システムプロンプトをある場所では と連結し、別の場所では と連結すると、2つの異なるキャッシュキーが生成されます。キャッシュはトークン列そのもののハッシュを計算し、意味(セマンティクス)ではありません。区切り文字を1つに決めて、それに従うようにLintしてください。

末尾の動的コンテンツ。 よくあるバグとして、「システムプロンプト」にタイムスタンプを足してしまうケースがあります — たとえば「コンテキスト」のために Today's date is 2026-05-08T14:32:01Z のような値を入れることです。このタイムスタンプは呼び出すたびに変わります。すると、タイムスタンプ以降でキャッシュされていないため、タイムスタンプを含む箇所より後は何も残りません。動的コンテンツはキャッシュ対象のセグメントから完全に排除し、代わりにユーザーメッセージのターンとして渡してください。

スキーマバージョンの頻繁な変更。 JSON出力のスキーマを反復的に調整している場合(通常は初期のプロダクトでよくあることです)、スキーマを1回編集するたびに、すべてのキャッシュされたシステムプロンプトが無効化されます。「スキーマの調整」にかかるコストは、キャッシュミスとして一部が支払われます。連続的な微調整ではなく、1回か2回の大きなスキーマ安定化のスイープを計画してください。

The production numbers

プロンプトキャッシュを有効にしたHaiku 4.5での、Batch API(それ自体が入力・出力の両方にさらに50%割引を上乗せします)を使ったときの、RCA呼び出しあたりのコストです。4000入力トークン+500出力トークン、入力トークンのおよそ75%がキャッシュされる場合:

  • 入力(キャッシュされる部分、3000トークン × 0.5バッチ × 0.10キャッシュ読み取り): $0.00015
  • 入力(キャッシュされない部分、1000トークン × 0.5バッチ × 1.00ベース): $0.00050
  • 出力(500トークン × 0.5バッチ × 5.00): $0.00125
  • キャッシュ書き込みの償却(1200トークン × 0.5バッチ × 1.25、書き込みサイクルあたり約30回のキャッシュヒットで割る): ~$0.00003

合計: ~$0.0033 / RCA呼び出し。

キャッシュなしの場合、同じ呼び出し形状でリアルタイムAPIだと、入力は約 $0.004、出力は約 $0.0025、合計で約 $0.0065 です。キャッシュだけで入力が約50%削減されます。さらにBatch APIでその上にもう50%の削減が効きます。キャッシュ+Batchの組み合わせが、RCA呼び出しあたりのコストを「1セントの3分の1程度」に収めます。

このレートにおける典型的なインシデントの束は、「機能する定額制の価格モデル」と「機能するが、最悪の単価経済性(ユニットエコノミクス)が成り立たない定額制の価格モデル」の違いです。私たちはこれを価格の考え方(pricing rationale)として文書化しています — 式の規律はマーケティングの姿勢ではなく、価格をフラットに保つための支えになる制約(ロードベアリングの制約)です。

Where this generalizes

イベントごと、インシデントごとにClaudeを呼び出すような運用をしているなら、上記の構造は、呼び出しの形状が何であっても適用されます。答えるべき問い:

  1. あなたのプロンプトで、すべての呼び出しで同一なのは何ですか? それがセグメント1です。答えが「何もない」なら、まだキャッシュ用に設計されていません — 定数を見つけてください。ほとんどの場合、何かはあります。
  2. テナントごと、またはコンテキストごとだが、短いウィンドウ内で再利用されるのは何ですか? それがセグメント2です。よくある例:リトリーバル(検索)コンテキスト、顧客固有のスタイルガイドライン、アカウントメタデータ。
  3. 本当に呼び出しごとに変わるものは何ですか? ユーザーメッセージのターンに入れます。キャッシュされるシステムブロックには入れません。
  4. 呼び出し頻度がブレークイーブンのしきい値を超えていますか? 同じキャッシュ済みプロンプトを、5分間のウィンドウで約1.25回未満しか呼ばないなら、キャッシュは損になります。ノイズの多い本番システムではボトルネックになることはほとんどありませんが、低ボリュームのツールでは起こり得ます。

このパターンはClaudeに限りません。OpenAIのプロンプトキャッシュも、数字は異なりますが同様の経済性に従います。GeminiのコンテキストキャッシュはTTLが異なりますが、「何が静的で、何が動的か」という分解は同じです。静的な部分がプロンプトの前方にまとまるようにプロンプトをセットアップする作業は、キャッシュをサポートするあらゆるモデルに対して、そのまま効果を発揮します。しかも、対応するモデルはますます増えています。

A single test

プロンプトキャッシュがあなたのパイプラインに適用できるか検討している場合、最安で、かつ最も情報量の多い最初の計測はこれです。つまり、典型的なリクエストのうち、直前のリクエストと バイト単位で完全に同一 なトークン数を数えます。「意味的に同じ」ではなく、文字通り同一です。答えが50%以上なら、キャッシュによるお金の取りこぼしがある状態です。静的プレフィックスに対してcache_controlを付け、次の請求日に入力コストの明細が下がるのを確認してください。

答えが20%未満なら、あなたのプロンプトは反復ではなくコンテキストのために設計されています。構造的な書き換えをしない限り、キャッシュはあまり効果をもたらさない可能性が高いです。いずれにせよ、数値を知るのは1時間で済み、「キャッシュの複雑さに見合うかどうか」を議論するよりも確実です。

上記のアーキテクチャが、Culpritの定額制の価格設定を経済的に筋の通ったものにしています — RCA呼び出しはインシデント周りで束になり、システムプロンプトとリトリーバルコンテキストが入力トークンを支配し、キャッシュヒット率は90%を十分に上回るところに収まっています。同じプリミティブでも、垂直領域(業界・用途)が違うだけです。請求が効いてくる規模でLLM機能を本番に投入しているなら、あなたが今すぐ使える最低労力・高い効果が見込めるリファクタリングがこれです。