前面十二篇讲的都是"怎么让 Agent 干得好",这篇讲"怎么让 Agent 干得起"。Agent 一次任务几十轮模型调用,每轮都携带全量上下文------输入 token 成本的大头不在新内容,在重复发送的前缀。Prompt Cache 是唯一的解,而缓存命中率不是一个可以事后调优的参数,是系统设计的产物。这篇从业界最佳实践讲到我们认可的全局命中率工程。
命中率是地基,不是优化
先建立直觉:一次 10 万 token 输入的调用,不开缓存每轮全价;开了缓存,命中部分价格打一折(Anthropic)甚至五十分之一(DeepSeek)。一个 50 轮的 Agent 会话,90% 命中率和 50% 命中率的成本差是数量级的。
Anthropic 在《Building Claude Code》复盘里把话说到了绝处:缓存命中率是 SEV 级基础设施指标 ------掉命中按线上事故走完整响应流程。原因不只是省钱:命中率高,首 token 延迟就低,平台才能给用户更宽松的额度。他们的原话是"没有缓存就没有 Claude Code"。对任何长会话 Agent,这句话都成立:缓存命中率不是锦上添花,是系统能跑起来的前提。
业界调研:前缀匹配决定一切
Prompt Cache 的原理一句话:两次请求的 token 序列从头部开始存在相同前缀,服务端复用这段前缀的 KV 计算结果,跳过重算。先立一个概念边界:它复用的是输入前缀的计算,不是答案------和应用层的响应缓存/语义缓存是两件事,命中了照样重新生成,只是省掉前缀的重复 prefill。计费上各家不同:Anthropic 显式标 cache_control 断点,写入 1.25 倍、读取 0.1 倍、默认 TTL 5 分钟(可付费延长到 1 小时);OpenAI/DeepSeek 是隐式哈希自动匹配,无写入费,命中直接打折;Gemini 显式/隐式并行。共同点是都有 1024 token 量级的起步门槛------短 prompt 没资格谈缓存。
由此推出 Anthropic 的七条军规,条条都是学费:
- 分层排序:静态 system+tools(全局共享)→ 项目文档(项目共享)→ 会话上下文(会话共享)→ 消息(逐轮增长)。越不变越靠前------前缀里任何位置变化,其后全部失效;
- 指令当基岩,消息当流水:时间戳、模式切换绝不写进 system prompt,塞下一条对话消息里;
- 会话中途不换模型:缓存跟模型绑定,换模型等于全部重建,比直接回答还贵;小模型需求走子任务独立上下文,不污染主缓存链;
- 工具集会话中途绝不动:规划模式不是移除执行工具,而是加"进入/退出规划"两个工具;几十个外部工具用延迟加载------前缀只放轻量占位符,用到才拉完整定义;
- 压缩必须缓存安全分叉:压缩请求与主对话完全同参(system/tools/历史),只在末尾追加压缩指令------复用主对话积攒的全部缓存,压缩本身几乎免费(这条和我们的第四篇异步整理遥相呼应);
- 像监控在线率一样监控命中率:Pi Agent 把 CH% 放在终端页脚常驻可见------观测即纪律;
- 缓存按账号隔离:账号池中转是命中率杀手。
经济学上也有一笔明白账:设稳定前缀 P、复用 N 次,缓存成本 P×写 + N×P×读,不缓存 (N+1)×P×全价------复用两次以上手动缓存就稳赚。正确指标不是"命中次数",是缓存读取 token 占可缓存输入的比例。DeepSeek Harness 的源码分析则从反面证明:请求组装管道的架构设计,可以把命中率逼近理论上限------命中率是设计出来的,不是调出来的。
最佳方案:全局缓存命中率工程四层
综合业界与我们的生产实践,Agent 系统的缓存命中率要分四层治:

① 物理层:字节确定性
前缀匹配的"前缀"是字节级的。一个键序漂移、一个随机 ID、一个时间戳,缓存从该点向后全断。所以出站报文的字节必须是确定性 的:schema 树构造期定型并深冻结、拒收无序容器(我们启动即失败);请求体多键对象走保序构造;所有来自集合的顺序(工具目录、技能列表、外部工具发现)收敛成保序或按名排序。最阴险的坑是 Map.of 这类容器------迭代序由 JVM 启动随机盐决定,进程内稳定、跨实例漂移,单测永远绿、生产跨实例断缓存。字节确定性必须有契约测试锁死,否则它会在任何一次重构里悄悄破功。
② 架构层:动静分层
把请求内容按变化频率分层摆放:全局静态(tools+system)在最前,会话级上下文居中,逐轮增长的消息在最后。动态信息(当前时间、模式状态、临时提醒)只走消息流,绝不污染基岩层。配套纪律:会话内不换模型、不增删工具、主对话与旁路调用(压缩、蒸馏、子任务)共享同一套前缀参数------旁路请求同参化,才能白嫖主对话积攒的缓存。
③ 生命周期层:看不见的断点
缓存断裂最危险的形式是离开模型视野的断点 。我们的生产事故:消息落库用了 MySQL JSON 列,写入时数据库把所有嵌套对象的键按内部规则重排------transcript 重载后,tool_call 参数的键序漂移,run 边界处缓存从首条受影响消息断裂,多 run 会话每个边界全价重缴一次。实测一次 run 边界命中 17536/23830,断点精确定位在"重载后首条 tool_call 的 arguments",列类型改回 LONGTEXT 字节透传才修复。教训推广成一条纪律:凡是参与 re-render 的状态,存储必须字节透传 ------JSON 列、ORM 序列化、任何"等价但不同字节"的变换都是缓存杀手。压缩则是另一场考验:折叠必然改写前缀,断链不可避免,所以纪律是两条------压缩请求本身与主对话同参(分叉共享缓存,第四篇的展开),折痕之后的内容逐字保留,让新缓存链立即稳定并从此不再断。压缩是缓存链上最贵的一次外科手术,开刀可以,缝合必须整齐。
④ 观测层:命中率是一等指标
观测的前提是先把五家供应商的用量方言翻译成一本账:OpenAI 的 cached_tokens 藏在 details 里、DeepSeek 平铺 prompt_cache_hit_tokens、Kimi 又一个字段、Anthropic 拆 cache_read/cache_creation、Gemini 叫 cachedContentTokenCount------归一化成 cacheRead/cacheCreation 两个 canonical 字段落库,才谈得上度量。然后:命中率按 run 边界分段看(run 内稳态高是正常的,run 边界断才是病)、按供应商分桶看、设告警当事故处理。诚实交代一个我们自己的缺口:Anthropic 链路的 cache_control 断点至今没打------用量字段都解析了,显式缓存没开,这条链路的命中率红利还没吃到。观测的价值就在于这种缺口无处遁形。
收尾
四层归一句:
缓存命中率的敌人从来不是流量,是你自己写出去的字节。物理层讲确定,架构层讲动静,生命周期讲透传,观测层讲账本------四层都是设计,没有一层是调参。
系列预告:下一篇《脑手分离》------给 Agent 一台电脑,再给它戴上镣铐:前面十三篇讲的都是"脑",接下来讲"手"往哪放、怎么管。
智创者社区:apmen.cn