KV Cache 是连接"Transformer 理论"和"LLM 工程部署"的一个关键概念。理解它,你就打通了从"模型怎么算"到"模型怎么跑"的最后一环。

KV Cache 深度解析:从 Transformer 底层原理到 API 成本优化

面向 AI Application Engineer 的知识科普、底层逻辑构建与工程实操指导


一、KV Cache 是什么?——从"为什么需要它"讲起

1.1 Transformer 推理的核心矛盾

LLM(如 GPT、Claude)的底层架构是 Decoder-only Transformer。在推理阶段,模型采用**自回归(autoregressive)**方式生成文本——每次只生成一个 token,然后将其追加到序列末尾,再生成下一个 token,循环往复直到生成结束标记。

在每一步生成中,Self-Attention 机制要求当前 token 的 Query 与序列中所有已有 token 的 Key 做点积运算,并对所有已有 token 的 Value 做加权求和。这就是 Attention 公式 $\text{softmax}(\frac{QK^T}{\sqrt{d_k}})V$ 的实际含义。

然而,Transformer 模型本身是无状态的。每次前向传播(forward pass)就像一个纯函数——你给它输入,它计算一遍,输出结果,然后所有中间计算结果(包括每个 token 的 K 和 V 向量)就消失了。模型不会自动"记住"上一次前向传播时计算过什么。

这意味着:如果不做任何优化,每生成一个新 token,模型就必须把整个序列(包括所有之前已经处理过的 token)从头重新送入模型,重新计算所有 token 的 K 和 V。随着序列变长,重复计算量以二次方速度增长,这在工程上是不可接受的。

1.2 KV Cache 的解决方案

KV Cache 的核心思想非常简单:把每一步已经计算过的 Key 和 Value 向量缓存在 GPU 显存中,下一步直接复用,避免重复计算。

本质上,KV Cache 就是给无状态的模型加上了状态——让模型能够"记住"之前步骤的计算结果。

1.3 具体示例

假设模型要生成 “The cat sat on the mat."。

Step 1:输入 prompt “The cat sat on the”(位置 1-5),模型一次性并行计算这 5 个 token 各自的 K 和 V,完成注意力运算,取位置 5 的输出送入预测头,预测出下一个 token 是 “mat”。

Step 2(无 KV Cache):当预测下一个token(位置 7)时,当前序列为 “The cat sat on the mat”(6 个 token)。模型必须把全部 6 个 token 重新送入,重新计算所有 6 个 token 的 K 和 V,做完整的注意力计算。但其中位置 1-5 的 K 和 V 与 Step 1 中算出来的完全一样——因为模型权重没变,输入也没变。这些是纯粹的浪费。

Step 2(有 KV Cache):位置 1-5 的 K 和 V 已经缓存在 GPU 显存中。预测下一个token(位置 7)时模型只需要把 “mat” 这一个 token 送入,计算它的 Q、K、V,把新的 K、V 追加到缓存,然后用它的 Q 与缓存中全部 6 个 K (包括 mat自己的 K)做注意力计算(*附注 1)。计算量从处理 6 个 token 降到了处理 1 个 token。

附注 1:

Causal Mask 的精确定义

Causal Mask 的规则是: 位置 $i$ 可以看到位置 1, 2, …, $i-1$, $i$ ——也就是说,位置 $i$ 可以看到自己。

为什么必须能看到自己?

想一个最简单的情况:位置 1(序列的第一个 token)。

如果 causal mask 规定位置 $i$ 只能看到 1 到 $i-1$,那位置 1 能看到的范围是位置 1 到 0——也就是什么都看不到。这个 token 的注意力输出就是空的,没有任何信息,模型无法基于它做任何有意义的计算。

而实际上,位置 1 的 Q 需要与自己的 K 做点积,得到自己对自己的注意力权重(在只有一个 token 可看的情况下,这个权重就是 1.0),然后取自己的 V 作为注意力输出。这样位置 1 的信息才能正常向后传播。

所以完整的信息流

在一个 Transformer 层中,位置 $i$ 的注意力输出是:

$$ o_i = \sum_{j=1}^{i} \alpha_{ij} \cdot v_j $$

这个求和范围是 $j = 1$ 到 $j = i$,包含 $i$ 自身。$\alpha_{ii}$(位置 $i$ 对自己的注意力权重)通常是非零的,也就是说每个 token 的输出中包含了自身 V 的贡献。

