晨光里的AI
《超级马力欧》的像素画面,马力欧在砖块平台之间奔跑,身旁飘着几枚旋转的金币

AI技术理解

不是存答案,而是存「计算进度」:KV Cache 为什么能省钱?

KV Cache 缓存的不是答案,而是模型处理上下文时产生的中间计算结果。从这一句出发,解释改动前文为什么会让缓存失效、换模型为什么要重新付费,以及它和上下文压缩之间的取舍。

Written by 晨旭发布于 约 10 分钟同步发布

TL;DR

「命中缓存,计费就会少很多」「切换模型,花费会更多哦」,这两句提示指向的是同一件事。 KV Cache 存的既不是答案也不是记忆,而是模型在预填充阶段读完输入后产生、并会在后续生成中反复使用的中间计算结果。 由此推出三件事:缓存只能复用从开头开始连续相同的那一段,所以改动前文会让其后的计算全部作废; 缓存属于生成它的那个具体模型,所以中途换模型要把整段历史重新算一遍; 而上下文压缩专门在改写前缀,于是它和缓存复用之间存在一个必须权衡的取舍。

目录

开篇

做 Agent 产品的时候,大概都听过这句话:

命中缓存,计费就会少很多。

用 AI 产品的时候,可能也会看到这样的小提示:

如果切换模型,花费会更多哦~

这两句话其实指向同一件事:KV Cache 可以改变一次请求要花多少钱。

第一次听到 KV cache 可能会下意识地觉得必须先把 Transformer、注意力机制、矩阵运算都搞明白,才有资格理解它。但如果只是想在做产品时把它用对,完全不需要这些术语。

下面可以从我们听到最多的那个问题开始:

为什么同一段上下文,第一次交给大模型处理比较贵,再次使用却能便宜很多?难道是模型把答案提前存好了吗?🤔

不是这样的。

KV Cache 缓存的是模型处理这些内容时产生的中间计算结果。下一次遇到相同的开头,系统可以直接复用这些结果,不必从头再算。

接下来解决的每一件事:为什么改动前面的内容会让缓存失效、为什么换模型就要重新付费、它和上下文压缩是什么关系,都是从这一句推出来的。👇

模型「读一遍」并不便宜

首先我们平时和大模型聊天,它为什么会一直记得前面说过的话?这个我在之前的文章中有写到~(一定要先理解这个原理才能理解下面的内容🌟)深度解析 Memory 本质(在第二部分:记忆的本质)

人读一份一百页的资料需要花时间理解,大模型也一样。

模型在收到一段文字后,它不能直接开始回答。它需要先处理所有输入,理解每句话之间的关系:

  • 这句话是谁说的?
  • 它和前面的要求有什么关系?
  • 哪些信息比较重要?
  • 当前问题应该参考哪些内容?

这个「阅读和理解输入」的过程需要大量计算。

一段几百字的对话可能不明显,但在编程、办公助手这种长程任务 Agent 中,输入经常包含:

  • 几十轮聊天记录
  • 大量代码
  • 文件内容
  • 搜索结果
  • 命令执行日志

一轮请求可能包含几万甚至几十万字。

如果每次请求都从头计算一遍,这会浪费大量时间和算力。

于是,KV Cache 出现了。👇

KV Cache:把已经算过的部分保存下来

大模型在收到一段文字之后,不能马上给出回答。它需要先处理整段输入,通过大量计算建立各部分内容之间的关系,再在这些计算结果的基础上生成回复。

而且,后面的计算会受到前面内容的影响。

即,大模型处理一次请求,可以粗略分成两个阶段:

  1. 处理输入,建立对上下文的计算结果;
  2. 根据这些结果生成回答。

第一阶段也叫预填充。KV Cache 主要节省的就是这一阶段中重复发生的计算。

可以把这个第一阶段想象成一张很长的电子表格。表格中有一百行公式,后面每一行都需要参考前面的结果。

第 1 行 → 计算结果 1
第 2 行 → 计算结果 2
……
第 100 行 → 计算结果 100

第一次打开表格时,电脑需要把一百行全部计算一遍。

如果我们只在末尾新增第 101 行,前面一百行完全没有变化,电脑就不必从头重算,只需要读取以前保存的结果,再计算新增的一行。

👉 KV Cache 保存的,就是大模型在阅读和理解已有内容时产生、并会在后续生成中反复使用的一部分中间计算结果。

它不是对原文的摘要,也不是模型对过去的记忆,而是模型服务替它保存下来的计算结果。

顺便说一句:为什么叫 KV Cache 呢?

KV 中的 K 和 V,分别来自两个英文单词:

  • K:Key
  • V:Value

