把 prompt 前缀写稳:缓存命中率低一个点,账单就厚一层

2026-09-19 成本优化prompt 工程缓存Claude Code

上周有个读者问:同样的业务量,隔壁那个人用同一个模型,账单只有他的三分之一。查下来不是模型选得不对,是 prompt 拼错了顺序。

这篇讲的事情只有一件:怎么让每次请求的前缀尽量不变。不是玄学,是一套有验收标准的工程动作。以下价格与机制取自 DeepSeek 官方定价页与缓存指南,其余厂商(含 Claude、Gemini)的缓存思路一致,字段名叫法不同。

先搞清楚机制

DeepSeek 的磁盘缓存是前缀匹配:只有从第 0 个 token 开始完全一致的部分才算命中,输入中间出现重复段落不会触发。缓存以 64 token 为存储单位,不足 64 token 的内容不进缓存;空闲条目几小时到几天会被清掉;官方明确写了”不保证 100% 命中”。

价差有多大(2026-09「deepseek-flash」闲时价):

  • 输入缓存命中:$0.003 / 百万 token
  • 输入缓存未命中:$0.15 / 百万 token

50 倍。 同样一份预算,前缀写得稳不稳,是能不能多撑两个月和能不能多撑两年的区别。

排布规则:三段式

把每次请求的内容按「会不会变」排成三段,从上到下严格按稳定性排序,谁都不许插队

第一段(必须完全一致,放最前)

  • 角色与系统提示,逐字固定,不要按用户 A/B 版本动态拼
  • 工具 / 函数定义,顺序也要固定(很多 SDK 按字典序输出,别每次打乱)
  • 少样本示例
  • 检索回来的知识片段(如果这批文档在同一次会话里稳定)

第二段(低频变化)

  • 业务配置:开关、阈值、输出语言
  • 用户画像里相对稳定的部分(订阅计划、角色)

第三段(每次都不一样,放最后)

  • 用户输入
  • 时间戳、随机数、request id
  • 上一轮的增量内容

三条红线:

  1. 别把日期塞进系统提示。「今天是 {{today}}」是最常见的自毁操作——它让每天第一次请求必然未命中。把日期挪到第三段。
  2. 别按用户 ID 排序或随机打乱前缀里的列表。示例顺序、工具顺序一旦抖动,从第 0 个 token 起就对不上了。SDK 里如果用 dict 遍历生成工具定义,先排个序再拼字符串。
  3. 前缀少于 64 token 的部分不用指望。如果你的系统提示总长不到 64 token,缓存机制压根不会命中任何东西——这种情况下先把提示写厚(这本就该做),再谈省钱。

验收:别猜,读字段

DeepSeek 的响应 usage 里直接给了两个数:

  • prompt_cache_hit_tokens:命中量
  • prompt_cache_miss_tokens:未命中量

把这两个数打到日志里,按天聚合出命中率。没有这一步,前面所有优化都是盲改。一条最低成本的埋点:

u = resp.usage
hit = getattr(u, "prompt_cache_hit_tokens", 0)
miss = getattr(u, "prompt_cache_miss_tokens", 0)
logger.info("cache", extra={"hit": hit, "miss": miss,
                            "ratio": round(hit / (hit + miss), 4) if (hit + miss) else 0})

上线第一周只看一个数:命中率。之后每次改动 prompt,只允许命中率持平或上升;掉了就回滚。

一周落地

  • 第 1 天:加上面的埋点,什么都不改,先记三天基线。
  • 第 2 天:把系统提示里的日期、随机数全部挪到最后一段。这一步通常立刻见效。
  • 第 3 天:给工具定义和示例列表加固定排序。
  • 第 4 天:把系统提示整段做成常量文件,禁止在代码里拼接(CI 里加一条 grep 检查也行)。
  • 第 5 天:用一个真实的会话用例跑一遍,看多轮的累计命中率——多轮场景下第二轮开始命中率会明显变高,这是正常的,也是这套办法收益最大的地方。

什么情况下别折腾

  • 单次请求的输入本来就很短(系统提示不足 64 token),先别优化,收益为零。
  • 每次输入都完全不同(比如每条用户的文档都不一样),前缀本来就短,缓存价值不大,与其调 prompt,不如考虑分层模型或者本地跑。
  • 命中率已经超过 80% 之后,继续投入的边际收益很小,把时间花在别处。

最后一句:这套办法的收益上限取决于你的 prompt 结构,不取决于你调得多细。做成不可随意拼接的常量,比”尽量注意顺序”靠谱得多——因为三个月后你会忘,脚本不会。