以 Claude 如何实现 92% 缓存命中率为例
每当 AI Agent 执行一步操作,都会将整段对话历史重新发送给 LLM。
这其中包括系统指令、工具定义,以及三轮对话之前就已处理过的项目上下文。所有内容每一轮都会被重新读取、重新处理,并按全价重新计费。

对于长时间运行的 Agent 工作流而言,这些冗余计算往往是整个 AI 基础设施中开销最大的一项。
假设一个 20,000 token 的系统提示在 50 轮对话中反复发送,就意味着 100 万 token 的冗余计算以全价被收费,却没有产生任何新价值。而这个成本会随着每位用户、每个会话不断累积。
解决方案就是 Prompt 缓存。但要用好它,你需要理解底层到底发生了什么。
静态上下文 vs. 动态上下文
在优化 Prompt 之前,你需要搞清楚什么会变、什么不会变。
每个 Agent 请求都由两个本质不同的部分构成:

- 静态前缀——在各轮对话中保持完全一致:系统指令、工具定义、项目上下文和行为准则。
- 动态后缀——随每一轮对话不断增长:用户消息、助手回复、工具输出和终端观测。
这种分离正是 Prompt 缓存得以实现的基础。基础设施会保存静态前缀的数学状态,这样后续共享完全相同前缀的请求就能跳过计算,直接从内存读取。
一旦你内化了这一点,本文中所有的架构决策都会变得显而易见。
KV Cache 是如何工作的?
要理解缓存为什么如此高效,你需要了解 Transformer 在处理 Prompt 时究竟在做什么。
每次 LLM 推理请求都包含两个阶段:

- 预填充(Prefill)阶段处理整个输入 Prompt。它对上下文中的所有 token 进行密集矩阵乘法运算,以构建模型的内部表征。这一阶段受计算能力限制,开销很大。
- 解码(Decode)阶段逐个生成 token。每个新 token 被添加到序列中,模型预测下一个 token。这一阶段受内存带宽限制,因为它主要在读取历史状态,而非进行大量计算。
在预填充阶段,Transformer 为每个 token 计算三个向量:Query、Key 和 Value。注意力机制利用这些向量来确定每个 token 与其他所有 token 之间的关系。任意给定 token 的 Key 和 Value 向量只取决于它之前的 token,而且一旦计算完成就不会再改变。
如果没有缓存,这些 Key 和 Value 张量在每次请求后就会被丢弃,下一次请求必须从头重新计算。对于一个 20,000 token 的前缀来说,这意味着 20,000 个 token 的注意力计算完全是不必要的重复。
KV Cache 通过在推理服务器上持久化这些张量来解决这个问题,使用 token 序列的加密哈希作为索引。当新请求带着相同的前缀到来时,哈希匹配,张量从内存中加载,那些 token 的预填充计算就被完全跳过了。
这将每个生成 token 的计算复杂度从 O(n²) 降低到了 O(n)。对于一个在 50 轮对话中反复出现的 20,000 token 前缀来说,这是一个巨大的缩减。
经济账
定价结构决定了这个架构决策的重要性。
缓存读取的成本是基础输入价格的 0.1 倍——即每个命中缓存的 token 可享受 90% 的折扣。缓存写入的成本是 1.25 倍——存储 KV 张量需要支付 25% 的额外费用。扩展至一小时的缓存成本是 2.0 倍。
以下是 Anthropic 各 Claude 模型的具体数据:

这笔账只有在缓存命中率保持高位时才划算。最好的生产环境案例就是 Claude Code。
Claude Code 的一次 30 分钟编码会话
Claude Code 的整个设计都围绕一个目标:保持缓存热度。
以下是一次真实的 30 分钟编码会话从计费角度的全貌。
第 0 分钟:Claude Code 加载系统提示、工具定义和项目的 CLAUDE.md 文件。这部分负载超过 20,000 个 token,由于每个 token 都是全新的,这是整个会话中最昂贵的时刻。但你只需要为此付费一次。
第 1 至 5 分钟:你开始下达指令,Claude Code 调度其 Explore 子 Agent 来浏览代码库、打开文件和运行 grep 命令。这些内容都被追加到动态后缀中。但那 20,000 token 的静态前缀现在以 $0.30/MTok 的价格从缓存中读取,而非 $3.00/MTok。
第 6 至 15 分钟:Plan 子 Agent 接收的是经过摘要的简报,而非原始结果——因为传递原始输出会不必要地膨胀动态后缀。它生成实施方案,你确认后,Claude Code 开始执行改动。每一轮对话都从缓存中读取静态前缀,命中率攀升至 90% 以上,每次访问还会重置 TTL 以保持缓存热度。
第 16 至 25 分钟:你请求修改,这意味着更多的工具调用、更多的终端输出,以及更多积累在动态后缀中的上下文。到现在为止,这个会话已经处理了数十万 token,但每一轮对话都在从缓存中读取那 20,000 token 的基础层。
第 28 分钟:你在终端中运行 /cost。如果没有缓存,200 万 token 按 Sonnet 4.5 费率计算将花费 $6.00。在缓存以 92% 效率运行的情况下,184 万 token 是缓存读取,总成本降至 $1.15。单个任务就实现了 81% 的成本缩减。

