提示词缓存命中率上不去?这7个隐藏杀手正在偷偷烧钱

📅 2026/10/27 ✍️ 小文 📖 约 1 分钟

很多团队接了Anthropic/DeepSeek/Kimi的缓存能力后,账单只降了10%而不是80%。本文拆解提示词缓存命中的真正判定条件,给出7类常见失效原因和可直接套用的排查清单。

几乎所有主流模型的提示词缓存(Prompt Caching)宣传都说“省90%成本”,但真实生产环境里,大多数团队第一次接入后只看到账单下降10%~20%。问题通常不在模型,而在于缓存命中条件被悄悄破坏。

缓存到底是怎么命中的

以主流实现逻辑来说,缓存本质是前缀哈希:系统把你请求的前若干token算成一个哈希,若与已有缓存项完全一致,就直接复用计算结果。这里的关键词是“完全一致的前缀”。

注意三个隐含约束:

  1. 前缀必须逐token相同,中间任何一处变化,从变化点之后的全部失效;
  2. 缓存有TTL(通常5分钟到1小时,不同厂商差异大),过期即穿透;
  3. 缓存的key通常绑定模型版本,换模型/换版本=从零开始。

理解这三点,就能推导出所有失效原因。

七个常见杀手

1. 系统提示里嵌了时间戳

最常见也最愚蠢的一个。为了方便调试,很多人在system prompt末尾加:

当前时间:2026-10-27 14:32:11

只要精确到秒,缓存永远命中不了。正确做法是把时间放到用户消息里,或者只保留日期且允许缓存按天更新。

2. 先插文档再插系统提示

缓存只认前缀。如果你的结构是「用户文档 → 系统提示」,那系统提示每次都排在不断变化的文档之后,等于零缓存。顺序必须是:静态内容在最前,动态内容在最后。

推荐分层:

[系统角色 + 规则]      ← 极少变化,可缓存
[工具定义 / JSON Schema] ← 极少变化,可缓存
[知识库长文档]          ← 按知识库版本变化,可缓存
[对话历史]             ← 每轮变化
[本轮用户输入]          ← 每轮变化

3. 每次请求都重新序列化工具定义

工具列表如果从字典/哈希表里遍历生成,顺序可能不稳定。JSON key 顺序、数组顺序变了,token 序列就变了。必须对工具定义做确定性排序(按name字母序),并固定序列化方式。

4. 用户身份信息被塞进系统提示

“你是XX公司的助理,当前用户是张三(ID 2931)”——这句话把系统提示变成了每用户一格缓存。十万用户就是十万份缓存,命中率断崖式下跌。用户信息应该放在用户消息层,或者作为后置的附加上下文。

5. A/B实验分流在缓存前缀里

实验开关如果写在系统提示里(比如“请使用简洁风格”),两个分支就是两套前缀。这不一定错,但要意识到每增加一个实验维度,缓存空间乘数增长,而且小流量分支的缓存几乎永远重建。更好的做法是把风格指令做成独立的短片段,放在可缓存的公共前缀之后。

6. 并发超过缓存写入速度

缓存写入是异步的。如果你的服务在同一个prompt上瞬间打出50个并发请求,它们很可能全部未命中——因为第一个请求的缓存还没写完。解决方案是做请求合并(singleflight):同一前缀的并发请求排队等第一个完成。

7. TTL与实际调用间隔不匹配

如果你设置的是5分钟TTL,而用户平均8分钟才发一条消息,那么每一轮都是冷启动。这时候应该考虑:要么升级到长TTL(成本更高但便宜于全量重算),要么把低频会话的历史做摘要压缩。

一个可直接用的排查顺序

按这个顺序查,通常10分钟能定位:

  1. 打印实际发送的完整请求体(脱敏),对比两次调用的前1000字符,找出第一个不同的位置;
  2. 统计 cache_read_input_tokens / (cache_read + input_tokens),这是真实命中率;
  3. 按第1条找到的分叉点,反查是哪个变量(时间、用户、实验、工具顺序);
  4. 检查并发峰值时段与平峰时段的命中率差异,判断是否写入竞争;
  5. 查TTL与P95会话间隔的关系。

收益预期要合理

即使全部修好,命中率也很难到100%。对话类应用,稳定态下系统提示+工具+知识库这一大块应该稳定命中,通常占总输入token的50%~80%,这部分是你能拿到的确定性收益。剩下的对话历史会持续增长,可以考虑定期做滚动摘要。

一个健康的基线是:单次请求缓存读token占比达到 60% 以上,成本下降 40%~60%。低于30%就一定是哪里出了问题,值得专门排查。

缓存不是“开了就省”,它是对请求结构设计的一次强制体检。把前缀理干净,收益自然来。

📤 分享到