所以前面说"位置 $i$ 的隐藏状态融合了位置 1 到 $i$ 的信息”,这个 $i$ 是精确的,不是 $i-1$。


二、KV Cache 在 Transformer 架构中的精确位置

2.1 所在位置

KV Cache 存在于每一个 Transformer 层的每一个注意力头中

一个典型 LLM(例如 32 层、32 个注意力头、每头维度 128)在推理时,需要为每一层 × 每一个头都存储 K 矩阵和 V 矩阵。对于序列长度为 $n$ 的上下文,KV Cache 总大小为:

$$ 2 \times n_{\text{layers}} \times n_{\text{heads}} \times n_{\text{seq}} \times d_{\text{head}} \times \text{bytes\_per\_element} $$

这就是为什么长上下文窗口(128K、1M tokens)对 GPU 显存是巨大挑战——KV Cache 的显存占用随序列长度线性增长,且在模型参数之外额外占用显存。

2.2 推理的两个阶段

KV Cache 的理解离不开推理过程中两个本质不同的阶段:

Prefill 阶段(预填充):用户的 prompt 输入进来,模型一次性并行处理所有 prompt token,为每一层每一个头计算并存储它们的 K 和 V。这个阶段是**计算密集型(compute-bound)**的,类似训练时的前向传播。这个阶段的耗时决定了 Time-to-First-Token(TTFT)——用户提交请求后等待第一个 token 输出的时间。

Decode 阶段(解码/生成):逐个生成 output token。每步只计算一个 token 的 Q/K/V,追加新的 K/V 到缓存,用新 Q 查询完整缓存。这个阶段是**内存带宽密集型(memory-bound)**的,因为每步需要从 GPU 显存中读取整个 KV Cache。

2.3 每一步预测的真正发生机制

这里有一个容易混淆的点需要澄清。

当我们说"预测第 N+1 个 token"时,模型实际做的是计算位置 N(序列最后一个 token)的输出表示。具体流程:

  1. 位置 N 的 token 通过 Embedding 层得到向量 $x_N$。
  2. $x_N$ 依次通过所有 Transformer 层,在每一层中与 $W_Q$、$W_K$、$W_V$ 做投影得到 $q_N$、$k_N$、$v_N$。
  3. $q_N$ 与位置 1 到 N 的所有 K 做点积得到注意力权重,再对所有 V 做加权求和。
  4. 注意力输出经过 FFN 等模块处理后,得到位置 N 的最终隐藏状态 $h_N$。
  5. $h_N$ 送入模型顶部的 Output Head(一个 Linear 层 + Softmax),Linear 层的权重矩阵形状为 $d_{\text{model}} \times V$($V$ 为词汇表大小),将 $h_N$ 映射为一个 $V$ 维的 logit 向量,Softmax 转为概率分布,从中采样或取 argmax 得到第 N+1 个 token。

关键认知:“预测第 N+1 个 token"和"计算第 N 个位置的输出"是同一件事。 第 N+1 个 token 在被预测出来之前根本不存在,自然也没有什么"第 N+1 个位置的 Q”。Q 始终来自当前序列最后一个位置的 token。

进一步理解:Causal Attention Mask(因果注意力掩码)保证了位置 $i$ 只能看到位置 1 到 $i$ 的信息。因此,$h_i$ 编码的是"在已知位置 1 到 $i$ 的所有 token 之后,下一个最可能出现的 token"这个语义。在训练阶段,模型一次性处理整个序列,每个位置都同时产生预测,每个位置都可以计算 loss(这就是为什么一条训练样本能同时提供多个训练信号)。在推理阶段,我们只关心最后一个位置的输出,因为只有它预测的是真正还未出现的下一个 token。

2.4 为什么只缓存 K 和 V,不缓存 Q?

在 Decode 阶段的每一步,我们只需要当前最后一个位置的 Q 去查询所有历史位置的 K 和 V。这个 Q 是当步实时计算的——它来自刚被追加到序列中的那个新 token。之前位置的旧 Q 呢 ?它们在之前各自的步骤中已经完成了自己的使命(即预测自己下一个位置的 token),当前步骤完全不需要它们。而所有位置的 K 和 V 在每一步都需要被访问(因为当前的 Q 要与所有历史 K 做点积、对所有历史 V 做加权),所以必须缓存。


