Skip to content

最大化大模型缓存命中率的实战方法

核心铁律:缓存只在"前缀完全一致"时命中,哪怕前缀里改一个字符,从那个字符开始往后全部失效。 所有优化技巧都是围绕这条铁律展开的。

一、Prompt 结构设计:把"稳定的"放前面,"变化的"放后面

这是最关键、收益最大的一条原则。Prompt 的结构应该像一座金字塔:越稳定的内容越靠下(靠前),越易变的内容越靠上(靠后)。

1.1 推荐的标准结构(从前到后)

层级内容变化频率缓存友好度
1. 系统提示词角色设定、规则、输出格式约束几乎不变⭐⭐⭐⭐⭐
2. 工具/函数定义Function Calling 的 schema偶尔更新⭐⭐⭐⭐⭐
3. Few-shot 示例示范输入输出偶尔调整⭐⭐⭐⭐⭐
4. 知识库/参考文档RAG 检索到的长文档按会话变化⭐⭐⭐⭐
5. 对话历史多轮对话的前几轮每轮追加⭐⭐⭐
6. 当前用户问题本次提问每次变化

1.2 反例(典型踩坑)

❌ 错误写法:
"现在是 2026-05-23 22:57:13,用户 ID 是 7027738,
 你是一个专业助手,请遵循以下规则……(5000 字规则)"

→ 把时间戳和用户 ID 放在最前面,导致后面 5000 字规则永远无法命中缓存
✅ 正确写法:
"你是一个专业助手,请遵循以下规则……(5000 字规则)
 ---
 用户上下文:ID=7027738,当前时间=2026-05-23 22:57:13
 问题:……"

→ 5000 字规则固定在前,每次都能命中缓存

二、消除"看不见的随机扰动"

很多时候 prompt 看起来一样,但隐藏着导致缓存失效的"暗雷"。

2.1 常见暗雷清单

暗雷影响解决方法
时间戳("今天是 X 月 X 日")每秒都变,缓存永远不命中移出系统提示,放到 prompt 末尾
随机 UUID / Trace ID每次请求都不同不要写进 prompt,用 metadata 传递
用户 ID / Session ID 在开头每个用户都不同放到 prompt 末尾的用户上下文段
浮点数轻微差异(如温度参数被写进 prompt)0.7 vs 0.70 也算不同统一格式化
空格 / 换行符不一致" " 和 " " 视为不同用模板引擎统一渲染
JSON 字段顺序不固定{"a":1,"b":2}{"b":2,"a":1}序列化时按字母排序 key

2.2 工程实践:用模板引擎统一渲染

python
# 推荐做法:把 prompt 拆成"静态模板 + 动态变量"
STATIC_SYSTEM_PROMPT = """你是一个专业助手……
(5000 字稳定内容)"""

def build_prompt(user_question, user_ctx):
    return [
        {"role": "system", "content": STATIC_SYSTEM_PROMPT},  # 这部分稳定命中
        {"role": "user", "content": f"上下文:{user_ctx}\n问题:{user_question}"}
    ]

三、多轮对话的缓存优化

多轮对话是缓存收益最大的场景,但也最容易因为不当操作让缓存失效。

3.1 保持历史"只追加,不修改"

  • 追加新消息:第 N 轮在第 N-1 轮历史末尾追加,前面全部命中。
  • 修改历史消息:哪怕只是"压缩前几轮",从修改点往后全部失效。
  • 重排消息顺序:调换两条消息位置,整段历史失效。

3.2 长对话的"截断 vs 摘要"权衡

当对话过长接近上下文窗口时,有两种处理方式:

策略缓存影响适用场景
滑动窗口截断(丢掉最早 N 条)截断点之后的内容失效一次,之后保持命中对话信息密度均匀
摘要压缩(把前 10 轮压成 1 段摘要)每次摘要内容都不同,缓存大面积失效谨慎使用,最好定期固化摘要

实战建议:优先用截断,必要时做摘要后长期固定该摘要,不要每轮都重新生成摘要。

四、RAG 场景的特殊技巧

RAG(检索增强生成)每次检索结果可能不同,对缓存非常不友好。需要专门优化。

4.1 检索结果排序要稳定

  • 按相似度分数排序:每次分数微小波动会导致顺序变化。
  • 检索后按文档 ID 排序:同样几篇文档,顺序固定,命中概率大增。

4.2 文档块大小固定化

  • 把文档切成固定 size 的 chunk(比如每块 512 tokens),而不是按句子或段落切。
  • 这样相同文档每次切出来的块完全一致,提升命中率。

