用大模型连续聊一下午,回复越来越慢、账单越来越厚,其中一笔持续膨胀的开销叫 KV 缓存——把对话历史算过的中间结果存起来反复用的记账本。这一篇按记账的规矩,把这笔内存账一笔一笔算给你看。
先看生成的一步:旧内容为什么要反复用
模型生成文字是逐字进行的:每出一个新 token,注意力都要回头看前面所有内容。打分需要每个旧 token 的两份中间结果,一份叫键(K),一份叫值(V)。如果每生成一个字都把整段历史重新算一遍,计算量会随序列长度平方级上涨,长对话根本跑不动。工程上的解法很直接:算过的键和值存起来,下一步直接取用,这份存储就是 KV 缓存。它能成立的前提是旧 token 的键值算一次就够,之后不再变化,存下来才有意义。顺带解释一个常见现象:贴进一篇长文档后,回复的第一个字特别慢,因为这一步要为全部输入一次性建好缓存,业内叫预填充,这笔账就是在那时一次记满的。
账本结构:一层一份,随对话累加
模型有很多层,每层的注意力各自维护一套缓存,所以账本是"层数乘以每 token 键值大小"逐条累加。每来一个新 token,账上就多一行;对话越长、层数越多、隐藏维度越宽,这页账越厚。这里有个可带走的判断:长对话越聊越卡,未必是模型变笨了,更常见的原因是缓存膨胀挤占显存和带宽,把推理速度拖了下来。
长对话是怎么吃掉内存的
把账摊开:缓存显存大致等于 token 数乘层数乘每层键值宽度乘每个数值的字节数,四项连乘。使用者能动的主要是第一项和最后一项——控制对话长度,或者用更低精度的数值存缓存,后者正是量化技术的主场之一。支持长文档阅读的产品如 Kimi、DeepSeek 在处理超长输入时缓存规模陡增,这是长上下文服务成本更高的底层原因之一,具体数字以官方技术报告为准。多轮对话尤其要留意:上一轮贴进去的三千字资料,在之后每一轮回复时都躺在缓存里,持续占内存也持续计费。反过来,把旧内容清掉再继续问,速度常常立刻回来,因为账本被裁短了,这就是为什么"新开一个对话"是长会话卡顿的第一处方。
四项连乘里,只有两项握在使用者手里。
| 影响因子 | 怎么把账做大 | 使用者能不能动 |
|---|---|---|
| token 数(对话长度) | 每来一个新 token 账上就多一行,多轮里贴进去的长资料每轮都躺着占内存 | 能动:清掉旧内容、开新对话,账本立刻被裁短 |
| 层数 | 每层注意力各维护一套缓存,层数越多,逐条累加出来的账页越厚 | 不能动:由模型结构决定 |
| 每层键值宽度(隐藏维度) | 维度越宽,每个 token 的键值就越大 | 不能动:同样由模型结构决定 |
| 每个数值的字节数(存储精度) | 同样的键值用更低精度存,占的字节就少 | 能动:量化技术的主场之一 |
缓存满了会发生什么
显存放不下时,各家策略不同:有的直接拒绝超长请求,有的把较早内容挤出去,后续用到再重算,有的压缩存储精度换取容量。对使用者的含义很实际——对话太长时模型可能"忘记"开头,未必是能力问题,可能是早期缓存被裁掉了。想让序列变短,分词粒度也有影响,可以看分词器的词表是怎么训练出来的;想看生成链路的后续,可以读采样是怎么从概率里挑字的、RoPE 位置编码是怎么工作的。
长对话缓存自查清单
- 越聊越慢?先检查历史消息的总长度
- 旧资料还用得上吗?用不上就开新对话,缓存清零
- 多轮追问同一份长文档?每一轮都在为它付内存与费用
- 开头内容被"遗忘"?可能是缓存被裁,别急着怪模型
一句话带走:缓存跟着上下文长度线性涨,砍历史就是砍账单。
KV缓存快问快答三则
- 为什么它只存键和值,不存其他中间量?:注意力打分只需要旧 token 的键值两份结果,查询是当步现算的,存了也用不上。
- 关掉对话窗口缓存就没了?:通常如此,新对话从空缓存开始,这也是"换个话题就利索"的原因之一。
- 缓存越大模型越聪明吗?:缓存只是历史结果的存放区,大小取决于聊了多长,与模型能力没有正比关系。