三、从"单次请求内的 KV Cache"到"跨请求的 Prompt Caching"

3.1 两个层面的区分

这是一个至关重要的概念区分:

层面一:单次请求内的 KV Cache ——这是 Transformer 推理引擎内部的优化。在一次 API 请求的 decode 阶段中,避免每生成一个新 token 都重新计算所有旧 token 的 K/V。这对 API 用户完全透明不可见,由推理框架(如 vLLM、TensorRT-LLM)自动处理。

层面二:跨请求的 Prompt Caching ——这是 API 提供商在基础设施层面做的优化。当一次 API 请求结束后,服务器端不丢弃该请求 prefill 阶段计算的 KV Cache,而是保留一段时间。如果后续的新请求拥有相同的 prompt 前缀,就可以直接加载这些已有的 KV Cache,跳过重复前缀的 prefill 计算。

3.2 跨请求 Prompt Caching 的工作原理

以"对长文档进行多轮问答"的场景为例:

第一次请求的 prompt 结构为:[系统指令] + [50000 token 的文档] + [用户问题 A]

第二次请求的 prompt 结构为:[系统指令] + [50000 token 的文档] + [用户问题 B]

两次请求中,前面 50000+ token 完全相同,只有最后的用户问题不同。

没有 Prompt Caching:第二次请求时,模型在 prefill 阶段把 50000+ token 从头重新计算一遍。你为完全相同的计算付了两次全价。

有 Prompt Caching:第一次请求的 KV Cache 被服务器保留。第二次请求进来时,系统识别出相同前缀,直接加载已有 KV Cache,只对用户新问题那几十个 token 做 prefill,然后进入 decode 阶段。

3.3 为什么必须是"相同前缀"(附注 2)

这是由因果注意力掩码(Causal Mask)的性质决定的。在 causal attention 中,位置 $i$ 的 K 和 V 只依赖于位置 1 到 $i$ 的输入。因此,只要 prompt 的前 $n$ 个 token 完全一致,这前 $n$ 个 token 在每一层中计算出的 K 和 V 就一定完全一致,与后面跟的内容无关。

但如果前缀中有哪怕一个 token 不同(比如开头加了时间戳),从该位置起所有后续的 K 和 V 都会改变,整个缓存失效。

附注 2:

什么是"相同前缀"

“前缀"就是 prompt 从头开始、连续一致的部分。只要从第一个 token 开始,两次请求逐个 token 比较都完全相同,那么这段连续相同的部分就是"相同前缀”。从第一个不同的 token 开始,缓存就失效了。

例 1:典型的缓存命中场景

第一次请求的 prompt:

[System] 你是一个法律文档分析助手。请基于以下合同内容回答问题。
[合同全文] (50000 tokens)
[User] 这份合同的终止条款是什么?

第二次请求的 prompt:

[System] 你是一个法律文档分析助手。请基于以下合同内容回答问题。
[合同全文] (50000 tokens,与上次完全相同)
[User] 合同中的付款周期是多久?

从第一个 token 开始逐个对比:System 指令一样、合同全文一样——直到用户问题开始出现分歧。前面大约 50000+ token 构成相同前缀,cache hit。模型只需对最后那段不同的用户问题做 prefill。

例 2:开头加了时间戳——缓存完全失效

第一次请求:

[timestamp: 2025-03-05 10:00:00]
[System] 你是一个法律文档分析助手...
[合同全文] (50000 tokens)
[User] 终止条款是什么?

第二次请求:

[timestamp: 2025-03-05 10:02:35]
[System] 你是一个法律文档分析助手...
[合同全文] (50000 tokens,完全相同)
[User] 付款周期是多久?

从第一个 token 开始对比:2025-03-05 10:00:00 vs 2025-03-05 10:02:35——在时间戳的秒数位置就已经不同了。相同前缀的长度几乎为零。 后面哪怕有 50000 token 一模一样也没用,因为前缀匹配在最开始就断裂了。整个 prompt 全价重算。

这就是为什么成本优化建议中反复强调:绝对不要在 prompt 开头放任何动态变化的内容。

例 3:中间改了一个词——前半段命中,后半段失效

