AI 工程实践 · 成本优化

Coding Agent 开发:
如何把缓存命中率做到 90%+

在 Coding Agent 里,缓存不是优化项,而是架构地基

做 Coding Agent 的人,迟早会盯上同一个指标:缓存命中率。

原因很直接。Coding Agent 是典型的「长上下文 + 多轮迭代」场景——读文件、跑命令、改代码、再读再改,每一轮都要把完整历史重新发给模型。历史越长,重复付费越多。

业界对这件事的重视程度,可能超出你的想象:Manus 团队曾公开表示,如果只能选一个指标,KV 缓存命中率是生产级 AI Agent 最重要的单一指标;Anthropic 的 Claude Code 团队更是对命中率设置了告警——一旦命中率过低就触发严重事件(SEV),和对待系统宕机一个级别。

更关键的是,在 Coding Agent 里,缓存不是「优化项」,而是「架构地基」。你的提示词结构、工具设计、会话管理方式,几乎都要围着它设计。

这篇文章就把「怎么把命中率做上去」讲清楚。

规则

一、先记住一条规则:缓存按「前缀」逐字匹配

所有优化都从这一条衍生而来:

缓存匹配是逐字的、从前向后的。请求开头只要有任何一个 token 变了,从变化点往后的全部内容都得重算。

一个典型的 Agent 请求长这样:

[tools 工具定义] → [system 提示词] → [项目规则 CLAUDE.md]
  → [对话历史] → [工具返回结果] → [本轮用户消息]

只有这段序列的前面部分与上次请求逐字一致时,匹配上的那一段才能按缓存价计费。

三家主流厂商的实现方式略有不同,先看清差异:

维度ClaudeOpenAIDeepSeek
控制方式显式:用 cache_control 指定断点自动:前缀够长(1024 token 起)自动尝试默认开启,无需改代码
命中条件断点之前内容逐字一致精确前缀匹配需先完整持久化前缀单元
缓存读取价约 0.1x(写入 5m 为 1.25x、1h 为 2x)提供折扣读价价差最大,可达数十倍
有效期默认 5 分钟,可选 1 小时通常 5–10 分钟不活跃失效数小时至数天自动清理

一句话:Claude 最可控但工程要求最高,OpenAI 最省心但控制感弱,DeepSeek 价差最猛但依赖链路透明。

病因

二、为什么 Coding Agent 天生不容易命中

如果你直连 API,写个固定 prompt 很容易命中。但 Coding Agent 没那么简单——它已经不是一个 agent,而是一整套调度系统:要管工具、压缩上下文、切模式、拉子任务。

麻烦就出在这里:请求前缀里塞满了工具定义、项目规则、权限模式、MCP schema、git 状态、历史消息……该长期稳定的和每轮都变的,混在了一起。

四个反复出问题的环节:

此外,还有一个容易被忽略的坑:统计口径。很多框架统一成简单格式,只留一个总输入 token,cache_creation 与 cache_read 全被抹平,你根本看不到缓存到底有没有生效。

技巧

三、六条实操技巧(来自 Claude Code 团队的经验)

技巧一:静态前置,动态后置

这是最基础、也最重要的一条。Claude Code 的请求是分层的:

层内容缓存粒度
最外层静态 system 提示词 + 工具定义全局缓存(跨会话共享)
第二层项目规则(CLAUDE.md)项目内缓存
第三层会话上下文会话内缓存
末尾对话消息不缓存(每轮都变)

越稳定的越往前放,越易变的越往后放。cache_control 断点要打在稳定内容之后,这样后面的用户问题随便变,前面那段前缀仍能复用。

技巧二:动态信息走「消息」,别改系统提示词

「现在是星期三」「用户刚改了某个文件」这类信息,直觉上是直接改 system 提示词。但这么做代价极大——整段缓存直接失效。

正确做法:把动态信息作为一条消息追加在对话末尾。Claude Code 的做法是在用户消息里加一个标签注入这类信息,前缀完全没动,缓存自然保住。

由此得出一条黄金规范:长期规则写进项目文件(CLAUDE.md),临时变化全部进对话消息。

技巧三:会话中途不要切模型

理由前面已经说了——缓存按模型隔离。真想用不同模型,正确姿势是用子代理(subagent)做侧活:主线模型不动、长前缀不动,子代理独立跑自己的缓存。

技巧四:工具定义延迟加载

工具一多,光工具定义就吃掉大量 token,而且经常变动。Claude Code 的解法是「延迟加载」:默认只发轻量的工具存根(工具名 + defer_loading: true 标记),模型真正需要时再通过一个「工具搜索」工具去发现完整定义。

这样即使你装了个新插件,也不会把整个前缀打翻。

技巧五:让子代理共享父级缓存前缀

多代理不是简单「多发几份请求」。让子代理复用主会话已建立的稳定前缀,能让「派 5 个子 agent 并行干活、账单却几乎只算一份」成为可能——这也是 Claude Code 架构里最值得学的设计之一。

技巧六:上下文压缩时,复用同一前缀

这一条特别精妙。当历史太长需要压缩时,天真做法是另起一个 prompt、把历史拍平发过去——这次调用必然全部 miss。

