提示缓存优化策略
提示缓存优化策略
学习目标
读完本章后,你将能够:
- 理解前缀匹配缓存的工作原理和失效规则
- 正确放置缓存断点(cache_control breakpoints)
- 诊断静默失效(silent invalidation)问题
- 验证缓存命中情况并计算缓存命中率
- 设计缓存感知的提示结构
核心概念
前缀匹配原理
提示缓存基于精确字节前缀匹配:
关键规则:
- 任何字节变化:前缀中任意字节的改变会使该断点及之后的所有缓存失效
- 渲染顺序:
tools→system→messages - 稳定优先:将不变的内容放在前面,变化的内容放在后面
为什么顺序很重要?
假设你的请求包含:系统提示 + 工具定义 + 用户消息。如果每次请求时工具定义都不同(如动态工具列表),但系统提示不变——把系统提示放在工具定义之前,系统提示的缓存仍然有效。
缓存断点放置
自动缓存
通过 cache_control: {type: "ephemeral"} 标记断点:
{ "system": [ {"type": "text", "text": "稳定的系统提示...", "cache_control": {"type": "ephemeral"}} ]}限制:
- 每个请求最多 4 个断点
- 最小可缓存前缀约 1024 tokens(短于此时静默不缓存)
断点放置策略
| 位置 | 建议 | 原因 |
|---|---|---|
| 工具定义末尾 | ✅ 放置 | 工具列表通常稳定 |
| 系统提示末尾 | ✅ 放置 | 系统提示固定不变 |
| 消息列表末尾 | ✅ 放置 | 消息是变化的内容 |
| 动态内容中间 | ❌ 避免 | 每次变化使之后全部失效 |
最佳实践:缓存感知提示结构
[固定] 系统提示核心部分[固定] 工具定义 ↓ cache_control 断点 1[半固定] 对话历史(前 N 轮) ↓ cache_control 断点 2[变化] 最新用户消息静默失效诊断
什么是静默失效?
静默失效指缓存看似正常但实际未被命中,不会报错,只是不会返回缓存数据。
常见静默失效原因
| 原因 | 症状 | 修复方法 |
|---|---|---|
datetime.now() 在系统提示中 | 每次请求时间戳不同 | 将时间戳移到消息部分 |
| 未排序的 JSON/dict | 每次序列化顺序不同 | 使用 sort_keys=True |
| 变化的工具集 | 每次请求工具不同 | 固定工具集或放在断点后 |
| 隐藏字符/编码差异 | 肉眼相同但字节不同 | 确保 UTF-8 编码一致 |
审计清单
每次缓存命中率为零时,检查:
- 系统提示中是否有动态内容? 时间戳、请求 ID、随机数
- 工具列表是否每次不同? 动态过滤工具会破坏缓存
- JSON 序列化是否确定? 使用排序后的 key
- SDK 版本是否一致? 不同 SDK 版本可能改变请求结构
验证缓存命中
检查 cache_read_input_tokens
response = client.messages.create(...)print(response.usage.cache_read_input_tokens)| 值 | 含义 |
|---|---|
0 | 缓存未命中(或首次请求,或前缀太短) |
> 0 | 缓存命中,值为命中的 token 数 |
creation_tokens | 缓存写入的 token 数(首次请求) |
缓存命中率计算
缓存命中率 = cache_read_input_tokens / (cache_read_input_tokens + input_tokens)注意:input_tokens 是本次请求中未命中的部分。
进阶:Task Budgets(Opus 4.7)
Opus 4.7 引入了 Task Budgets,与提示缓存结合使用:
output_config: { task_budget: { type: "tokens", total: 100000 # 整个 Agent 循环的 Token 预算 }}- 模型可以看到运行中的 Token 倒计时,自动调节输出长度
- 不同于
max_tokens(硬性上限,模型不知道) - 最低 20,000 tokens
常见陷阱与对策
前缀太短
问题:标记缓存的前缀少于 1024 tokens,静默不缓存。
对策:确保第一个断点之前至少有 1024 tokens 的稳定内容。
动态内容污染前缀
问题:时间戳、请求 ID、随机种子等动态内容放在系统提示开头。
对策:将系统提示拆分为”固定部分 + 动态变量”,固定部分在前,动态变量放在消息中。
断点浪费
问题:在每次都变化的内容上设置断点。
对策:只对稳定内容设置断点。通常 2-3 个断点足够。
误读缓存指标
问题:只关注 cache_read_input_tokens,忽略 cache_creation_input_tokens。
对策:首次请求的 cache_read 为零是正常的——此时正在写入缓存。检查后续请求是否命中。
参考来源
- Anthropic 官方文档:https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching
- Anthropic Skills 仓库:
skills/claude-api/shared/prompt-caching.md - Anthropic Engineering Blog:https://anthropic.com/engineering