第一次请求:

[System] 你是一个专业的文档分析助手。
[文档 A](20000 tokens)
[文档 B](30000 tokens)
[User] 总结文档 B 的要点。

第二次请求:

[System] 你是一个专业的文档分析助手。
[文档 A](20000 tokens,完全相同)
[文档 C](30000 tokens,不同于文档 B)
[User] 总结文档 C 的要点。

从头逐 token 比较:System 一样,文档 A 一样——到文档 B vs 文档 C 开始出现分歧。相同前缀大约是 20000+ token(System + 文档 A 的部分),这部分 cache hit。但从文档 B/C 开始往后的全部内容(30000+ token)需要重新 prefill。

例 4:few-shot examples 顺序不同——几乎完全失效

第一次请求:

[System] 你是一个情感分析助手。
[Example 1] 输入: "这个产品太棒了" → 输出: 正面
[Example 2] 输入: "服务很差劲" → 输出: 负面
[Example 3] 输入: "还行吧" → 输出: 中性
[User] 请分析: "今天心情不错"

第二次请求:

[System] 你是一个情感分析助手。
[Example 2] 输入: "服务很差劲" → 输出: 负面
[Example 1] 输入: "这个产品太棒了" → 输出: 正面
[Example 3] 输入: "还行吧" → 输出: 中性
[User] 请分析: "价格太贵了"

System 一样,但紧接着 Example 1 vs Example 2 就不同了。相同前缀只有 System 那一小段。 即使 examples 的内容完全相同只是换了顺序,缓存也几乎完全失效。

工程启示:few-shot examples 的顺序一旦确定就不要随机打乱,否则会破坏缓存。

一句话总结

前缀匹配就像两根绳子从头开始重叠比对——第一个分叉点之前的部分是缓存命中区,分叉点之后的所有内容不论多相似都必须重新计算。 所以 prompt 的设计原则就是:把不变的东西尽量往前堆,把变化的东西尽量往后放。


四、三大 API 提供商的 Prompt Caching 定价对比(截至 2025 年)

4.1 Anthropic(Claude)——显式控制,高命中率

  • 机制:需要在请求中通过 cache_control 参数显式标记缓存断点。
  • 定价:5 分钟缓存的 cache write 为基础 input token 价格的 1.25x,cache read 仅为基础价格的 0.1x(即 10%)。另有 1 小时缓存选项,write 成本更高但保持时间更长。
  • 最小缓存 token 数:Claude Sonnet 4.5 / Opus 4 / Sonnet 4 等需要 1024 tokens
  • 缓存失效逻辑:按 Tools → System Message → Message History 的固定顺序处理请求组件,前面组件的变化会导致后续缓存失效。
  • 实测命中率:主动标记缓存时,命中率接近 100%,性能可预测。
  • break-even point:由于 cache write 有 1.25x 溢价,至少需要 2 次请求才能回本。

4.2 OpenAI(GPT)——全自动,零配置

  • 机制:对 gpt-4o 及更新模型全自动生效,无需代码更改,无额外费用。
  • 定价:cache hit 时提供最高 90% 的折扣(最新模型),无 cache write 额外费用。
  • 最小缓存 token 数1024 tokens,以 128 token 为增量匹配。
  • 缓存生命周期:通常在 5-10 分钟不活跃后清除,最长一小时。支持 prompt_cache_key 参数辅助路由。
  • 实测命中率:因为是自动路由,命中率约 50% 左右,性能可能不够一致。

4.3 Google(Gemini)——双重机制,按时计费

  • 机制:提供两种模式。Implicit caching 默认启用,自动检测重复前缀并给予折扣。Explicit caching 需要通过 API 手动创建缓存、控制 TTL。
  • 定价:Gemini 2.5 及以上模型,cached token 读取成本为标准 input 价格的 10%(即 90% 折扣)。Explicit caching 有额外的按时间计费的存储成本(约 $4.50/百万 token/小时)。
  • TTL:Explicit caching 默认 60 分钟,可自定义。Implicit caching 无存储成本。
  • 适用场景:Explicit caching 更适合需要长时间反复查询同一大文档的场景。

4.4 核心差异总结

