「zero data retention」(ゼロデータ保持)、すなわちZDRがこの機能にどのように適用されるかについては、APIとデータ保持を参照してください。
プロンプトキャッシングはレイテンシとコストを大幅に削減しますが、それはプロンプトの先頭が直近のリクエストとバイト単位で完全に一致している場合に限られます。ツールの順序の入れ替え、システムプロンプトに補間されたタイムスタンプ、以前のメッセージへの編集などが、気づかないうちにキャッシュを無効化することがあります。キャッシュ診断がなければ、唯一のシグナルは usage.cache_read_input_tokens がゼロに落ちることだけで、何が変わったのかを示す手がかりはありません。
キャッシュ診断はそのギャップを埋めます。前回のレスポンスの id を渡すと、APIが2つのリクエストを比較し、どこで分岐したか(モデル、システムプロンプト、ツール、またはメッセージ履歴)を教えてくれるため、推測ではなく根本原因を修正できます。
キャッシュ診断はベータ版です。この機能を使用するには、APIリクエストにベータヘッダー cache-diagnosis-2026-04-07 を含めてください。
キャッシュ診断は現在、Claude APIでのみ利用可能です。Amazon BedrockやGoogle Cloudではサポートされていません。
ベータヘッダーが存在する場合、APIは各リクエストの軽量なフィンガープリントを、レスポンスの id をキーとして保存します。次のリクエストでは、その id を diagnostics.previous_message_id として含めます。APIは新しいリクエストのフィンガープリントを再構築し、保存されたものと比較して、最初の分岐点を記述する diagnostics オブジェクトをレスポンスに付加します。
この比較はリクエストの構造に関するものであり、キャッシュが実際にヒットしたかどうかとは独立しています。diagnostics の結果を usage.cache_read_input_tokens と組み合わせる方法については、使用状況と合わせて診断を読むを参照してください。
フィンガープリントにはハッシュとトークン数の推定値のみが含まれ(生のプロンプト内容は決して含まれません)、限られた期間のみ保持され、組織とワークスペースにスコープされ、他の目的には使用されません。
すべてのターンでベータヘッダーを送信してください。最初のターンでは、比較対象となる以前のメッセージなしでオプトインするために "previous_message_id": null を渡します。以降のターンでは、前回のレスポンスの id を渡します。
client = anthropic.Anthropic()
SYSTEM = "You are an AI assistant analyzing a large document. <document>...</document>"
# ターン1: previous_message_id=None でオプトイン
r1 = client.beta.messages.create(
model="claude-opus-5",
max_tokens=1024,
cache_control={"type": "ephemeral"},
system=SYSTEM,
messages=[{"role": "user", "content": "Summarize section 1."}],
diagnostics={"previous_message_id": None},
betas=["cache-diagnosis-2026-04-07"],
)
# ターン2: 前のレスポンスIDを参照
r2 = client.beta.messages.create(
model="claude-opus-5",
max_tokens=1024,
cache_control={"type": "ephemeral"},
system=SYSTEM,
messages=[
{"role": "user", "content": "Summarize section 1."},
{"role": "assistant", "content": r1.content},
{"role": "user", "content": "Now summarize section 2."},
],
diagnostics={"previous_message_id": r1.id},
betas=["cache-diagnosis-2026-04-07"],
)
diagnostics = r2.diagnostics
if diagnostics is None:
print("No divergence detected.")
elif diagnostics.cache_miss_reason is None:
print("Comparison still pending.")
else:
print(f"cache_miss_reason: {diagnostics.cache_miss_reason.type}")ストリーミングレスポンスでは、diagnostics は message_start イベントに現れます。
# ターン2: 前のレスポンスIDを参照してストリーミング
with client.beta.messages.stream(
model="claude-opus-5",
max_tokens=1024,
cache_control={"type": "ephemeral"},
system=SYSTEM,
messages=[
{"role": "user", "content": "Summarize section 1."},
{"role": "assistant", "content": r1.content},
{"role": "user", "content": "Now summarize section 2."},
],
diagnostics={"previous_message_id": r1.id},
betas=["cache-diagnosis-2026-04-07"],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
print()
r2 = stream.get_final_message()
diagnostics = r2.diagnostics
if diagnostics is None:
print("No divergence detected.")
elif diagnostics.cache_miss_reason is None:
print("Comparison still pending.")
else:
print(f"cache_miss_reason: {diagnostics.cache_miss_reason.type}")message_start イベントは完全な diagnostics フィールドを含みます。取り得る値についてはレスポンス形式を参照してください。
マルチターンの会話では、最新のレスポンスの id を各ターンの previous_message_id として引き継ぎます。最初のイテレーションではオプトインのために null を渡し、以降の各イテレーションでは前回のレスポンスの id を渡します。
このワークフローは単発のシェルコマンドにはうまく変換できません。ループパターンについてはSDKのタブを参照してください。ターンごとのHTTPリクエストは基本的な使い方と同一です。
レスポンスの Message にある diagnostics フィールドには、4つの状態があり得ます。
| 値 | 意味 |
|---|---|
| フィールドなし | リクエストに diagnostics が含まれていなかったか、ベータヘッダーが欠けていました。 |
null | previous_message_id が null だった(最初のターンで比較対象がない)か、比較が実行されて分岐が見つからなかったかのいずれかです。 |
{"cache_miss_reason": null} | レスポンスがシリアライズされた時点で比較がまだ実行中でした。これはレスポンスが非常に速く開始された場合に発生することがあります。結論が出ていないものとして扱い、次のターンを確認してください。 |
{"cache_miss_reason": {...}} | cache_miss_reason が付加されています。*_changed タイプの場合、これは最初の分岐点を特定します。previous_message_not_found と unavailable は比較が生成されなかったケースです。 |
cache_miss_reason が非nullの場合、次のようになります。
{
"id": "msg_01Xyz...",
"type": "message",
"role": "assistant",
"content": [{ "type": "text", "text": "..." }],
"usage": {
"input_tokens": 42,
"cache_read_input_tokens": 0,
"cache_creation_input_tokens": 41850,
"output_tokens": 210
},
"diagnostics": {
"cache_miss_reason": {
"type": "system_changed",
"cache_missed_input_tokens": 41850
}
}
}cache_miss_reason は type による判別共用体です。レスポンスは最も早い分岐のみを報告するため、まずそれを修正してください。後続の分岐はその背後に隠れている可能性があります。
| タイプ | 意味 | 変更すべきこと |
|---|---|---|
model_changed | model が前回のリクエストと異なります(たとえば、ルーター、A/Bテスト、フォールバックが別のモデルを選択した場合)。キャッシュはモデルごとです。 | キャッシュされた会話の中ではモデルを一定に保ってください。 |
system_changed | system パラメータが異なります。通常、タイムスタンプ、リクエストID、またはその他のリクエストごとの値がシステムプロンプトに補間されています。 | システムプロンプトをバイト単位で安定した定数にし、動的なデータはキャッシュブレークポイントの後の最初の user メッセージに移動してください。 |
tools_changed | tools 配列が異なります。ターン間でツールが追加、削除、または並べ替えられたか、ツールの input_schema JSONが非決定的にシリアライズされました。 | すべてのターンで同じツールリストを固定された順序で、決定的にシリアライズされたスキーマ(たとえば、キーをソートする)とともに送信してください。 |
messages_changed | モデル、システム、ツールはすべて一致していますが、messages の以前のエントリが追加ではなく、変更、並べ替え、または削除されました。通常、会話履歴が切り詰められたり編集されたり、アシスタントのターンや tool_result ブロックが再送信時に異なる形で再シリアライズされたりしています。 | 履歴を追加専用として扱い、アシスタントの content とツール結果をそのまま返してください。 |
previous_message_not_found | 指定された previous_message_id に対する保存済みフィンガープリントが存在しません。これはリクエストが変更されたことの証拠ではありません。通常、前回のリクエストがベータヘッダーを持っていなかったか、別のワークスペースから送信されたか、送信されてから時間が経ちすぎています。 | すべてのターンでベータヘッダーを送信し、連続するターンの時間間隔を短く保ってください。 |
unavailable | このリクエストに対して診断情報が利用できませんでした。これには、model、system、tools は一致しているが、プロンプトに影響する別のリクエストパラメータ(tool_choice、thinking、context_management、output_config、output_format、または有効な anthropic-beta ヘッダーのセット)が異なる場合や、分岐が比較範囲を超えている非常に長い会話が含まれます。リクエストは通常どおり処理されました。 | キャッシュされた会話の存続期間中、プロンプトに影響するリクエストパラメータを一定に保ってください。問題が続く場合は、プロンプトキャッシングのページにあるよくある問題のトラブルシューティングの手動チェックを適用してください。 |
4つの *_changed タイプには cache_missed_input_tokens という整数も含まれます。これは分岐点より後にあった入力トークン数の推定値で、どれだけのキャッシュ可能なプレフィックスが失われたかの目安になります。トークン化前のバイト長から導出されるため、課金の数値ではなく規模の指標として扱ってください。usage.input_tokens と異なる(場合によっては超える)ことがあります。
diagnostics は「リクエストは変わったか?」に答え、usage.cache_read_input_tokens は「キャッシュはヒットしたか?」に答えます。両者を組み合わせることで、どこを調べるべきかがわかります。
このマトリクスは、実際の previous_message_id を渡したターンに適用されます。最初のターン(previous_message_id: null)では、diagnostics は常に null であり、キャッシュは読み取りではなく書き込み中であるため cache_read_input_tokens は通常ゼロです。トラブルシューティングは不要です。また、このマトリクスは cache_miss_reason が null の場合(比較がまだ保留中。次のターンを確認してください)や、その type が previous_message_not_found または unavailable の場合(比較が生成されなかった)には適用されません。
| 診断結果 | キャッシュ読み取りトークン | 解釈 |
|---|---|---|
null | 多い | 期待どおりに動作しています。プレフィックスは安定しており、キャッシュがヒットしました。 |
null | 少ないまたはゼロ | リクエストは一致していますが、キャッシュエントリがもう利用できませんでした。ターン間の間隔を短くするか、1時間キャッシュTTLの使用を検討してください。 |
cache_miss_reason が *_changed タイプ | 少ないまたはゼロ | あなたのバグです。リクエストが変更されました。type が示す原因を修正してください。 |
cache_miss_reason が *_changed タイプ | 多い | まれなケースです。プロンプトの後半で変更が発生しましたが、それより前の cache_control ブレークポイントはヒットしました。修正する価値はありますが、影響は小さいです。 |
previous_message_id の検索用フィンガープリントは短期間で期限切れになります。診断比較は時間的に近接したリクエスト間で実行してください。unavailable が返される場合があります。unavailable を返すか、比較がまだ実行中だった場合は cache_miss_reason: null を返します。キャッシュ診断はZDR対象(適格)です。Anthropicはこの機能のためにプロンプトの生テキストやClaudeの出力を保存しません。
各リクエストに対して保存されるフィンガープリントは、暗号学的ハッシュとトークン数の推定値のみで構成され、レスポンスの id をキーとし、組織とワークスペースにスコープされます。フィンガープリントは短期間で期限切れになり、他の目的には使用されません。
すべての機能にわたるZDR適格性については、APIとデータ保持を参照してください。
Was this page helpful?