它们是模型处理上下文时产生、并会在后续生成中反复使用的两组内部数据。

不理解背后的数学公式没关系~只要记住:

KV Cache 保存的不是聊天记录、摘要或者最终答案,而是模型处理上下文时产生的部分中间计算结果。

所以,可以把 KV Cache 简单理解为:

大模型的上下文计算缓存。

把这个过程放到真实对话中看,就更容易理解了~👇

第一次请求是:

你是一名编程助手。项目使用 Go。数据库使用 MySQL。请帮我设计用户表。

第二次请求是:

你是一名编程助手。项目使用 Go。数据库使用 MySQL。请帮我设计用户表。接下来再帮我设计订单表。

第二次请求的开头与第一次完全相同,只在末尾增加了一个新问题。

这时,模型服务就可以直接复用前面那部分已经保存的计算结果,只处理新增加的内容。这就是所谓的「缓存命中」。

连续对话特别适合使用 KV Cache,正是因为每一轮通常都可以理解为:

上一轮的全部内容 + 本轮新增内容

👉 前面的聊天历史保持不变,新的消息不断追加在末尾,因此已经完成的计算可以被反复复用。

但如果我们把原来的「项目使用 Go」改成「项目使用 Python」,情况就不一样了。👇

这时,后面几句话可能就需要根据 Go 和 Python 这两个不同编程语言的特点,去重新理解。从发生变化的位置开始,后续计算结果通常都不能继续使用。(因为模型理解输入的方式是计算,而这个计算过程就是和上面比喻的表格一样,前后相互关联着。)

所以,KV Cache 并不要求整次请求从头到尾都与上一次完全相同,但它通常只能复用:

从开头开始,连续保持相同的那一部分内容。

不过,拥有相同的开头还不够。KV Cache 是某个具体模型计算出来的结果,因此能否复用,还与当前使用的是不是同一个模型有关。👇

为什么换了模型,花费通常会增加?

前面提到,聊天过程中只要上下文的开头保持不变,模型服务就可能复用之前保存的计算结果,只处理后来新增的内容。

但这些缓存并不只由聊天内容决定,它还属于生成它的具体模型。

不同模型使用不同的参数和计算方式。即使读取完全相同的聊天记录,产生的中间计算结果通常也不一样:

模型 A:聊天历史 → 模型 A 的缓存
模型 B:同一段聊天历史 → 模型 B 的缓存

因此,模型 B 通常不能直接使用模型 A 生成的缓存。

如果在聊天途中切换模型,可能出现这样的过程:

  • 切换前:模型 A 已有缓存 → 主要处理本轮新增内容
  • 刚切换到模型 B:模型 B 没有对应缓存 → 需要重新处理整段聊天历史
  • 继续使用模型 B:模型 B 已经建立自己的缓存 → 后续请求又可以复用相同的上下文前缀

这就是为什么聊天进行到一半时切换模型,花费往往会突然增加:聊天记录虽然没有消失,但新模型需要重新处理它。

那如果后来又切回模型 A,能不能重新使用原来的缓存呢?🤔

有可能,但也不能保证。还要看旧缓存是否仍然存在、是否已经过期,以及请求开头是否仍然完全一致等等。

所以,缓存能否复用,需要两件事同时成立:请求的开头没有变化,以及处理它的还是同一个模型。可以这样记:

聊天历史属于这段对话,但缓存属于处理它的那个具体模型。

不过,这里就有一个矛盾。KV Cache 能省钱,前提是前面的内容一直保持不变。但在真实的 Agent 产品里,有一件事专门在做相反的事情:上下文压缩。👇

上下文压缩与 KV cache

KV Cache,是在缓存模型处理上下文时产生的中间计算结果,回到前面说过的两个阶段,它只参与第一阶段:处理输入,完全不参与生成答案。

所以,即使前面的聊天记录命中了缓存,模型仍然需要根据本轮新增的问题生成回答。同一段历史后面接上不同的问题,当然会得到不同的答案;即使问题完全相同,由于生成过程可能存在随机性,两次回答也不一定一模一样。

如果输入完全相同,读取此前保存的中间计算结果,与重新计算这部分内容,理论上应该得到相同的效果。KV Cache 没有删除原始信息,也没有重新改写上下文,它只是让相同的内容不必重复计算。

这也是 KV Cache 和上下文压缩最重要的区别:

  • KV Cache 是「相同的内容不重复算」
  • 上下文压缩是「把原来的内容变短以后再算」

👉 KV Cache 通常是无损复用,而上下文压缩会删减、截断或总结历史信息,因此可能遗漏细节。

下面重点讨论一下最常见的摘要式压缩:👇