维度AnthropicOpenAIGoogle Gemini
控制方式显式标记全自动隐式自动 + 显式手动
Cache Write 溢价有(1.25x)无(但显式有存储费)
Cache Read 折扣90% off最高 90% off90% off
命中率可控性高(接近 100%)低(约 50%)中等
最小 token 数10241024因模型而异(最低 1024)
存储费用Explicit 有按时计费

五、AI Application Engineer 的成本优化思维框架

5.1 第一层:Prompt 结构设计思维

将 prompt 视为"稳定前缀 + 动态后缀"的分层结构:

  • 稳定前缀(放最前面):系统指令、工具定义、few-shot examples、长文档上下文——这些在多次请求间不变的内容。
  • 动态后缀(放最后面):用户的具体问题、当前轮对话内容等每次请求不同的部分。

常见反模式:在 prompt 开头加入时间戳、请求 ID 或其他每次变化的元数据——这会导致前缀从第一个 token 就不匹配,缓存完全失效。

5.2 第二层:请求模式匹配思维

根据应用场景选择最合适的提供商和缓存策略:

  • 多用户共享相同系统 prompt 的高频场景(如客服 bot):适合 OpenAI 的自动缓存,零配置获益。
  • 单用户对同一长文档的多轮问答:适合 Anthropic 或 Gemini 的显式缓存,确保高命中率。
  • 异步批处理场景:可以叠加 Batch API 折扣(通常 50%)和 Prompt Caching 折扣,双重节省。

5.3 第三层:全链路 Token 经济学思维

不要只盯着单次请求成本,要计算完整任务链路:

  • 一个用户完成一次任务全流程消耗多少 input/output token?
  • 其中有多少是跨请求重复的?
  • 缓存命中率能达到多少?
  • break-even point 在哪里(Anthropic 的 cache write 有 1.25x 溢价,至少需要 2 次请求回本)?

5.4 第四层:架构级成本思维

  • Model Routing:用便宜模型处理简单任务,复杂任务才用贵模型。
  • Context Compaction:用 summarization 压缩长对话历史,避免 context window 无限膨胀。
  • 监控验证:监控实际的缓存指标来验证优化策略是否生效。Anthropic 返回 cache_read_input_tokenscache_creation_input_tokens;OpenAI 返回 cached_tokens;Gemini 返回 cachedContentTokenCount

5.5 第五层:对底层约束的工程直觉

理解提供商能给 90% 折扣的根本原因:cache hit 时跳过了 prefill 阶段大量的矩阵乘法运算,只需从显存中读取已有的 KV 向量。但这些向量仍然占用 GPU 显存,因此提供商必须在显存成本和折扣力度之间平衡——这解释了为什么缓存有 TTL 限制、有最小 token 数要求、以及 Google 的 explicit caching 要按存储时长收费。


六、全景知识图谱

Transformer Self-Attention (Q, K, V 的计算)
    │
    ├─→ 训练阶段:所有 token 并行计算,无 KV Cache 概念
    │
    └─→ 推理阶段:自回归生成,逐 token 输出
         │
         ├─→ Prefill 阶段(处理 prompt,计算密集型,决定 TTFT)
         │    └─ 一次性计算所有 prompt token 的 K/V 并存入缓存
         │
         └─→ Decode 阶段(逐 token 生成,内存带宽密集型)
              └─ 每步只算新 token 的 Q/K/V,Q 查缓存中所有 K/V
                   │
                   └─→ [层面一] 单次请求内的 KV Cache
                        (推理引擎自动处理,API 用户不可见)
                        │
                        └─→ 请求结束 → 通常 KV Cache 被丢弃
                             │
                             └─→ [层面二] 跨请求的 Prompt Caching
                                  (API 提供商保留 KV Cache 供后续复用)
                                  │
                                  ├─ Anthropic: 显式控制, cache write 1.25x, read 0.1x
                                  ├─ OpenAI: 全自动, 最高 90% off, 命中率不可控
                                  └─ Google: 隐式+显式, 90% off, explicit 有存储费
                                       │
                                       └─→ AI Application Engineer 的成本优化
                                            ├─ Prompt 结构:稳定前缀 + 动态后缀
                                            ├─ 请求模式:匹配场景选择策略
                                            ├─ Token 经济学:全链路成本计算
                                            ├─ 架构设计:routing + compaction + monitoring
                                            └─ 底层直觉:理解折扣背后的资源约束