缓存是什么、如何控制
全面详解提示缓存的前缀匹配规则、cache_control断点设置及跨API降低长会话输入成本至约一折的方法
为什么 Agent 离不开缓存
LLM API 是无状态的、重新计算一遍。编程 Agent 一个任务动辄上百轮工具调用,意味着开头那几万 token 的系统提示、工具定义、文件内容要被重复处理上百次。
Prompt Caching(提示缓存) 解决的就是这个问题(KV cache)存下来,下一轮请求如果前缀完全一致,就直接复用,不必重算。
效果(以 Anthropic API 为例):
| 类型 | 价格(相对基础输入价) | 延迟 |
|---|---|---|
| 普通输入 token | 1× | 正常 |
| 缓存写入(首次) | 1.25×(5 分钟 TTL)/ 2×(1 小时 TTL) | 正常 |
| 缓存读取(命中) | 0.1×,一折 | 大幅降低首 token 延迟 |
注意首次写入是 1.25×——比不用缓存还贵,原因和回本账见为什么第一次调用更贵。一个长会话里 90% 以上的输入 token 都可以是缓存命中——这正是 Claude Code、Codex CLI 能以可接受的成本运行长任务的基础。OpenAI API 也有对应机制(automatic prompt caching,前缀 ≥1024 token 自动生效,命中打五折/部分模型更低),原理相同。
缓存的关键性质
缓存只匹配完全一致的前缀。请求内容按固定顺序参与哈希:
tools(工具定义)→ system(系统提示)→ messages(对话历史)
────────────────────────────────────────────▶ 位置越靠前,越应该稳定
规则只有一条,但推论很多:
- 前缀哪怕变了一个字节,从那个位置往后的缓存全部失效,需要全额重建;
tools排在最前——中途增删任何一个工具定义,整个缓存作废(这也是工具爆炸的隐性成本之一);- 对话是“追加式”的就天然缓存友好,只在末尾加新内容,每轮只有增量需要计算。
缓存的控制 断点
Anthropic API 的缓存是显式控制的 cache_control,告诉服务端“缓存到这里为止的所有前缀”:
import anthropic
client = anthropic.Anthropic()
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
system=[
{
"type": "text",
"text": "你是代码审查助手。以下是项目编码规范:\n" + long_style_guide,
"cache_control": {"type": "ephemeral"}, # ← 断点:缓存 tools + 到此为止的 system
}
],
messages=[{"role": "user", "content": "审查这个 PR ..."}],
)
要点:
- 一个请求最多可放 4 个断点,可分别标在工具定义末尾、系统提示末尾、对话历史末尾等层级上;
- 默认 TTL 为 5 分钟,每次命中会刷新;可用
"cache_control": {"type": "ephemeral", "ttl": "1h"}换 1 小时 TTL(写入价更高,适合低频但重复的场景;注意订阅账号的 1h TTL 可用性并不透明,见缓存策略篇的说明); - 有最小长度门槛(多数模型 1024 token,Haiku 类为 2048/4096),太短的前缀不会被缓存;
- 响应里的
usage字段可以验证效果:cache_creation_input_tokens(本轮写入)、cache_read_input_tokens(本轮命中)。接了新系统后先看这两个数,命中为 0 说明前缀被你弄变了。
一个算术题
假设系统提示 + 工具定义 + CLAUDE.md 共 30K token,任务跑了 50 轮:
- 无缓存 × 50 轮 = 150 万 token 全价输入;
- 有缓存 30K × 1.25(写入)+ 后续 49 轮 × 30K × 0.1(命中)≈ 18.4 万 token 等效费用,约为前者的 12%,且每轮首 token 延迟显著下降。
上下文越大(尤其 1M 模式),这笔账越悬殊——1M 前缀重建一次的成本,足够缓存命中跑几十轮。
缓存不是万能的
- 缓存只降输入成本,输出 token 照常计费;
- TTL 过期(默认 5 分钟无访问)后需要重新写入——长时间挂起的会话回来第一轮总是慢一些、贵一些;
- 缓存按账号隔离,不同 API key/组织之间不共享;
- 它不能替代上下文管理,缓存救不了。