假设一段对话原本是:

A + B + C + D + E + F

系统保留最近的 E、F,把前面的内容整理成「摘要 1」:

摘要 1 + E + F

随后,对话又产生了 G、H、I:

摘要 1 + E + F + G + H + I

在正常聊天阶段,前面的内容没有变化,只是在末尾不断增加新消息,因此已有缓存可以持续复用。

但当系统再次压缩时,就需要在两种方式之间选择。

(1)第一种方式是保留旧摘要,只压缩后来新增的历史:

摘要 1 + 摘要 2 + H + I

👉 这种方式能够保留更稳定的输入前缀,有利于继续复用旧缓存。但随着压缩次数增加,摘要会越来越多,内容之间也可能出现重复、过时甚至矛盾。

(2)第二种方式是把旧摘要和新增历史重新整理成一份统一摘要:

摘要 2 + H + I

👉 这样可以删除过时信息、合并重复内容,并让上下文长期保持简洁一致。不过,「摘要 1」变成「摘要 2」以后,输入前缀发生了变化,压缩后的对话就需要重新计算并建立新的缓存。

但是,(这里有个小分支)这难道就意味着之前的缓存完全浪费了吗?

不一定。🙂‍↔️

如果压缩系统设计得当,那么生成新摘要的请求这里就可以用到 KV cache 了~系统只需要继续处理新增内容和压缩指令,再生成新的摘要。

但是,(回到主线)新摘要替换旧内容以后,主对话模型处理请求就不能用之前的缓存了,因为「摘要 1」已经变成「摘要 2」,从变化的位置开始,旧缓存通常不能继续用于压缩后的对话。

整个过程可以概括为:

  1. 正常对话:前文不变,末尾增加消息 → 持续命中缓存
  2. 生成摘要:利用未变化的前缀生成新摘要 → 旧缓存仍可能降低压缩成本
  3. 摘要替换:新摘要改变了输入前缀 → 重新建立缓存
  4. 继续对话:新摘要保持不变,末尾增加消息 → 再次持续命中缓存

所以,摘要管理和缓存复用之间存在一个取舍:

  • 保留所有旧摘要,缓存更加稳定,但上下文会越来越杂乱;
  • 定期合并旧摘要,会暂时损失部分缓存,但上下文更加简洁一致。

👉 实际系统可能采用增量摘要、定期合并,或者两者结合。无论选择哪一种,核心目标都是在缓存成本、上下文长度和信息完整性之间取得平衡。

最后

KV Cache 是模型服务保存下来的中间计算结果。相同的上下文再次出现时,系统可以直接复用这些结果,只计算新增的内容。

但它不是完全免费,因为读取缓存、新增内容和生成答案仍然需要资源。

它也不是在缓存答案,更不是模型突然拥有了长期记忆。

完整过程可以概括为:

  • 第一次请求:处理全部内容 → 产生中间计算结果 → 保存可复用结果 → 生成回答
  • 后续请求:读取已有计算结果 → 处理新增内容 → 生成新回答

所以,「缓存命中为什么更便宜」的真正答案是:

因为大模型不必重新完成已经做过的计算。存储和读取依然有成本,但通常远低于重新调用 GPU 计算整段上下文的成本。

核心结论

标注「判断」「假设」的是我的看法而非事实;标注「已推翻」的保留在这里,不删除。

  • KV Cache 存的既不是聊天记录、摘要,也不是答案,而是模型读完输入后产生、并会在后续生成中反复使用的中间计算结果。它是上下文计算缓存,不是模型的记忆。#

    把握较大

  • 缓存只能复用从开头开始、连续保持相同的那一部分内容。从发生改动的位置起,后面的计算结果通常都不能继续使用。#

    把握较大

  • 聊天历史属于这段对话,但缓存属于处理它的那个具体模型。中途切换模型,新模型必须把整段历史重新处理一遍,这就是花费突然增加的原因。#

    把握较大

  • 上下文压缩和缓存复用天然冲突:保留旧摘要能维持稳定前缀、利于命中缓存,但上下文会越来越杂乱;合并成一份新摘要能让上下文简洁,却改写了前缀、让旧缓存作废。#

    判断 · 把握中等

引用本文

晨旭,《不是存答案,而是存「计算进度」:KV Cache 为什么能省钱?》,晨光里的AI,2026-09-07

[晨旭:《不是存答案,而是存「计算进度」:KV Cache 为什么能省钱?》](https://chenxu.xin/writing/kv-cache-why-cheaper)

带进你的 AI 继续追问

这篇文章有一份干净的 Markdown 原文,可以直接交给任何模型读,不用复制粘贴。