Anthropic APIのPrompt Cachingを理解する — コスト削減の仕組みと適用判断
Anthropic APIのPrompt Cachingは、長いsystem promptやドキュメントのコンテキストを再利用するときにコストとレイテンシを大幅に下げる機構。仕組みと適用が効くシーンを解説します。
要点
Prompt CachingはAnthropic APIが提供する仕組みです。長いsystem promptやドキュメントのコンテキストを再利用するときに、大幅なコスト削減と低レイテンシを実現します。最初の呼び出しでキャッシュを書き込み、その後の呼び出しではキャッシュを読むだけの設計です。
効くかどうかは、仕組みよりも「ブレークポイントをどこに置くか」と「プレフィックスが呼び出しごとに変わらないか」で決まります。この2点を外すと、書き込み料金だけを払い続けることになります。
なぜキャッシュでコストが下がるか
通常のAPI呼び出し
普通のAPI呼び出しでは、毎回system prompt + user message + (tools定義など)を送信します。これらすべてが毎回入力トークンとして課金されます。
[Call 1] system prompt(5000 tokens)+ user(50)+ tools(2000) → 7050 input tokens
[Call 2] system prompt(5000 tokens)+ user(50)+ tools(2000) → 7050 input tokens
[Call 3] ...10回の繰り返しで70,500 input tokens × 単価がかかります。
Prompt Cachingを使う場合
最初の呼び出しでキャッシュ書き込み(write)が走り、以降の呼び出しは キャッシュ読み込み(read)で済みます。
[Call 1] system + tools をキャッシュ書き込み → 7000 tokens(write 単価、+25%)+ user 50
[Call 2] system + tools はキャッシュ読み込み → 7000 tokens(read 単価、-90%)+ user 50
[Call 3] ...読み込み時の単価は通常の約1/10(0.1x)です。Claude Fable 5.1とMythos 5.1は1/40(0.025x)です。Claude Opus 5.5とSonnet 5.5は1/20(0.05x)になります。書き込み時は1.25x(初回コスト)になります。
上の例で10回呼び出したときの総額を、Sonnet 5.5とOpus 5.5の単価で計算します。キャッシュ対象はsystemとtoolsの7,000トークン、userの50トークンは毎回通常料金です。
10回呼び出したときの input 総額
Sonnet 5.5(キャッシュなし)
約$0.141
70,500トークン × $2 / MTok
Sonnet 5.5(5分キャッシュ)
約$0.025
書き込み$0.0175 + 読み込み$0.0063 + user$0.001
Opus 5.5(キャッシュなし)
約$0.282
70,500トークン × $4 / MTok
Opus 5.5(5分キャッシュ)
約$0.050
書き込み$0.035 + 読み込み$0.0126 + user$0.002
通常料金に対する比率は、Sonnet 5.5もOpus 5.5も約18%です。どちらも読み込み単価が0.05xなので、割引率は同じになります。出力トークンはキャッシュの影響を受けないので、出力が長い用途では全体の削減幅はこれより小さくなります。
料金の内訳
倍率は5分書き込みが1.25x、1時間書き込みが2x、読み込みが0.1xです(一部モデルは読み込みだけ別倍率)。モデルごとの実額は次のとおりです。モデルの選び方との兼ね合いはClaudeモデル選び方ガイドにまとめています。
| モデル | 通常input | 5分書き込み | 1時間書き込み | 読み込み |
|---|---|---|---|---|
| Fable 5.1 | 通常input$10 | 5分書き込み$12.50 | 1時間書き込み$20 | 読み込み$0.25 |
| Fable 5 | 通常input$10 | 5分書き込み$12.50 | 1時間書き込み$20 | 読み込み$1 |
| Opus 5.5 | 通常input$4 | 5分書き込み$5 | 1時間書き込み$8 | 読み込み$0.20 |
| Opus 5 / 4.8 / 4.7 / 4.6 / 4.5 | 通常input$5 | 5分書き込み$6.25 | 1時間書き込み$10 | 読み込み$0.50 |
| Sonnet 5.5 | 通常input$2 | 5分書き込み$2.50 | 1時間書き込み$4 | 読み込み$0.10 |
| Sonnet 5 | 通常input$2 | 5分書き込み$2.50 | 1時間書き込み$4 | 読み込み$0.20 |
| Sonnet 4.6 / 4.5 | 通常input$3 | 5分書き込み$3.75 | 1時間書き込み$6 | 読み込み$0.30 |
| Haiku 4.5 | 通常input$1 | 5分書き込み$1.25 | 1時間書き込み$2 | 読み込み$0.10 |
単価は100万トークンあたりの米ドルです。同じ表に載るMythos 5.1とMythos 5はFable 5.1・Fable 5と同じ単価で、限定提供のモデルです。倍率はBatch APIの割引などとも重なります。
5分版と1時間版の使い分け
TTL(キャッシュの有効期間)は5分がデフォルトで、ttlを指定すると1時間にできます。
| TTL | 指定 | 向く場面 |
|---|---|---|
| 5分(デフォルト) | 指定{"type": "ephemeral"} | 向く場面呼び出しが5分より短い間隔で続く |
| 1時間 | 指定{"type": "ephemeral", "ttl": "1h"} | 向く場面間隔が5分を超えやすいが、1時間以内には次が来る |
5分版はヒットするたびにTTLが無料で更新されるので、トラフィックが続く限り維持されます。1時間版は書き込みが2xと割高なため、損益分岐が変わります。共通部分だけで数えると、5分版は2回目の呼び出しで1.35x(通常の2.0xより安い)に収まります。1時間版は2回で2.1xと通常より高く、3回目で2.2x(通常は3.0x)になって初めて逆転します。
つまり1時間版は、5分で切れてしまう間隔の用途で3回以上使い回せるときに回収できる設計です。読み込みが安いFable 5.1 / Mythos 5.1では、max_tokens: 0 の空リクエストで5分版を延命するほうが1時間版より安くなる場合もあります。計算はClaude Fable 5.1の仕様解説の料金の節にあります。
TTLは、キャッシュを書き込むか読むリクエストの開始時点から数えます。応答の生成にかかった時間も寿命から引かれるので、応答のストリーミングに4分かかった場合、同じプレフィックスを再利用する次のリクエストは、応答が終わってから約1分以内に始める必要があります。
最小キャッシュトークン数の壁
Prompt Cachingで最初につまずきやすいのが、短いブロックはキャッシュ対象にならない点です。cache_controlを付けても、対象プレフィックスがモデルごとの最小トークン数に満たないと、キャッシュは書き込まれません。
| 最小トークン数 | モデル |
|---|---|
| 512 | モデルFable 5.1 / Fable 5 / Opus 5.5 / Opus 5 / Sonnet 5.5 |
| 1,024 | モデルOpus 4.8 / Sonnet 5 / Sonnet 4.6 / Sonnet 4.5 |
| 2,048 | モデルOpus 4.7 |
| 4,096 | モデルOpus 4.6 / Opus 4.5 / Haiku 4.5 |
世代が新しいほど小さいわけではなく、モデルごとに個別の値です。
厄介なのは、閾値を下回ってもエラーは返らないことです。cache_controlは受け付けられます。レスポンスのcache_creation_input_tokensとcache_read_input_tokensは両方とも0のままで、キャッシュされていない状態です。「指定したのに効かない」ときは、まずこの最小トークン数を疑うと切り分けが早く進みます。閾値にわずかに届かない場合は、キャッシュ対象の内容を足して閾値を超えたほうが、頻繁に再利用するプロンプトでは安くなることがあります。
キャッシュが効く場面
Prompt Cachingが効くシーンの典型は次の4つです。
1. 長いsystem promptを持つチャットボット
カスタマーサポート / 社内Q&A / FAQボット等で、system promptに数千tokensのドメイン知識を持たせている場合。会話のターンが続くたびに同じsystem promptが再送信されるので、キャッシュの効果が大きくなります。
2. 大規模documentをcontextに持つRAG
10万トークンのドキュメントセットをコンテキストとして持たせ、複数のクエリを投げる場合。documentが共通でクエリだけ変わる構造はキャッシュに向きます。
3. tools定義が長いagent
20個以上のツールを定義したエージェントでは、tools定義が毎回のリクエストで大きな割合を占めます。会話が続く間、tools定義は変わらないのでキャッシュに向きます。
4. バッチ処理(同じ指示で多数のレコード処理)
「100件のレコードに対し、共通の評価promptで採点」のような構造はキャッシュの典型例。最初の1回でキャッシュを温めれば、99回は読み込み単価で済みます。
並列に投げる場合は順序に注意が要ります。キャッシュのエントリは最初のレスポンスが始まってから使えるようになるので、100件を一斉に送ると、全件が書き込みになりえます。最初の1件の応答が返り始めるのを待ってから、残りを送ります。
Message Batches APIで投げる場合は、非同期かつ並列で処理されるため、キャッシュヒットはベストエフォートです。処理が5分を超えることもあるので、1時間版のキャッシュが向きます。
キャッシュが効かない場面
逆にキャッシュが効かない、または不利になるケースがあります。
1. system promptが毎回変わる
会話ごとにsystem promptをユーザー入力で組み立てる場合、キャッシュキーが毎回異なるためヒットしません。書き込みコスト(1.25x)だけ無駄に発生します。
2. 1ユーザーあたりの呼び出しが1回限り
「1回prompt投げて終わり」のフロー(検索クエリ単発処理 等)では、書き込みコストが回収できません。
3. キャッシュTTLを超える間隔
5分版のキャッシュは、最後に使ってから5分経つと消えます。ユーザーが10分以上会話を中断するボットでは、キャッシュが頻繁に再書き込みされて効果が落ちます。ヒットするたびにTTLは更新されるので、処理全体を5分で終える必要はなく、リクエストの間隔が5分以内なら維持されます。
4. ワークスペースをまたぐ共有を期待する
キャッシュは組織間で共有されず、Claude APIではワークスペースごとにも分離されます(BedrockとGoogle Cloudは組織単位)。同じプロンプトでも、別のワークスペースのキーで呼べば初回は書き込みです。複数のワークスペースを使い分けているチームは、呼び出し元を揃える必要があります。
ブレークポイントの置き場所とプレフィックス階層
cache_controlは「ここまでのプレフィックスをキャッシュする区切り」を意味します。この区切りをキャッシュブレークポイントと呼び、1リクエストにつき最大4つまで置けます。各ブレークポイントでは、その時点までの内容を累積したハッシュをキーに、ちょうど1つのキャッシュエントリが書き込まれます。
ブレークポイントを増やしても追加料金はかかりません。払うのは、書き込まれたトークンと、読み込まれたトークンと、通常のinputトークンだけです。
プレフィックスはtools → system → messagesの順で構成され、前の階層が変わると後ろの階層のキャッシュも無効になる従属関係があります。
| 変更箇所 | tools | system | messages |
|---|---|---|---|
| tools定義を変更 | tools無効 | system無効 | messages無効 |
| systemを変更 | tools有効 | system無効 | messages無効 |
| messagesを変更 | tools有効 | system有効 | messages無効 |
この階層を踏まえると、変わりにくいものを前に、変わりやすいものを後ろに置く設計が効きます。tools定義とsystem promptを安定させ、ユーザー入力をmessages側に逃がすほど、キャッシュヒットが長く続きます。
キャッシュ前提のcontext設計は、Anthropic Engineeringのcontext engineering解説でも扱われています。
tools以外にも、messagesだけを無効にする変更があります。tool_choiceの変更、画像の追加・削除、thinking設定(budget_tokensなど)やoutput_config.effortの変更です。thinkingとeffortは、モデルによってはtoolsとsystemのキャッシュも巻き込みます。ウェブ検索や引用機能のオン・オフはsystem promptを書き換えるので、system以降が無効になります。
変わるブロックに置くと、毎回書き込みになる
効かないブレークポイントの典型は、リクエストごとに変わるブロックへ置いてしまう形です。たとえば、固定のsystem(ブロック1〜5)のあとに、タイムスタンプとユーザー入力を含むブロック6があるとします。
ブレークポイントの置き場所
ブロック6に置く
リクエスト1でタイムスタンプ入りの内容が書き込まれます。リクエスト2ではタイムスタンプが違うため、ハッシュが一致しません。遡っても、ブロック1〜5には書き込みが無いのでヒットしません。毎回、書き込み料金を払って読み込みは一度も起きません。
ブロック5に置く
ブロック5は、リクエストをまたいで変わらない最後のブロックです。ここへ置けば、2回目以降は書き込み済みのエントリを読めます。
遡り探索は、安定している内容を見つけて新しくキャッシュするのではなく、過去のリクエストが書き込んだエントリを探すだけです。ブレークポイントは、リクエスト間で同じ内容になる最後のブロックに置きます。
20ブロックの遡り範囲
キャッシュ読み込みは、現在のブレークポイントから最大20ブロック前までを遡って、過去のリクエストが書き込んだエントリを探します。連続するtool_useブロックは1つ、連続するtool_resultブロックも1つとして数えるので、並列ツール呼び出しだけでは窓から押し出されにくくなっています。
会話が伸びる様子を、毎ターン最後のブロックにブレークポイントを置くケースで追います。
会話が伸びたときの遡り
- ターン110ブロック、ブロック10に置く
過去の書き込みが無いので、ブロック10にエントリが書き込まれます。
- ターン215ブロック、ブロック15に置く
ブロック15には無いので遡り、ブロック10でヒットします。ブロック11〜15だけが新しく処理され、ブロック15に書き込まれます。
- ターン335ブロック、ブロック35に置く
ブロック35から16までの20位置を調べても見つかりません。ブロック15のエントリは窓の1つ外で、ヒットしません。
ターン3の対策は、ブロック15の位置に2つ目のブレークポイントを置くことです。そこから新しい遡り窓が始まり、ターン2のエントリを拾えます。ただし遡りは過去に書き込まれたエントリしか見つけられないので、窓が外れそうなブロック数に達する前から、近くにブレークポイントを置いて書き込みを溜めておく必要があります。最大4つまで使えるので、長い会話では複数を段階的に配置します。長く動くエージェントでの会話の持たせ方は長時間エージェントのハーネス設計にあります。
自動キャッシュと明示ブレークポイントの選び方
リクエストのトップレベルにcache_controlを1つ置く自動キャッシュがあり、最後のキャッシュ可能なブロックに自動でブレークポイントが付きます。会話が伸びるたびに、ブレークポイントも最後のブロックへ前進します。
{
"model": "claude-opus-5-5",
"max_tokens": 1024,
"cache_control": { "type": "ephemeral" },
"system": "You are a helpful assistant.",
"messages": [{ "role": "user", "content": "..." }]
}自動キャッシュは、料金・最小トークン数・20ブロックの遡り窓が明示ブレークポイントと同じです。選び分けは、末尾に変わるブロックがあるかどうかで決まります。
- マルチターンの会話で、毎ターン末尾が新しいメッセージになる: 自動キャッシュで足ります
- 末尾にタイムスタンプやリクエストごとの文脈が付く: 自動キャッシュは変わるブロックに付いてしまうので、固定部分の最後に明示ブレークポイントを置きます
- システムプロンプトと会話を別々にキャッシュしたい: 明示ブレークポイントと自動キャッシュを併用します。自動キャッシュは4つの枠の1つを使います
併用では、最後のブロックにTTLの異なるcache_controlが付いていると400エラーになります。明示ブレークポイントがすでに4つあるときも400です。従来のAmazon Bedrock(Opus 4.6以前)の連携は、トップレベルのcache_control自体が400になるため、明示ブレークポイントを使います。
実装パターン
Python SDKの例
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
system=[
{
"type": "text",
"text": "あなたは社内 FAQ ボットです。\n\n[ここに 5000 tokens の社内ナレッジ]",
"cache_control": {"type": "ephemeral"}, # ← キャッシュ指定
}
],
messages=[{"role": "user", "content": "退職時の手続きを教えて"}],
)cache_control を付けたブロックがキャッシュ対象になります。
TypeScript SDKの例
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
system: [
{
type: "text",
text: "あなたは社内 FAQ ボットです。\n\n[5000 tokens]",
cache_control: { type: "ephemeral" },
},
],
messages: [{ role: "user", content: "退職時の手続きを教えて" }],
});キャッシュ効果の確認
APIレスポンスには usage フィールドが含まれ、キャッシュヒットの統計が確認できます。
{
"usage": {
"input_tokens": 50,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 5000,
"output_tokens": 200
}
}cache_creation_input_tokens: 今回の書き込みtokenscache_read_input_tokens: 今回の読み込みtokensinput_tokens: 最後のブレークポイントより後ろの、キャッシュ対象外のtokens
ここで読み違えやすいのがinput_tokensです。これは入力全体ではなく、最後のブレークポイント以降のトークンだけを指します。キャッシュが効いているときはinput_tokensが小さく、cache_read_input_tokensが大きくなるのが正常な姿です。実際に処理された入力の合計は3つの合算(cache_read + cache_creation + input)で求まります。
レート制限(ITPM)では、ほとんどのモデルでcache_read_input_tokensは数えません。対象はinput_tokensとcache_creation_input_tokensの合計で、Haiku 3.5だけは読み込みも数えます。キャッシュが効くほど、実効スループットが上がります。
1時間版を使ったときは、usageの中にcache_creationオブジェクトが加わります。ephemeral_5m_input_tokensとephemeral_1h_input_tokensに書き込みが分かれて出ます。その合計がcache_creation_input_tokensです。ウェブ検索などのサーバーツールを使うと、指定していない5分版の書き込みが出ることもあります。
効いていないときの切り分け
cache_read_input_tokensが常に0のとき、次の順に見ると原因に早く届きます。
効かないときの切り分け順
- 1
トークン数を見る
cache_creation_input_tokensも0なら、最小トークン数(モデルごとに512〜4,096)に届いていません。この場合はエラーも返りません。 - 2
ブレークポイントの位置を見る
置いたブロックが、タイムスタンプや入力メッセージのように毎回変わる内容を含んでいないかを調べます。
cache_creation_input_tokensが毎回大きいのは、この形のサインです。 - 3
プレフィックスの揺れを見る
tool定義・画像・
tool_choice・thinking設定・effortが、呼び出しの間で変わっていないかを調べます。thinkingの設定変更でキャッシュが外れた事例もあります。SwiftやGoのようにJSON変換でキーの順序が入れ替わる言語では、tool_useブロックのキー順を固定します。 - 4
時間を見る
呼び出しの間隔が5分(1時間版なら1時間)を超えていないかを調べます。起点はリクエストの開始時点で、応答の生成時間も含みます。
APIには、連続する2つのリクエストを比べて、プレフィックスがどこで食い違ったかを返すキャッシュ診断の機能もあります。
まとめ
Prompt Cachingは「ブレークポイントまでの内容が毎回同じ」ことが成立条件です。長い静的promptを持つRAG / チャットボット / バッチ処理では効きますが、promptが毎回変わる用途では効果が出ません。導入前に、固定部分が最小トークン数を超えるかと、呼び出し間隔がTTLに収まるかの2点を見ておくと、「使ったが効かなかった」を避けられます。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。