LabHub

博客

LLM 缓存的原理 — 提示词缓存与 Prefix Cache 为什么能省钱

한국어English日本語中文

引言 — 「前缀相同就便宜」的谜团

LLM API 的提示词缓存,第一次看会觉得很怪。费用变便宜靠的不是把同一个问题问两次,而是提示词的前半部分相同——即使后半部分完全不同也没关系。更奇怪的是,有的提供商对缓存的"写入"额外收费,"读取"费用却只要十分之一。这份怪异的价目表不是营销手段,而是Transformer 内部物理规律的直接体现

本文从这套物理规律的底层往上讲起:Transformer 生成文本时内部到底发生了什么,KV 缓存是什么、为什么只对前缀(prefix)生效,vLLM 和 SGLang 这类推理服务引擎是怎么实现它的,以及在实战中该如何为 Anthropic、OpenAI 的提示词缓存做设计。

第一步 — 推理分为两个阶段:Prefill 与 Decode

Transformer 推理有两个性质完全不同的阶段。

提示词缓存节省的,正是prefill 阶段的运算。如果一个 10 万 token 的系统提示词已经在缓存里,那这 10 万 token 就不用再重新 prefill 一次。所以缓存命中省下的不只是成本,还会大幅缩短 TTFT。

第二步 — 注意力与 K/V:每个 token 都会印一张名片

为什么 prefill 可以被跳过?得看注意力(attention)内部。

在 Transformer 的每一层,每个 token 都会被转换成三个向量 — Query(查询)、Key(键)、Value(值)。直观地说,每个 token 会先印好介绍自己的名片(K)内容物(V),新来的 token 则用自己的问题(Q) 去翻看之前所有 token 的名片、计算相关度,再按相关度把内容物混合进来。

这里有一个核心性质。GPT 类模型的注意力是因果的(causal)——每个 token 只能看见排在自己前面的 token。这意味着:

token i 的 K 和 V,只是 token 1 到 i 的函数。不管后面来什么 token,它们都绝不会改变。

这条性质是一切缓存的根基。只要"到目前为止的文本"相同,每个 token 的 K/V 向量就会逐比特(bit)算出完全相同的值。既然如此,算过一次的东西为什么还要再算一次?存起来就行了。

第三步 — KV 缓存:存起来就好

KV 缓存 正是那个存储的地方。在 decode 阶段生成新 token 时,模型需要用到之前所有 token 的 K/V。如果没有缓存,每生成一个 token 就得把目前为止的整个序列重新算一遍,生成成本会随序列长度呈平方级爆炸。有了 KV 缓存,只需要计算新 token 的 Q/K/V,之前的部分直接从缓存里读。

这不是免费的。KV 缓存很吃内存。一个 token 占用的 KV 缓存大小,大致是这样算出来的:

每 token 的 KV 缓存 = 2(K 和 V)× 层数 × KV 头数 × 头维度 × 字节数

例:7B 级模型(32 层,隐藏维度 4096,fp16)
   = 2 × 32 × 4096 × 2 字节 ≈ 每 token 约 512KB
   → 一段 4,000 token 的对话 ≈ 约 2GB
   → 几十个并发用户,GPU 内存就会瞬间见底

这种内存压力解释了不少最新架构的设计——GQA(多个 Q 头共享 K/V 头)和 MQA 都是通过减少 KV 头数来把缓存压缩到几分之一的设计,KV 缓存量化(FP8/INT4)也是出自同样的压力。在长上下文时代,真正的成本往往不是权重,而是 KV 缓存。

第四步 — Prefix Cache:跨请求也能复用

到目前为止的 KV 缓存说的都是一次请求之内的事。Prefix Cache(前缀缓存) 把这个概念扩展到了请求之间

如果两个请求共享相同的前半部分(比如同一个系统提示词 + 同一份文档),那这部分的 K/V 就是完全相同的——这正是前面说的因果性带来的好处。所以没必要在第一个请求算完就把 K/V 块丢掉,留着它,等下一个请求的前缀一致时,跳过 prefill 直接接着用就行。

为什么只有"前缀"才行? 这是因果性的另一面。token i 的 K/V 是 1..i 的函数,所以哪怕中间只改了一个 token,那个位置之后所有 token 的 K/V 都会变。在提示词中间插入一个时间戳,后面 10 万 token 的缓存就会整体失效,原因就在这里。缓存反应的不是"内容是否包含",而是"前缀是否逐字节相同"。

真实推理服务引擎的实现:

第五步 — 提供商的提示词缓存:怎么读价目表

API 提供商的"提示词缓存",就是把这套 prefix cache 商品化的结果。现在这份价目表可以当作物理规律来读了。

Anthropic(Claude) — 显式缓存。在请求里标出 cache_control 断点(最多 4 个)。

OpenAI — 自动缓存。1024 token 以上的提示词只要前缀一致,不需要任何标记就会自动生效,按公开资料,被缓存的输入 token 有 50% 的折扣。没有显式标记用起来省心,但你也就没有"缓存到哪里"的控制权。

两种方式的验证方法是一样的——看响应里的 usage 字段。如果是 Anthropic,cache_read_input_tokens 不为 0,才说明缓存真的被读取了。反复请求却一直是 0,说明某处藏着一个沉默的缓存破坏者。

第六步 — 实战设计规则

搞懂原理之后,规则自然就出来了。

结语 — 价目表就是物理规律

浓缩成一句话就是这样。注意力是因果的,所以前面 token 的 K/V 不会变;KV 缓存在一次请求内复用它,prefix cache 在请求之间复用它;提示词缓存的价目表(写入溢价、读取折扣、只认前缀、有最小长度)不过是把这种复用的成本结构原样换算成了价格。"为什么只有前面部分才行?"这个问题的答案,从来不是营销政策,而是一行数学:token i 的 K/V 只是 1..i 的函数。

服务成本的整体图景,在 AI 模型开发生命周期 的服务小节,以及把 GPU 拆分使用的 MIG 指南 中继续展开。

参考资料

评论

还没有评论。

登录后即可发表评论