这就是热缓存的样子——你只需为静态基础层付费一次,此后就可以免费读取。被收费的只有不断增长的动态尾部。
基于哈希的缓存的脆弱性
关于 Prompt 缓存,最反直觉的一点是:
"1 + 2 = 3" 可以命中缓存,但 "2 + 1" 就是一次缓存未命中。
基础设施从头开始对完整的 token 序列进行哈希。如果序列中的任何内容发生变化——哪怕只是两个元素的顺序互换——哈希值就会改变,整个前缀将以全价重新计算。

这不是一个无关紧要的实现细节。它是 Claude Code 每一项工程决策所围绕的核心约束。
以下是一些在生产环境中导致缓存失效的真实案例:
- 在系统提示中注入时间戳,导致每次请求都产生唯一的哈希值。
- JSON 序列化器在不同请求之间对 tool schema 的键进行了不同的排序,使前缀失效。
- 一个 AgentTool 在会话进行中更新了参数,导致整个 20,000 token 的缓存被清除。
由此得出三条规则:
- 不要在会话进行中修改工具。工具定义是缓存前缀的一部分,添加或删除工具会使其后的所有内容失效。
- 不要在会话中途切换模型。缓存是模型特定的,这意味着切换到更便宜的模型需要从头重建整个缓存。
- 不要通过修改前缀来更新状态。Claude Code 不会编辑系统提示,而是在下一条用户消息中追加提醒标签,从而保持前缀不变。
在你自己的 Agent 中应用
无论你是使用 Claude Code 还是从头构建自己的 Agent,同样的规则都适用。
按以下顺序组织你的 Prompt:
- 系统指令和行为规则放在最前面。会话中途不要修改它们。
- 提前加载所有工具定义。不要添加或删除。
- 检索到的上下文和参考文档紧随其后。在整个会话期间保持稳定。
- 对话历史和工具输出放在最后。这就是你的动态后缀。

在 Anthropic API 上启用自动缓存后,缓存断点会随着对话的增长自动前移。如果不开启,你需要手动跟踪 token 边界——而一个错误的边界就意味着完全错过缓存。
当你接近上下文限制需要进行上下文压缩时,请使用缓存安全的分叉策略。保留相同的系统提示、工具和对话历史,然后将压缩指令作为新消息追加。缓存的前缀得以复用,唯一按全价计费的新 token 只有压缩指令本身。

要验证缓存是否正常工作,请监控每个 API 响应中的三个字段:
- cache_creation_input_tokens 是写入缓存的 token 数。
- cache_read_input_tokens 是从缓存中读取的 token 数。
- input_tokens 是未经缓存处理的 token 数。
你的缓存效率 = cache_read_input_tokens / (cache_read_input_tokens + cache_creation_input_tokens)。像监控可用性一样监控这个指标。
核心要点
Prompt 缓存不是一个简单开启的功能,而是一种需要围绕它进行设计的架构纪律。
核心思路很简单:组织你的 Prompt,让静态内容位于顶部,动态内容在底部增长。基础设施会对前缀进行哈希、存储 KV 张量,并在每次后续读取时给你 90% 的折扣。
但纪律体现在细节中。不要在系统提示中注入时间戳,不要打乱工具定义的顺序,不要在会话中途切换模型,也不要修改缓存断点上游的任何内容。
Claude Code 展示了大规模应用的效果——92% 的缓存命中率和 81% 的成本缩减。如果你正在构建 Agent 却没有围绕 Prompt 缓存进行设计,那你正在浪费大部分的利润空间。
以上就是全部内容!
如果你觉得这篇教程有用:
欢迎关注 → @_avichawla
我每天分享关于 DS、ML、LLM 和 RAG 的教程与洞察。