Back

大模型缓存命中机制深度解析:不只是前缀匹配,三层缓存体系让Token成本断崖式下降

引言

如果你看过自己团队的大模型 Token 消耗看板,多半会注意到一个现象:主力模型的缓存命中率常年挂在 90% 上下,而且越是高频使用、跑得越久的模型,命中率越稳。

这不是某一家的"黑科技"。从 OpenAI、Anthropic 到 DeepSeek、Google,所有主流大模型 API 服务商都已将缓存作为基础设施。但缓存到底怎么命中?是不是只有前缀完全一样才行?语义相似能不能命中?为什么有人用 DeepSeek 10 块钱跑一整天,有人同样的调用量花了几百块?

本文从底层原理到工程实践,系统拆解大模型缓存的三层体系、各厂商实现差异,以及最大化命中率的实战策略。

一、缓存的三层体系

大模型缓存不是单一机制,而是三个层次协同工作:

层次 机制 匹配要求 省什么 代表实现
L1 KV Cache 单请求内复用已算 K/V 同一次生成内自动生效 计算量(平方级→线性级) 所有推理框架
L2 Prefix Cache 跨请求复用相同前缀的 KV 从第一个 token 开始逐字节一致 输入 token 费用 + 首 token 延迟 OpenAI / Anthropic / DeepSeek / vLLM
L3 Semantic Cache 语义相似复用完整回答 向量相似度 > 阈值 全部 token 费用(含输出) GPTCache / 自建网关

三层缓存严格程度递减:L1 对开发者完全透明,L2 要求前缀逐字节一致,L3 不要求文本一致但属于业务层优化而非模型原生能力。

二、L1:KV Cache — 一切的基础

2.1 为什么需要 KV Cache

大语言模型是自回归模型:每生成一个 token,都要"回头看"之前所有 token,通过注意力机制计算哪些位置更值得关注。如果没有优化,生成第 N 个 token 时要把前 N-1 个 token 重新前向计算一遍,总计算量随序列长度平方级增长

关键观察是:每个位置的 Key(K)和 Value(V)只由该位置的输入 token 与模型权重决定,一旦算出就是常量,不会随后续 token 生成而改变。

KV Cache 的思路极其朴素:把每个 token 在每一层算出的 K、V 张量存下来,后续步骤直接复用。

2.2 两阶段推理

有了 KV Cache,一次推理分为两个阶段:

  • Prefill(预填充):把整个 prompt 一次性喂入模型,并行算出所有位置的 K/V 写入缓存。这个阶段计算密集,但可以高度并行。
  • Decode(解码):逐 token 生成,每来一个新 token 只计算它自己这一个位置的 Q/K/V,把新的 K/V 追加进缓存,再用它的 Q 去和缓存里全部 K/V 做注意力。每步增量计算量恒定。

整段生成的复杂度从平方级降到线性级。但 KV Cache 只在单次请求内生效,请求结束就释放。要跨请求复用,需要 Prefix Cache。

三、L2:Prefix Cache — 省钱的核心

3.1 工作原理

Prefix Cache 把 KV Cache 的复用范围从"单次请求内"扩展到"跨请求"。当多个请求共享相同的前缀时(比如相同的 system prompt、工具定义、知识库文档),这部分前缀的 K/V 只算一次,后续请求直接读取。

底层数据结构通常有两种实现:

  • Radix Tree(前缀树):SGLang、TensorRT-LLM 使用。每个节点代表一个 token 序列片段,天然支持最长前缀匹配,查找效率高。
  • 定长 Block + 哈希链:vLLM 的 Automatic Prefix Caching(APC)使用。把前缀切成定长 block,每个 block 的哈希由"父块哈希 + 本块内容"链式生成,这条哈希链天然表达了"完整前缀"。

两种路线殊途同归:让相同前缀的 KV 只算一次、被很多请求复用。

3.2 匹配规则:逐字节一致

这是最容易被误解的地方。Prefix Cache 是精确的 token 序列匹配,不是语义匹配:

  • 多一个空格、少一个换行、改一个标点 → 后续全部失效
  • 工具定义顺序变化 → 失效
  • system prompt 里加了当前时间戳 → 失效
  • 图片的 detail 参数不同 → 失效
  • 模型不同 → 缓存隔离,不共享

OpenAI 官方文档原话:"Cache hits are only possible for exact prefix matches within a prompt."

缓存从第一个 token 开始匹配,结构必须是:

[固定不变的内容][动态变化的内容]
     ↑ 缓存命中      ↑ 每次新算

如果动态内容插在中间,后面的静态内容也无法命中。

3.3 最小门槛与增量粒度

不是多长的前缀都能缓存。各厂商设置了最小 token 门槛:

