
Claudeのプロンプトキャッシュの失敗の原因を検知してみる
リテールアプリ共創部の末永です。
Claude のプロンプトキャッシュは、長いシステムプロンプトや tool 定義を使うアプリケーションで、コストやレイテンシを抑えるために使える機能です。
ただ、プロンプトキャッシュが利用されなかったときに、原因を追うのは少し面倒です。Cache diagnostics という機能が追加されたので、キャッシュが利用されない原因を検知できるか試してみました。
Cache diagnostics で分かること
Claude の Prompt caching は、プロンプトの先頭部分が前回リクエストと一致している場合に利用されます。system prompt に timestamp を埋め込んだり、tool の順番が変わったりすると、意図せずキャッシュ対象の先頭部分(prefix)が変わります。
Cache diagnostics では、前回レスポンスの id を次のリクエストに渡すことで、前回と今回のリクエストのどこが変わったかを確認できます。
Cache diagnostics closes that gap. Pass the
idof your previous response, and the API compares the two requests and tells you where they diverged (the model, the system prompt, the tools, or the message history) so you can fix the root cause instead of guessing.
返ってくる cache_miss_reason.type には、system_changed や tools_changed などがあります。system prompt が変わった、tool 定義が変わった、message history が変わった、といった原因をレスポンスから見られるようになります。

Prompt caching には、リクエスト全体に cache_control を付けて cache breakpoint を自動で進める方法と、コンテンツブロックに明示的に cache_control を付ける方法があります。今回は system prompt を変えたときに system_changed を検知したいため、後者の Explicit cache breakpoints を使います。
なお、diagnostics は「リクエストが変わったか」を見るための情報です。実際にキャッシュがヒットしたかは usage.cache_read_input_tokens 側を見る必要があります。
AI SDK の呼び出しに diagnostics を足す
今回の検証では Vercel AI SDK を使いました。AI SDK の Anthropic provider には Prompt caching 用の cacheControl がありますが、Cache diagnostics の diagnostics.previous_message_id は通常のオプションから直接渡せなかったため、createAnthropic の fetch で body に追加しています。
該当部分だけ抜き出すと次のような感じです。
const anthropic = createAnthropic({
headers: {
"anthropic-beta": "cache-diagnosis-2026-04-07",
},
fetch: async (input, init) => {
const headers = new Headers(init?.headers);
headers.set("anthropic-beta", "cache-diagnosis-2026-04-07");
const body = JSON.parse(String(init?.body)) as Record<string, unknown>;
body.diagnostics = {
previous_message_id: previousMessageId,
};
const response = await fetch(input, {
...init,
headers,
body: JSON.stringify(body),
});
lastResponse = await response.clone().json();
return response;
},
});
Cache diagnostics は beta header が必要なので、anthropic-beta も付けています。レスポンスは response.clone().json() で読み、diagnostics を後続の処理で使えるようにしています。
キャッシュ対象にしたい system message には、AI SDK の providerOptions で cacheControl を付けます。
const messages: ModelMessage[] = [
{
role: "system",
content: systemPrompt,
providerOptions: {
anthropic: {
cacheControl: { type: "ephemeral" },
},
},
},
{
role: "user",
content: "~",
},
];
あとは通常どおり generateText を呼びます。
const result = await generateText({
model: anthropic(MODEL),
messages,
});
補足ですが、AI SDK では messages 配列に role: "system" を含めると warning が出ます。system prompt は top-level の system option に渡す形が推奨されています。
cache_miss_reason を Langfuse で WARNING にする
今回見たいのは、キャッシュが利用されなかったときに原因を検知できるかです。そこで、cache_miss_reason.type が *_changed だった場合に Langfuse の observation を WARNING にします。
function warningFromDiagnostics(diagnostics: Diagnostics | undefined) {
const reason = diagnostics?.cache_miss_reason;
if (!reason) {
return undefined;
}
if (reason.type.endsWith("_changed")) {
return `Claude cache diagnostics warning: ${reason.type}`;
}
return `Claude cache diagnostics inconclusive: ${reason.type}`;
}
この関数を使って、Langfuse の generation に level と statusMessage を設定します。
const warning = warningFromDiagnostics(lastResponse?.diagnostics);
generation.update({
output: {
text: result.text,
diagnostics: lastResponse?.diagnostics ?? null,
cacheReadTokens: result.usage.inputTokenDetails.cacheReadTokens,
cacheWriteTokens: result.usage.inputTokenDetails.cacheWriteTokens,
},
level: warning ? "WARNING" : "DEFAULT",
statusMessage: warning ?? "No cache-prefix divergence reported.",
});
今回は system prompt の一部を変えて、意図的に system_changed が返る状態を作りました。これで Langfuse 上でも、該当の generation が WARNING として見れます。
キャッシュされないケースを確認する
検証では、キャッシュ対象にしている system prompt の一部だけを変えました。
この状態で前回レスポンスの id を diagnostics.previous_message_id に渡すと、cache_miss_reason に system_changed が返りました。


画像の cacheReadTokens が 0 になっていることからも、このリクエストではキャッシュが利用されていないことが分かります。
最後に
Cache diagnostics を使うと、プロンプトキャッシュが利用されなかった原因を API レスポンスとして確認できました。
すべてのアプリケーションに必須というより、長いシステムプロンプトや tool 定義をキャッシュしていて、キャッシュミスがコストやレイテンシに影響しやすい場合に入れておくと便利そうです。ここでは Langfuse を使用していますが、他の観測ツールでももちろん活用できます。
では👋