4.3 热点文档预热

  • 对高频查询的文档(FAQ、产品手册),可以**定时发送"假请求"**保持缓存活跃,避免过期。
  • 注意:厂商缓存通常有 TTL(5 分钟到 1 小时不等),过期就要重算。

五、批量任务的缓存优化

如果你要处理"基于同一模板的 1000 个任务",正确的姿势能让成本降一个量级。

5.1 同模板任务要"集中、连续"发送

✅ 推荐:把 1000 个同模板任务在 5 分钟内连续发送
→ 第 1 次缓存模板,后 999 次全部命中

❌ 不推荐:分散在一天内零散发送
→ 缓存可能多次过期,反复重算

5.2 利用厂商的 Batch API

  • OpenAI、Anthropic 都提供 Batch API(异步批量接口),通常额外打 5 折。
  • 缓存折扣 + Batch 折扣可以叠加,组合优惠极大。

六、跨用户共享缓存的设计模式

如果你做的是 SaaS 产品,多个用户共用一套系统提示,缓存可以跨用户复用

6.1 让"公共部分"完全一致

  • 系统提示、产品介绍、规则说明:所有用户使用同一份,不要因用户身份做微调。
  • 用户个性化内容:集中放在 prompt 末尾的一个"用户上下文"段

6.2 多租户场景的分层缓存

[全局公共系统提示]  ← 所有用户命中

[租户级配置]       ← 同租户用户命中

[用户个性化上下文]  ← 仅当前用户

[当前问题]         ← 每次不同

七、Function Calling / Tool Use 的优化

Agent 应用经常带几十个工具定义,每个工具的 JSON schema 都很长。

7.1 工具列表要稳定

  • ❌ 根据用户权限动态过滤工具列表 → 每个用户的工具集不同,缓存难命中。
  • 全量发送所有工具,在系统提示里说明权限规则 → 工具列表稳定,命中率高。
  • 权衡:多发的 token 成本 vs 失去缓存的成本,前者通常更划算。

7.2 工具 schema 序列化要确定性

  • 工具的 JSON schema 按固定 key 顺序输出。
  • 描述文本里不要塞动态内容(时间、版本号等)。

八、监控和度量:知道自己省了多少

光做优化不够,得知道实际命中率才能持续改进。

8.1 关注厂商返回的 usage 字段

主流厂商的 API 响应里都会返回缓存命中详情,例如:

json
{
  "usage": {
    "prompt_tokens": 8200,
    "cached_tokens": 7800,        // 命中缓存的 token 数
    "completion_tokens": 800,
    "cache_hit_rate": 0.95         // 命中率
  }
}

8.2 设定优化目标

应用类型健康命中率目标
单轮问答> 60%(主要靠系统提示)
多轮对话> 85%(历史累积)
Agent / RAG> 90%(长前缀 + 模板化)
批量任务> 95%(同模板高频复用)

如果命中率低于目标,回头检查前面 1–7 条是否有踩坑。

九、不同厂商的缓存机制差异(避坑提示)

不同厂商的缓存策略略有不同,迁移模型时要重新评估

厂商触发方式最小缓存长度TTL折扣力度
OpenAI自动(≥1024 token 前缀)1024 token5–10 分钟5 折
Anthropic手动标记 cache_control1024 token5 分钟(可延长到 1 小时)1 折读 / 1.25 倍写
DeepSeek自动64 token数小时1 折
Google Gemini显式创建 cached content 对象32K token自定义按存储 + 命中分别计费

注意 Anthropic 的特殊性:它的缓存写入要加价 25%,但读取只要 10% 价格。所以只有重复使用超过 2 次才划算,一次性请求不要乱标 cache_control

十、一张实战 Checklist

部署前自查:

  • [ ] 系统提示、工具定义放在 prompt 最前面
  • [ ] 时间戳、UUID、用户 ID 移到 prompt 末尾或 metadata
  • [ ] JSON 序列化按固定 key 顺序
  • [ ] 模板用统一渲染函数,避免空格/换行差异
  • [ ] 多轮对话只追加不修改历史
  • [ ] RAG 检索结果按文档 ID 排序
  • [ ] 批量任务集中连续发送
  • [ ] 监控 cached_tokens 指标,设定命中率目标
  • [ ] 针对所用厂商确认其缓存机制(自动/手动、TTL、最小长度)

十一、一句话收尾

优化缓存命中率的本质,就是把 prompt 当作一份"分层契约"来设计:底层是亘古不变的规则,顶层是瞬息万变的输入。层次越清晰、变化点越靠后,省下的钱就越多。

对中等规模的 Agent 应用,认真做完上面这些优化,月度账单降低 40%–70% 是非常常见的结果