关于"zero data retention"(零数据保留),即 ZDR 如何适用于此功能,请参阅 API 与数据保留。
提示缓存可以显著降低延迟和成本,但前提是您的提示开头与最近的请求逐字节完全相同。工具顺序的改变、插入到系统提示中的时间戳,或对较早消息的编辑,都可能在不知不觉中使缓存失效。如果没有缓存诊断,唯一的信号就是 usage.cache_read_input_tokens 降为零,而没有任何关于发生了什么变化的指示。
缓存诊断填补了这一空白。传入您上一个响应的 id,API 会比较这两个请求并告诉您它们在哪里出现了分歧(模型、系统提示、工具或消息历史),这样您就可以修复根本原因,而不是靠猜测。
缓存诊断目前处于测试阶段。请在您的 API 请求中包含 beta 标头 cache-diagnosis-2026-04-07 以使用此功能。
缓存诊断目前仅在 Claude API 上可用。Amazon Bedrock 或 Google Cloud 不支持此功能。
当存在 beta 标头时,API 会存储每个请求的轻量级指纹,以响应 id 作为键。在您的下一个请求中,将该 id 作为 diagnostics.previous_message_id 包含进来。API 会为新请求重建指纹,将其与存储的指纹进行比较,并在响应中附加一个 diagnostics 对象,描述第一个分歧点。
该比较针对的是请求结构,与缓存是否实际命中无关。请参阅结合 usage 解读诊断结果,了解如何将 diagnostics 结果与 usage.cache_read_input_tokens 结合使用。
指纹仅包含哈希值和令牌数量估计值(绝不包含原始提示内容),保留时间有限,作用域限定于您的组织和工作区,并且不会用于任何其他目的。
在每一轮都发送 beta 标头。在第一轮中,传入 "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}")在 streaming(流式传输)响应中,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。
此工作流程不太适合用一次性的 shell 命令来表达。请参阅 SDK 选项卡了解循环模式;每轮的 HTTP 请求与基本用法相同。
响应 Message 上的 diagnostics 字段有四种可能的状态:
| 值 | 含义 |
|---|---|
| 字段不存在 | 请求未包含 diagnostics,或缺少 beta 标头。 |
null | 要么 previous_message_id 为 null(第一轮,没有可比较的内容),要么比较已运行且未发现分歧。 |
{"cache_miss_reason": null} | 响应被序列化时比较仍在运行中。当响应启动非常快时可能会发生这种情况。将其视为不确定,并检查下一轮。 |
{"cache_miss_reason": {...}} | 附加了 cache_miss_reason。对于 *_changed 类型,这标识了第一个分歧点;previous_message_not_found 和 unavailable 是未产生比较结果的情况。 |
当 cache_miss_reason 非空时,它看起来像这样:
{
"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 没有对应的已存储指纹。这并不能证明您的请求发生了变化。通常是上一个请求没有携带 beta 标头、来自不同的工作区,或者自发送以来已经过了太长时间。 | 在每一轮都发送 beta 标头,并使连续轮次在时间上保持接近。 |
unavailable | 此请求的诊断信息不可用。这包括 model、system 和 tools 都匹配,但另一个影响提示的请求参数(tool_choice、thinking、context_management、output_config、output_format 或活动的 anthropic-beta 标头集合)不同的情况,以及分歧超出比较范围的超长对话。您的请求已正常处理。 | 在缓存对话的整个生命周期内保持影响提示的请求参数不变。如果问题持续存在,请应用提示缓存页面上常见问题排查中的手动检查。 |
四种 *_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 类型 | 低或零 | 您的 bug。请求发生了变化;请修复 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?