摘要:Prompt Cache(提示词缓存)是现代大模型降低长会话延迟、削减Token成本的核心能力,本质是复用Transformer推理时生成的KV‑Cache张量,避免重复做Prefill计算。很多开发者只看到缓存带来的成本收益,却不理解严格的精确前缀匹配规则;会话中途切换模型、增删/重载MCP服务器,是Agent开发中最常见、最隐蔽的缓存失效反模式,会带来成本暴涨、延迟飙升,且往往没有明显报错提示。本文拆解底层原理,重点剖析MCP变更、会话内切换模型等典型错误做法,给出可落地的工程规范。
一、基础概念:KV‑Cache 与 Prompt Cache 的区别
很多人混淆两种缓存,先厘清边界:
-
请求内KV‑Cache(会话内部缓存)
单次请求推理过程内部缓存。同一个请求,模型每生成一个输出Token,保存历史Token对应的Key、Value张量;只计算新增Token,避免整段上下文重复注意力计算。生命周期仅限单次API调用,请求结束销毁。
-
跨请求Prompt Cache(提示词缓存)
把KV张量保存在服务端,跨多次API请求复用相同的上下文前缀。Anthropic Claude、OpenAI都实现该能力。
- 第一次请求:完整执行Prefill,生成KV张量,写入缓存(写入价格略高于普通输入Token)
- 后续请求:如果请求开头前缀Token序列字节完全一致,直接读取缓存KV,跳过重复Prefill,缓存读取Token通常仅原价10%,显著降本降延迟。
- 输出Token不参与缓存,输出计费规则不变。
核心真相:Prompt Cache做的是字节/Token精确前缀匹配,不是语义匹配。语义一样、Token序列不一样,直接缓存未命中。多一个空格、句号,JSON工具定义字段顺序调换,都会破坏匹配。
二、缓存命中核心规则:前缀依赖模型专属
2.1 请求前缀层级顺序(决定缓存破坏的传导逻辑)
一个标准Agent请求,从前到后的顺序为,越靠前的层级改动,破坏力越大,该位置之后全部缓存失效。
- 第一层:模型标识(不在文本内,属于缓存Key元数据)
- 第二层:Tools工具定义(MCP工具全部在这里)
- 第三层:System系统提示词、项目固定上下文
- 第四层:对话消息历史 messages(user/assistant/tool_result)
数学根源:Transformer因果掩码(Causal Masking),文本单向依赖。序列中某一个Token发生变化,它后面所有位置的注意力表征全部失效,不能复用旧KV张量。不是选择性失效某一小块,是改动点之后全部作废。
举个直观例子:
缓存前缀:[T工具定义][S系统提示][Msg1][Msg2][Msg3]
新请求: [T工具定义][S系统提示][Msg1][MsgX][Msg3]
Msg2修改为MsgX → 从MsgX位置往后Msg3全部缓存失效,Msg1仍然可以命中。
如果修改最前面的Tools工具定义:Tools一改,System、全部对话历史全部失效。
2.2 缓存Key两大组成部分
- 请求文本Token前缀(字节完全一致)
- 元数据:模型ID、effort/思考等级。这部分不属于Prompt文本,但是参与缓存哈希计算。
重点:不同模型缓存完全隔离,哪怕文本100%一模一样,换模型=全部缓存失效,每个模型拥有独立缓存池。
2.3 TTL生存时间
缓存不是永久保存,Anthropic默认TTL为5分钟,每次命中会刷新TTL;慢Agent循环、长时间人工暂停很容易超时丢失缓存,可开启1小时TTL选项。超时后,即使前缀完全一致,也要重新写入缓存。
三、高频错误做法深度剖析
痛点:很多失效是静默发生,没有报错。用户只会观察到会话突然变慢,账单Token用量异常暴涨,很难定位根因。
错误1:同一个会话中途切换模型(/model命令、opusplan模式自动切换、技能内指定不同模型)
现象
会话已经跑了几十万Token上下文,为了省钱,中途切到廉价小模型处理子任务,切回去之后会话速度明显变慢,输入Token成本飙升。
底层原理
缓存是模型专属隔离。旧模型的KV张量对新模型完全无效,架构、权重、注意力头全部不同,无法复用。哪怕完整对话文本完全复制粘贴给新模型,也需要完整重新Prefill全部上下文,全部缓存清零。
反直觉案例:已经积累100k token Opus缓存,切换Haiku处理简单任务。此时Haiku要完整重算100k上下文,总成本反而高于继续用Opus直接回答。
隐蔽变种:
opusplan:plan阶段使用Opus,执行阶段自动切Sonnet;每次模式切换本质是模型切换,缓存全部重建,很多使用者没有意识到这点。- Skill/技能配置头指定
model=xxx,执行这条技能时临时切换模型,后续轮次缓存直接作废。 - 安全自动回退:触发安全拦截自动降级另一模型,会话漂移到新模型,缓存静默失效。
❌错误范式:
主会话:Opus,跑大量上下文 → /model haiku做子任务 → /model opus切回来继续干活
# 切回Opus后,没有任何缓存,几十万token全部重算
✅正确范式:使用Sub‑Agent子代理隔离子任务
主会话保持固定模型不变,把需要小模型完成的任务,提取精简关键信息交给独立子Agent会话;子Agent拥有自己独立缓存池。完成后返回结果摘要给主会话,主会话的缓存完全不受干扰。
主会话(Opus,缓存持续保温) → 提取精简任务上下文 → 独立子会话(Haiku)执行子任务 → 返回简短结果给主会话
错误2:会话中途新增/删除MCP服务器,随后执行resume / reload插件
这是Agent开发中最容易踩坑的陷阱,大量人误以为"MCP只是外部工具,不会影响缓存"。
现象
任务跑一半,发现缺少工具,修改配置文件新增MCP服务;然后resume恢复会话。恢复之后下一轮请求明显变慢,大量输入Token被消耗。
底层原理
MCP服务器暴露的工具,会被序列化为Tools数组,放在请求最靠前的第二层。Tools数组属于缓存前缀最上游。
- 注意:单纯修改MCP配置,不会立刻破坏当前会话缓存。Claude Code只在会话初始化加载MCP。
- 真正杀手:
resume、reload plugin、重启会话、断开重连MCP。触发工具列表重新组装序列化,Tools数组发生变更。
Tools数组一旦改动,它下游System Prompt、整段对话历史的缓存全部失效。
额外坑点:
- MCP服务意外崩溃、自动重连,会静默触发工具集重建,缓存悄无声息失效,用户完全没有感知。
- 工具JSON序列化顺序不稳定:工具字典转JSON时key顺序不固定,即使工具集合完全一样,字节序列不同,缓存也无法命中。MCP工具列表必须保证确定性序列化顺序。
- 区分延迟加载工具:部分部署环境开启工具搜索延迟加载,MCP工具不会全部塞进请求前缀,变更不会破坏缓存;但默认部署、Azure托管、标记alwaysLoad的MCP会直接加载进前缀,一改动就摧毁缓存。
❌错误范式:
- 会话运行中途,编辑mcp配置添加新MCP服务,执行resume继续原有长会话。
- 反复启停MCP服务器,依赖自动重连。
✅正确实践
- 所有需要用到的MCP服务器,在会话启动之前全部配置完毕,会话运行期间尽量不再增删MCP。
- 如果中途确实必须新增工具:优先用Sub‑Agent子会话运行新MCP,主会话不改动工具集。
- 尽量避免长任务会话运行过程执行
resume、reload‑plugins。 - 工具列表强制确定性排序序列化,禁止依赖字典随机输出JSON。
错误3:在缓存前缀上游(System、Tools)写入动态可变内容
❌典型反模式:
- System Prompt中注入实时时间戳、每次变化的环境变量;每一次请求前缀都变化,缓存永远无法命中。
- 会话中途编辑
CLAUDE.md项目配置文件,再执行resume,系统上下文变更,缓存摧毁。
✅规范:动态变化信息放到消息列表的末尾(动态后缀),不要修改上游System/Tools层。
错误4:过度使用/compact会话压缩
/compact会重新生成、改写历史消息,直接破坏原有前缀缓存。压缩是业务功能,但是会牺牲缓存收益,长会话尽量少频繁执行compact。优先使用rewind回退历史而非整体压缩。
错误5:缓存断点标记位置不合理
cache_control标记断点打在动态变化的位置,静态内容没有被纳入缓存范围,命中率低下。静态内容(System、固定工具、长参考文档)靠前打缓存断点;动态用户交互放在断点之后。
四、工程最佳实践清单
会话架构层面
- 长主会话全程锁定同一个模型 ,跨模型子任务交给独立Sub‑Agent,绝不主会话内切换模型。警惕
opusplan隐式模型切换。 - MCP、插件、工具集合,会话启动前一次性配置完整;运行阶段禁止增删MCP+resume。必须新增工具则新建子Agent会话。
- 静态内容全部放到请求靠前位置(Tools、System);每次会变化的动态内容放在消息末尾(动态后缀)。
- System提示词禁止嵌入时间戳、每次变化变量;动态信息通过user消息传入。
- 工具Schema序列化必须保证确定性顺序,避免字典无序JSON导致伪变更。
- 监控usage返回值:观测
cache_read_input_tokens指标,一旦该值大幅下跌,排查是否发生缓存失效。
排错诊断
出现会话突然变慢、Token用量暴涨,自查清单:
- 是否会话内切换过模型(包括opusplan、技能指定model)?
- 是否中途增删MCP服务器,执行resume/reload?
- 是否修改CLAUDE.md、System配置后重载会话?
- 是否执行/compact压缩对话?
- 是否会话停顿超过默认5分钟TTL导致缓存过期?
特殊场景:Plan模式如何兼顾功能与缓存
直觉错误:进入计划模式就把工具集替换成只读工具,限制模型写文件。但是替换Tools数组会直接摧毁全部缓存。
Claude Code的解决方案值得借鉴:工具集合全程固定不变,使用状态标记工具,而不是动态修改Tools数组。新增EnterPlanMode、ExitPlanMode工具,调用该工具之后,通过追加消息约束行为,工具定义本身保持原样,缓存前缀不受任何改动。
五、总结
Prompt Cache核心铁律:
精确Token前缀匹配;越靠前的组件改动,破坏范围越大;模型属于缓存元数据,跨模型缓存完全隔离;Tools工具定义(含MCP)位于最上游,改动会摧毁下游全部缓存。
很多Agent开发者把缓存当成一个开关,打开就自动降本。现实中,缓存是架构约束,而不是附加开关。会话中途切换模型、动态增删MCP再重载,是两大高频隐形反模式,表面功能正常,底层缓存全部作废,带来不必要成本与延迟。
在设计长会话Agent系统时,必须把缓存命中约束前置到架构设计阶段:主会话模型固定,工具集会话启动时确定,动态状态尽量不改动上游前缀,子任务交给独立隔离的Sub‑Agent处理。