缓存优化模式——让缓存命中率飙升

Table of Contents
你有没有发现,Claude Code用久了,有时候响应很快,有时候却慢得像在思考人生?这往往跟"缓存命中率"有关——缓存命中了,秒回;没命中,得重新计算。
今天咱们聊聊Claude Code的七个"缓存优化秘籍",看它是怎么把缓存命中率玩到极致的。
缓存优化的核心思路
缓存优化的本质是:减少变化,保持稳定。
想象一下,你去饭店点菜:
- 缓存命中:服务员记得你爱吃什么,直接下单
- 缓存未命中:每次都要重新问你喜欢什么
API缓存也一样。每次请求都要发一大段"系统提示词+工具说明"给服务器。如果这段内容变了,服务器就得重新处理,费时费钱。
所以优化的方向是:让这段内容尽量不变,或者变化的影响最小。
秘籍一:日期记忆化——跨午夜不失效
有个问题困扰了很多AI应用:午夜跨天。
假设你在23:59问了AI一个问题,系统提示词里写着"今天是2026-04-01"。然后在00:01又问了一个,日期变成了"2026-04-02"。
就这一个字符的变化,导致整个系统提示词的缓存失效——约11,000 tokens要重新计算。
Claude Code的解决方案:记忆化日期。
getSessionStartDate = 记住第一次获取的日期,以后永远用这个
这样即使过了午夜,系统提示词里的日期还是"昨天",缓存不会失效。
那AI怎么知道"今天"变了?Claude Code会在消息尾部追加新日期。尾部变化不影响前缀缓存。
这就像你跟服务员说:“我每次都点一样的菜”,只是今天加了个甜点——主菜不用重新介绍。
秘籍二:月度粒度——从每天变到每月变
系统提示词解决了,但工具提示词也需要时间信息。如果用完整日期(2026-04-01),每天都会变。
解决方案:只用月份(April 2026)。
变化频率从每天降到每月,30倍的稳定性提升。
这就像你办了一张月卡,不用每天买票。
两种时间精度的分工:
- 系统提示词:精确到日,但记忆化(每会话一次)
- 工具提示词:精确到月(每月一次)
秘籍三:Agent列表从工具描述移到消息附件
这是影响最大的一个优化——消除了**10.2%**的全量缓存重建成本。
问题在哪?AgentTool的描述里列出了所有可用Agent(包括MCP服务器提供的)。但这个列表是动态的:
- MCP服务器异步连接
- 插件刷新
- 权限模式变化
每次列表变化,AgentTool的Schema就变了,导致整个工具Schema缓存失效。
解决方案:把动态列表从工具描述中移出,改为消息附件。
工具描述变成静态的,只描述功能;可用Agent列表作为附件追加在消息尾部。
这就像:
- 以前:菜单上印着"今日特供取决于厨师心情"
- 现在:菜单固定,服务员口头告诉你今天的特供
菜单不变,缓存稳定;特供信息放尾部,不影响前缀。
秘籍四:技能列表预算——1%上限控制
SkillTool也面临类似问题:技能列表动态变化。
解决方案:严格控制预算,最多占上下文窗口的1%。
对于200K窗口,就是8,000字符。如果列表超长,就截断。
这样做有两个好处:
- 限制工具描述大小,减少字节匹配的难度
- 预算满了新技能就不加,列表不变,缓存不破
这就像你的衣柜:满了就不买新衣服,保持整洁。
秘籍五:$TMPDIR占位符——消除用户差异
BashTool的提示词里需要告诉AI临时目录在哪。通常是/private/tmp/claude-{UID}/,不同用户UID不同。
这意味着:不同用户看到不同的提示词,无法共享全局缓存。
解决方案:用$TMPDIR占位符替代具体路径。
Claude Code的沙箱环境会设置$TMPDIR变量,AI用$TMPDIR引用临时目录效果一样。
但所有用户看到的提示词都相同——都是$TMPDIR——可以共享缓存。
这就像:
- 以前:菜单上印着"本店地址:北京市朝阳区xxx号"(每个分店印不同的地址)
- 现在:菜单上印着"本店地址:见门店招牌"(所有分店菜单一样)
秘籍六:条件段落省略——宁可不说,不要说了又删
系统提示词中有些段落是条件性的:某个功能开启时加一段说明。
问题是:如果这个条件在会话中途变化(比如远程配置更新),段落出现/消失会改变提示词,缓存中断。
解决方案:宁可不说,不要说了又删。
具体做法:
- 如果不重要,干脆总包含或总不包含
- 如果必须条件包含,放在动态区域(不参与全局缓存)
- 用附件机制替代内联条件
这就像你承诺朋友:“能来就来,不确定就别答应”——比"答应后又反悔"好。
秘籍七:工具Schema缓存——会话级锁定
工具Schema的生成涉及多个运行时决策:
- GrowthBook功能开关
- 工具的动态描述
- MCP工具的Schema
如果每次请求都重新生成,GrowthBook的任何变化都会导致Schema变化。
解决方案:会话级缓存。
TOOL_SCHEMA_CACHE = new Map()
第一次请求:生成Schema,存入缓存
后续请求:直接用缓存,不再重新生成
这样即使GrowthBook中途刷新,Schema也不会变。
这就像你第一次去餐厅,仔细看菜单;之后再去,直接点熟悉的菜。
有个细节:StructuredOutput工具的名称固定,但不同调用传入不同的Schema。所以缓存键不能只用名称,还要包含Schema内容。
曾经有个bug:只用名称缓存,导致不同工作流用了错误的Schema,错误率从5.4%飙升到51%。
七个秘籍的共同本质
回顾这些优化,可以提炼出一个决策流程:
发现动态内容
↓
必须在前缀中?
↓否→ 移到消息尾部/附件
↓是
能消除差异?
↓是→ 使用占位符/标准化
↓否
能降低变化频率?
↓是→ 记忆化/降低精度/会话缓存
↓否
能限制变化幅度?
↓是→ 预算控制/条件省略
↓否
标记为动态区域(不参与全局缓存)
四个核心原则:
1. 把动态内容推向请求尾部
越靠前的内容,变化破坏性越大。所以:
- 日期记忆化锁定系统提示词
- Agent列表移到消息附件
- 条件段落确保前缀不抖动
2. 降低变化频率
如果必须在前缀中,降低变化频率:
- 日期从每天变到每月
- 技能列表用预算控制
- Schema缓存从每请求变到每会话
3. 消除用户维度差异
全局缓存要求所有用户看到相同前缀:
$TMPDIR占位符消除UID差异- 日期记忆化消除时区差异
4. 先测量,再优化
每个优化都来自数据:
- 10.2%成本来自Agent列表变化
- 77%工具变化是单个Schema变化
- GrowthBook翻转导致中断
没有监控,这些优化不会被发现。
实战:如何提升自己的缓存命中率
理解这些模式,你可以这样优化自己的AI应用:
1. 审计系统提示词
找出其中的动态内容:
- 日期/时间 → 记忆化或降低精度
- 用户名/路径 → 用占位符
- 配置值 → 移到动态区域或附件
2. 锁定工具Schema
工具定义在会话内应该保持不变。如果必须动态改变:
- 考虑用消息附件替代
- 使用会话级缓存
- 延迟加载MCP工具
3. 监控cache_read_input_tokens
这是判断缓存是否工作的唯一指标:
- 如果持续下降,说明有缓存中断
- 结合日志找出变化源
- 应用相应的优化模式
4. 理解前缀顺序
cache_control断点之前的内容变化会废弃该断点的缓存。构建请求时:
- 最稳定的内容放最前面
- 动态内容放后面或尾部
常见陷阱
| 陷阱 | 原因 | 解决方案 |
|---|---|---|
| 系统提示词嵌入时间戳 | 每次请求都变 | 记忆化 |
| 动态工具列表 | MCP连接变化 | 附件机制 |
| 用户特定路径 | 不同用户不同 | 环境变量占位符 |
| Feature flag影响Schema | 远程配置刷新 | 会话级缓存 |
| 频繁切换模型 | 模型是缓存键的一部分 | 固定模型选择 |
总结
缓存优化是Claude Code降本增效的关键手段:
- 七个秘籍:日期记忆化、月度粒度、Agent列表附件化、技能预算、$TMPDIR占位符、条件省略、Schema缓存
- 核心思路:减少变化,保持稳定
- 四个原则:推向后缀、降低频率、消除差异、数据驱动
- 实战要点:审计提示词、锁定Schema、监控指标、理解前缀
这就像经营一家餐厅:
- 菜单稳定,客人点菜快(缓存命中)
- 特供信息口头告知,不影响菜单(附件机制)
- 每日食材新鲜,但菜单不换(记忆化)
- 用数据优化,什么菜受欢迎(测量驱动)
理解这些模式,你就能:
- 诊断自己的AI应用缓存问题
- 应用相应的优化策略
- 显著降低API成本
下篇咱们聊聊权限系统——给AI装上"安全刹车"。
