Claude Prompt缓存怎么验证命中?用前缀、断点和usage做A/B测试
验证Claude Prompt caching不能只看请求里有没有cache_control。一组完整证据至少包括:请求A写入缓存,请求B在有效期内复用相同前缀,并在usage里出现缓存读取Token。即使B有cache_read_input_tokens,也只能证明某段前缀被复用,不能证明整个Prompt都命中。若没有同时保存模型、渠道、断点位置和请求条件,单独一张usage截图无法复现结论。
动态边界:本文按 2026-08-11 查阅的 Anthropic Prompt caching 和 Messages 文档撰写。模型、渠道、最低长度、TTL 和价格都可能变化,发布或上线前要按当日文档重新核对。
三个usage字段分别说明什么
Anthropic Messages API当前使用以下字段记录输入Token:
| 字段 | 代表什么 | 排查时怎么看 |
|---|---|---|
cache_creation_input_tokens |
本次写入缓存的输入Token | 首次写入或创建新前缀时可能大于0 |
cache_read_input_tokens |
本次从现有缓存读取的输入Token | 后续请求是否复用前缀的直接证据 |
input_tokens |
最后一个缓存断点之后的输入Token | 不能单独当作本次全部输入 |
总输入量可按当前文档核对:
total_input_tokens = cache_read_input_tokens
+ cache_creation_input_tokens
+ input_tokens
典型基线是:A出现creation,B出现read。B也可能在读取旧前缀的同时写入新增前缀,因此不能看到creation就断言“没有命中”。如果creation和read都为0,先检查长度门槛、断点位置以及目标API是否支持缓存。
缓存比较的是哪一段
Prompt caching覆盖断点之前的完整前缀,顺序是tools → system → messages。显式断点所在块也包含在前缀内。
真正需要稳定的是这些语义结构和内容:
- 工具定义、工具数组顺序与schema;
- system各内容块及其顺序;
- 断点之前的消息、角色和内容块顺序;
- 图片、文档、thinking等被放入前缀的内容。
时间戳、随机ID、本轮检索结果或用户问题若放在断点之前,会形成新前缀。客户端重新排列数组、改写内容块或插入消息,也会影响匹配。
不要把“原始JSON文本必须字节级一致”写成官方规则。公开文档描述的是完整Prompt前缀,未公布服务端缓存键如何处理JSON对象键顺序。本地可以比较解析后的请求结构,但最终是否命中仍以API返回的usage为准。
自动缓存与显式断点怎么选
自动缓存:适合持续追加的对话
在请求顶层设置cache_control后,系统会把自动断点放到最后一个可缓存块,并随着对话增长向后移动。它适合历史消息主要在尾部追加、早期内容不被重写的多轮对话。
自动缓存也占用一个断点槽位。若最后一个块已经有相同TTL的显式断点,自动缓存不重复创建;TTL不同,或已有4个显式断点而没有剩余槽位时,当前API会返回400。
渠道边界要单独看。Anthropic 当前页面列出的例外是 legacy Amazon Bedrock(Opus 4.6 及更早集成)不支持顶层自动缓存;该集成上应改用显式断点。Bedrock 其他模型或集成以及其他云渠道、兼容网关,都要以各自当前请求格式为准,不要从这一例外外推。
显式断点:适合稳定资料加动态问题
把cache_control放在最后一个稳定内容块上,动态查询留在断点之后:
{
"model": "<按当前文档选择的模型>",
"max_tokens": 256,
"system": [
{
"type": "text",
"text": "<稳定且达到当前模型门槛的资料>",
"cache_control": { "type": "ephemeral" }
}
],
"messages": [
{
"role": "user",
"content": "<每次变化的问题>"
}
]
}
示例不提供具体Token门槛,因为支持模型与最低长度会变化。请求低于门槛时,API可能正常响应但不创建缓存;上线前应查目标模型与渠道的当日说明。
一套可复现的A/B/C测试
步骤1:固定环境
记录模型ID、API或渠道、客户端版本、TTL和断点方式。固定tools、system、thinking配置与输出参数,关闭会在稳定前缀中注入时间或随机值的中间件。
步骤2:发送A,建立写入
用不含生产数据的测试样本发送请求A,保存测试编号、时间、模型、断点位置及完整usage。缓存条目在首个响应开始后才能被其他请求读取,所以不要让A与B同时起跑。
步骤3:发送B,验证读取
在目标渠道当前记载的 TTL 内发送B(文档默认通常为5分钟,但上线前仍要核对)。保持断点之前的前缀不变,只允许修改断点之后的动态查询。查看B的cache_read_input_tokens,同时记录creation和input。
步骤4:发送C,制造单变量差异
选择一种改动:移动断点、修改一处system内容、调整tools数组,或把时间戳移入稳定区。一次只改一项,再比较usage变化。A、B、C必须使用同一模型、同一渠道、同一客户端版本和同一TTL;tools、system、thinking、输出配置与断点前内容均保持不变。
步骤5:按证据强度下结论
| 观察结果 | 可以支持 | 不能直接推出 |
|---|---|---|
| A写入,B读取 | B复用了已写入的某段前缀 | 线上所有请求都会命中 |
| B同时读取和写入 | 读取了旧前缀,并可能缓存新增部分 | B完全没有变化 |
| A、B都只有写入 | 两次未复用同一条目或原条目不可用 | 一定是某个字段导致 |
| creation和read都为0 | 可能低于门槛、配置未生效或渠道不支持 | 平台缓存服务故障 |
| C的读取量下降 | 单变量改动影响可复用范围 | 它是生产命中率低的唯一原因 |
20-block回溯如何影响长对话
当前文档说明,每个断点最多向前检查20个块,并把当前断点算作第一个位置。写入只发生在设置断点的位置;回溯只能寻找以前已经写入的条目,不会替你在任意早期位置补写缓存。
如果对话一次新增很多内容块,当前断点可能在20个位置内找不到早先条目。可以把断点放在稳定前缀末尾,或者事先设置额外断点。当前最多可用4个断点,不能等到回溯窗口已经越过旧条目后再指望新断点找回它。
如何安全比较两次请求
不要直接把生产Prompt写进排查日志,也不要简单哈希密钥、姓名等低熵敏感值。更安全的做法是使用无客户数据的固定测试夹具,在本地比较解析后的结构:
- 对比模型、TTL和断点位置;
- 对比tools数量、名称顺序和schema版本;
- 对比system块数量、类型和允许留存的测试文本;
- 对比断点前的消息角色与内容块顺序;
- 最终回到usage确认服务端是否读取缓存。
本地结构摘要只能帮助判断两次测试输入是否按预期构造,不能证明服务端一定命中。TTL、长度门槛、回溯窗口和渠道实现仍可能改变结果。
TTL和并发的两个坑
默认ephemeral缓存有效期为5分钟,命中会刷新有效期。当前还支持1小时TTL,但写入价格不同。混用TTL时,不要把“1小时条目必须先于5分钟条目”当成通用规则;应按目标模型和渠道的当前 API 规则测试两种顺序,记录状态码、错误体和usage。是否采用1小时TTL应根据真实调用间隔、当前文档和发布当天价格计算,不应只追求更高命中率。
并发测试也要控制时序。缓存写入在首个响应开始后才可供读取;多个相同前缀请求同时发出时,后续请求未必已经看到第一条缓存。应让A开始返回,再发送B。
建议记录的测试结果
可以把每次结果保存为一行JSONL,只留下无敏感信息的测试元数据,并按需要记录请求ID、错误类型摘要、价格文档版本和失败重试次数:
{
"case": "baseline-b",
"time": "<带时区时间>",
"model": "<模型ID>",
"cache_mode": "explicit",
"breakpoint_label": "stable-system-end",
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"input_tokens": 0,
"output_tokens": 0,
"request_id": "<服务返回的请求ID>",
"error_type": null,
"retry_count": 0,
"pricing_doc_date": "<价格文档查阅日期>"
}
这些数字只是字段占位,不是测试结果。测试文件不要包含API Key、客户Prompt、内部工具定义、完整请求体或真实调用标识。
验收清单
- 已记录模型、API或渠道、TTL和测试时间
- 已按当前文档核对缓存方式与长度门槛
- 已标注目标渠道、模型和文档查阅日期
- 已明确使用自动缓存还是显式断点
- 断点前的tools、system和messages可复现
- 请求A开始响应后才发送请求B
- 同时保存creation、read和input三个字段
- 单变量测试没有同时改动多个前缀部分
- 本地结构对比只使用无生产数据的测试夹具
- 结论只覆盖本次模型、渠道和请求结构
常见问题
第二次请求有read,是否代表整个Prompt都命中?
不是。read只表示其中一段输入来自缓存。结合断点位置、creation和input,才能判断本次读取及新增范围。
自动缓存和显式断点可以同时使用吗?
可以,但自动断点也占用4个可用槽位之一。还要处理最后一个显式断点与自动缓存的TTL关系;不匹配可能返回400。
本地结构摘要相同,为什么read仍然为0?
摘要只能说明你选择记录的结构相同。目标模型可能未达到最低长度,缓存可能过期,回溯窗口可能未找到旧条目,渠道也可能有不同支持边界。以usage和目标平台文档为准。
缓存命中后,回答内容会完全相同吗?
不会因此保证一致。缓存复用的是输入前缀处理结果,不是固定最终生成;采样设置和生成过程仍会影响输出。
参考资料
- Anthropic Prompt caching(查阅于 2026-08-11):https://platform.claude.com/docs/en/build-with-claude/prompt-caching
- Anthropic Messages API(查阅于 2026-08-11):https://platform.claude.com/docs/en/api/messages
- Anthropic release notes(查阅于 2026-08-11):https://platform.claude.com/docs/en/release-notes/overview
更多推荐
所有评论(0)