厂商 最小缓存长度 增量粒度
OpenAI 1,024 tokens 128 tokens 递增
Anthropic Claude 1,024 tokens(Sonnet/Opus);Haiku 2,048 按 cache_control 断点
DeepSeek 自动识别,无显式门槛 自动
Google Gemini 1,024 tokens -
vLLM 自建 ≥ 1 个 block(通常 16 tokens) block 对齐

不满门槛的请求,cached_tokens 永远是 0。

3.4 缓存有效期

缓存不是永久的。当 GPU 显存不足时,系统按 LRU(最近最少使用)淘汰;即使显存充足,缓存也有 TTL:

厂商 默认 TTL 最长 TTL
OpenAI(内存) 5-10 分钟无活动,最长 1 小时 24 小时(Extended,KV offload 到 GPU 本地存储)
Anthropic 5 分钟 1 小时(1 小时 TTL 写入价为 2x 输入价)
DeepSeek 硬盘缓存,TTL 较长 -

实测数据显示:一个 11,561 token 的上下文,冷启动 prefill 耗时 4,109ms,缓存命中后仅 22ms——加速约 185 倍,99.9% 的前缀被复用

四、各厂商实现对比

4.1 OpenAI:全自动 + Cache-Aware 路由

OpenAI 的 Prompt Caching 对开发者完全透明,无需改代码:

  1. Cache Routing:请求根据前缀哈希(通常前 256 tokens)路由到最近处理过相同前缀的机器
  2. Cache Lookup:在目标机器上检查前缀是否已缓存
  3. Cache Hit:命中则直接复用,cached_tokens 字段报告命中数
  4. Cache Miss:未命中则完整处理,处理完后写入缓存

支持 prompt_cache_key 参数手动影响路由,适合多请求共享长前缀的场景。但注意:同一 prefix + key 组合的请求超过约 15 次/分钟时,部分请求会溢出到其他机器,降低命中率。

2026 年,GPT-5.6 曾出现一个 bug:只匹配完全相同的完整 prompt,不支持部分前缀匹配(GPT-5.5 和 GPT-4o-mini 正常)。社区报告后已确认是 bug 而非预期行为。

4.2 Anthropic Claude:显式标记 + 多断点

Claude 的设计给了开发者更精细的控制。需要在请求中用 cache_control 标记缓存断点:

response = client.messages.create(
    model="claude-sonnet-4-6",
    system=[
        {"type": "text", "text": "你是产品助手。"},
        {"type": "text", "text": long_document, "cache_control": {"type": "ephemeral"}},
    ],
    messages=messages,
)

Claude 支持最多 4 个 cache checkpoint,可以分层缓存:

[System Prompt + Tools]  ← checkpoint 1(全局缓存,跨 session 共享)
[CLAUDE.md 项目上下文]    ← checkpoint 2(项目级缓存)
[Session context]         ← checkpoint 3(会话级缓存)
[Conversation messages]   ← 动态部分,每次追加

这种分层设计让 Claude Code 这样的 Agent 产品能实现极高的命中率。Anthropic 官方甚至为缓存命中率设置了 SEV 级别告警——如果命中率太低,按生产事故处理。