正确做法是:复刻会话原本的「系统提示词 + 工具定义 + 历史」前缀,只在尾部追加一句「请把以上内容压缩成摘要」。于是这次摘要调用的前缀,正是上一轮刚被缓存的那一段,压缩本身也命中了缓存。

清单

四、一张风险分级清单

把日常操作按「是否摧毁缓存」分级,执行起来最直观:

级别操作影响
🔴 高危中途换模型、增删工具 / 重载 MCP、改项目规则文件、装新 Skill、闲置超 TTL缓存几乎必废
🟡 中危改 thinking 开关 / 推理预算、重载插件视实现而定
🟢 低危正常对话(只在末尾追加消息)、侧线探索走 subagent、反复读同一文件基本安全(文件内容也能被复用)
监控

五、怎么监控:盯「写读比例」,别只看总量

Claude 场景下最该盯的是两个字段的比例:

判断逻辑很简单:

单看总 token 极易误判:输入看起来很多,但如果大部分是 cache read,实际非常便宜。

接入层

六、别忘了接入层:中转站会吃掉你的缓存红利

很多人把前缀优化做得很漂亮,最后却在接入层翻了车。

原因在于,经过中转站后,缓存信息可能直接消失——不少 OpenAI 兼容接口会把 usage 统一成简单格式,只留一个总 input token。而更麻烦的是三层「猫腻」:

  1. 吃差价:上游已按缓存读取的低价结算,中转站却按普通输入卖给你
  2. 打散路由:在多个 key、账号、区域之间轮询,同一会话的请求被分散,模型名一致不代表缓存位置一致
  3. 改写请求:插入自己的 system 提示词、修改 tool schema、重排 messages——你的前缀早被改了,而你在日志里根本看不到最终请求

影响有多大?算笔账就清楚了。设普通输入价为 1、缓存读取为 0.1:

方案计算实际单位成本
3 折、命中率 50%(0.5×1 + 0.5×0.1) × 0.30.165
6 折、命中率 95%(0.05×1 + 0.95×0.1) × 0.60.087

结论很反直觉:折扣只作用在明面单价上,而命中率改变的是计费基数。长上下文任务里,命中率差一点,价格排序就可能反过来。

所以选接入方式时,至少看三件事:

  1. usage 字段是否透传(最好保留供应商原始字段,能看到缓存读写明细)
  2. 缓存输入按什么价计费
  3. 同一会话是否有路由粘性

这也正是向量云在解决的问题:通过火山方舟官方 API 与独享推理接入点,让请求路由固定、缓存空间隔离——缓存不被其他用户挤占,前缀才能稳定命中,独享场景下命中率可达 70%~90%,而不是公共资源池里随波逐流的 20%~40%。

获取 API Key:https://ark.tokenrize.cn/ · 注册即享专属 Token 折扣

一句话总结:前缀稳定性管住前半段,独享接入点守住后半段,命中率才是省出来的。

写在最后

七、写在最后:四条保命口诀

把全文压成四句话,可以直接贴到团队规范里:

  1. 静态在前、动态在后——时间戳、git 状态塞进用户消息,绝不写进 system 正文
  2. 要换模型就用 subagent 做侧活——主线模型不变、长前缀不动
  3. 临时指令写进当前对话——别去改项目规则这种长期记忆
  4. 压缩也要复用前缀——别另起 prompt 把历史拍平

最后回到那句话:在 Coding Agent 里,缓存不是优化项,而是架构地基。你越早把它当成设计的第一性原理,后面要还的技术债就越少。

关于向量云
向量云(南京)科技有限公司

面向大模型应用落地的技术服务型企业,依托字节跳动火山方舟能力,为合作方提供低折扣模型 Token、可独立部署的运营支撑平台及全链路运营支持。

火山方舟官方授权 官方 API Key 独享接入点 缓存隔离降本 即用即扣 · 不设账期

常见问题(FAQ)

火山方舟官方 API 与官方 Key 直连 · 向量云 · 按量计费享 Token 折扣

Q1. 为什么 Coding Agent 的缓存命中率总是不稳定?

A. 因为 Agent 的请求前缀里混入了大量动态内容:每轮变化的 system prompt(时间、cwd、git 状态、权限模式)、易变的 tool schema,加上中途切模型、compaction 与 subagent 改写前缀,都会让原本稳定的前缀失效。

Q2. 会话中途换模型为什么会更贵?

A. 缓存是按模型隔离的,切换后缓存完全不通用。原模型上攒的稳定缓存需要全额重建,重建的输入成本通常远大于模型单价差。正确做法是用 subagent 做侧活,主线模型保持不变。

Q3. 上下文压缩时怎么保住缓存?

A. 不要另起 prompt 把历史拍平。应复刻会话原本的「系统提示词 + 工具定义 + 历史」前缀,只在尾部追加「请压缩成摘要」的指令,这样压缩调用本身也能命中上一轮建立的前缀缓存。

Q4. 怎么判断缓存是否真的生效了?

A. 看 cache_creation_input_tokens(写入)与 cache_read_input_tokens(读取)的比例:写入高、读取低说明前缀不稳;读取高而普通输入低说明缓存生效。只盯总 token 容易误判。