Claude 的定价结构也反映了缓存的成本逻辑:

  • 5 分钟 TTL 缓存写入:1.25x 输入价
  • 1 小时 TTL 缓存写入:2x 输入价
  • 缓存读取:0.1x 输入价(一折

第一次写入反而更贵,真正的节省来自后续多次命中。

4.3 DeepSeek:全自动硬盘缓存 + 极致价差

DeepSeek 的方案最"无脑"——无需手动标记,系统自动识别重复前缀。最大的差异化在于 KV Cache 存在**硬盘(SSD)**上,而非仅在 GPU 显存中,因此缓存容量大、保留时间长。

价格差距极为悬殊:

模型 缓存命中输入 未命中输入 价差 输出
V4 Flash 0.02 元/百万 token 1 元 50 倍 2 元
V4 Pro 0.025 元/百万 token 3 元 120 倍 6 元

0.025 元的物理本质是 SSD 读取预存 KV Cache 的 I/O 成本(约 0.8-1.2ms),而 3 元对应的是加载全部权重、执行完整注意力计算的 GPU 成本。

2026 年 8 月 23 日起,DeepSeek 实施峰谷计费:周末全天低谷价,工作日高峰(9:00-12:00、14:00-18:00)为低谷 2 倍。缓存命中价在峰谷时段也有差异,但始终远低于未命中价。

真实案例:

  • 半个月 8.5 亿 Token 仅花 101 元,输入缓存命中率 92%
  • 充值 10 元从中午用到傍晚,Pro 模型缓存命中率 99.09%
  • 22 个编程任务仅 2.5 元

4.4 Google Gemini:显式创建缓存

Gemini 采用类似 Claude 的显式模式,通过 cachedContent API 创建命名缓存,设置 TTL(默认 1 小时,可延长),在请求中引用缓存名称。缓存创建有固定费用,命中输入价约为正常价的 25%。

五、L3:语义缓存 — 突破前缀限制

5.1 为什么需要语义缓存

Prefix Cache 有两个硬约束:必须是前缀、必须逐字一致。这对客服 FAQ 这类场景很不友好——"怎么退款"和"我要退货"语义相同但 token 序列完全不同,前缀缓存帮不上忙。

语义缓存的思路完全不同:把用户问题向量化,在向量库中搜索相似度,超过阈值就直接返回缓存的完整回答。

用户提问 → embedding 向量化 → 向量库相似度搜索
  ├─ 相似度 > 0.92 → 直接返回缓存回答(< 50ms,几乎零成本)
  └─ 相似度 < 阈值 → 调用 LLM → 结果写回缓存(设 TTL)

开源方案 GPTCache 支持 Redis、Milvus、FAISS 等向量后端,可以嵌入 API 网关层。

5.2 语义缓存的代价

语义缓存省的是全部 token 费用(含输出),但有明确的适用边界:

  • 必须设 TTL:价格、库存、政策等时效性信息不能长期缓存
  • 必须加业务 key:如 sku_id + policy_version,避免跨场景误命中
  • 相似度阈值需调优:太高命中率低,太低返回错误答案。客服场景通常 0.90-0.95
  • 不适合需要个性化的场景:返回的是固定答案,无法根据用户上下文调整

5.3 前沿研究:位置无关缓存

学术界正在攻克"非前缀位置缓存复用"的难题:

  • Prompt Cache(MLSys 2024):用标记语言(PML)把可复用模块声明出来,无论出现在 prompt 哪个位置都能复用,首 token 延迟最高加速 8 倍(GPU)/ 60 倍(CPU)
  • CacheBlend(EuroSys 2025 最佳论文):允许把非前缀位置的文档块缓存直接拼接,只对一小部分 token 做"选择性重算"来修正跨块注意力,TTFT 加速 2.2-3.3 倍
  • EPIC(ICML 2025):系统性提出"位置无关缓存",把非前缀复用的主要障碍归因于 attention sink 现象并做针对性修正

这些方案目前多在研究阶段,尚未被主流 API 厂商商业化。

六、最大化缓存命中率的实战策略

策略 1:静态内容前置(最关键)

这是 Claude Code 团队的核心经验。正确的 prompt 结构:

1. System Prompt(永不改,一个字都不要动)
2. Tools 定义(顺序固定,不随机排序)
3. 长期上下文(CLAUDE.md / 知识库文档 / 业务规则)
4. 会话历史(append-only,只追加不修改)
5. 当前用户输入(每次变化)

Claude Code 曾因为多种原因破坏过这个顺序:在 system prompt 里放精细时间戳、以非确定性方式打乱工具定义顺序、修改 AgentTool 能调用的 agents 列表——每一次都导致缓存大面积失效。

策略 2:用消息更新替代修改前缀

当信息变化时(时间推移、文件被修改、状态更新),不要改 system prompt,而是通过下一条 message 传入:

❌ 错误:system prompt 里写 "现在时间是 2026-08-23 15:30"
   → 每分钟都在变,缓存永远命中不了

✅ 正确:system prompt 保持不变,在 user message 里追加
   → "<system-reminder>现在是周三 15:30</system-reminder>"
   → 前缀缓存完整保留

策略 3:同一会话持续追加

多轮对话天然形成前缀递增:第一轮 1000 tokens,第二轮 1200 tokens(前 1000 命中),第三轮 1400 tokens(前 1200 命中)。新建对话等于缓存从零开始。

不同话题用不同会话隔离——如果在一个对话里混多个不相关主题,上下文"污染"会导致公共前缀断裂。

策略 4:不要在会话中途切换模型

缓存按模型隔离。如果已经和一个模型对话到 100K tokens,这时切到另一个模型问简单问题,新模型的缓存是空的,全部要重新计算。

正确做法:简单问题用轻量模型开新会话;复杂问题留在原模型继续。

策略 5:RAG 场景的特殊处理

RAG 是前缀缓存的"天敌"——检索到的文档每次不同,又往往夹在中间。解决方案:

  • 高频稳定文档前置:产品手册、FAQ、固定规则放在 system prompt 中作为固定前缀
  • 动态检索结果后置:每次变化的检索片段放在用户消息之前、固定前缀之后
  • 语义缓存兜底:对于高频相似问题,用 L3 语义缓存直接返回
  • 文档分块缓存:对于稳定的大文档,用 Anthropic 的多 checkpoint 或 OpenAI 的 extended retention 单独缓存

策略 6:模型分级调度

  • 高频简单任务(分类、翻译、摘要)用 V4 Flash / Haiku,缓存命中价极低
  • 复杂推理任务用 V4 Pro / Opus / GPT-5
  • 不要"一刀切"用最贵的模型

策略 7:错峰调用

DeepSeek 等厂商已实施峰谷定价。非实时批量任务(数据清洗、离线分析、报告生成)安排在夜间或周末运行,输出成本可降 50%。

策略 8:监控命中率

所有主流 API 都在响应中返回 cached_tokens 字段。应该:

  • 按模型、功能、租户分别统计缓存命中率
  • 设置命中率告警(Claude Code 团队按 SEV 级别处理)
  • 命中率突然下降时,检查是否有人改了 system prompt、工具顺序或模型版本

命中率低的常见原因:

  • 会话太短(3 轮以内命中率天然低)
  • 请求被轮询路由到不同后端(非 cache-aware 网关)
  • 前缀中有动态内容(时间戳、随机 ID、用户 ID)
  • prompt 模板频繁变更

七、成本测算:缓存到底能省多少

以一个客服场景为例:每天 10,000 次请求,system prompt + 知识库 = 5,000 tokens,用户问题平均 50 tokens,回答平均 200 tokens,使用 DeepSeek V4 Flash:

方案 日输入成本 日输出成本 日总成本
无缓存 50.5 元 40 元 90.5 元
前缀缓存(命中率 90%) ~5.5 元 40 元 45.5 元
前缀缓存 + 语义缓存(语义命中 30%) ~4 元 28 元 32 元

如果用 DeepSeek V4 Pro 且缓存命中率达到 99%(Agent 编程场景的典型值),有用户实测半个月 8.5 亿 token 只花了 101 元,平均每百万 token 成本仅 0.12 元。

Claude Code 官方给出的数字更激进:对于被缓存的长篇系统指令或文档,输入 token 计费直接打一折。一份 10 万 token 的文档,首次提交后,后续所有针对它的交互请求,文档部分的成本几乎可以忽略。

八、常见误区

误区 1:缓存省输出 token。 错。KV Cache 和 Prefix Cache 只省输入 token 的计算,输出 token 逐字生成的过程完全不变。只有语义缓存(直接返回存好的回答)才省输出。

误区 2:语义相同就能命中前缀缓存。 错。"你好"和"您好"是不同的 token 序列,前缀缓存要求逐字节一致。语义相似走的是 L3 语义缓存,是完全不同的机制。

误区 3:缓存永久有效。 错。TTL 从 5 分钟到 24 小时不等,GPU 显存不足时 LRU 淘汰。需要长 TTL 的场景应使用 OpenAI Extended(24h)或 Anthropic 1 小时 TTL。

误区 4:缓存会影响回答质量。 错。KV Cache 只是复用已计算的中间张量,模型权重、采样参数、温度设置完全不变。同一个 prompt,有无缓存生成的结果在概率上完全一致。

误区 5:小 prompt 也能命中。 错。有最小 1,024 token 门槛(Claude Haiku 为 2,048),短请求不会被缓存。

误区 6:第一次调用就省钱。 错。缓存第一次写入时,Anthropic 收取 1.25-2x 的写入溢价;其他厂商虽然不额外收费,但第一次请求是完整计算。真正的节省从第二次命中开始。

结语

大模型缓存的内核始终是同一条原则:算过且不会变的东西不要重算。 按"变化频率从低到高"组织上下文,让可复用的前缀尽可能长,是所有缓存优化的根本。

三层缓存各有定位:KV Cache 解决单次推理的效率问题,Prefix Cache 解决跨请求的重复计算问题,语义缓存解决高频相似问题的复用问题。理解它们的边界和协同方式,就能在自己的系统里把延迟和成本压到最低。

对于开发者而言,当下最立竿见影的行动是:检查你的 prompt 结构,把静态内容前置、动态内容后置;在 API 网关层接入语义缓存;监控 cached_tokens 字段并设置命中率告警。这三步通常能将输入 token 成本降低 50-90%。

缓存不是锦上添花,而是大模型应用从"能用"到"用得起"的关键基础设施。


参考资料

  • OpenAI Prompt Caching 官方文档
  • Anthropic Prompt Caching 官方文档与 Claude Code 工程实践
  • DeepSeek API 定价与上下文硬盘缓存文档
  • vLLM Automatic Prefix Caching 文档
  • Google Gemini Caching 文档
  • 《为什么大模型的缓存命中率能到 90%?》阿里技术,2026.07
  • 《构建 Claude Code 的经验:Prompt Caching 是一切》Anthropic
  • CacheBlend (EuroSys 2025 Best Paper)
  • EPIC: Position-Independent Caching (ICML 2025)
  • Prompt Cache (MLSys 2024)

评论