如果你仔细看过 LLM API 的计费明细,会发现两个耐人寻味的现象:一,命中”前缀缓存”的输入 token,价格显著低于原价,不同平台折扣力度不同,但普遍能便宜一半以上;二,同一场多轮对话里,越往后往往响应越快、账单越便宜。
这两个现象背后是同一个机制——KV Cache。它是大模型推理系统里最重要、却最少被应用开发者谈论的组件。上一篇我们聊了注意力作为”预算”的应用层含义,这一篇往下钻一层:看看推理系统实际是怎么为注意力买单的,以及理解它之后,你能写出更便宜、更快的 Agent。
一、为什么推理必须缓存
先建立一个最小的事实框架。LLM 生成文本是自回归的:一个 token 接一个 token 地往外蹦,每生成一个新 token,模型都要拿它的 Query 去和全部历史 token 做 attention。
而 attention 需要三样东西:Query、Key、Value。关键在于,历史 token 的 Key 和 Value 每一轮算出来的结果都是一样的——第 1 个 token 的 K/V 不会因为对话进行到第 50 轮就发生变化。如果每生成一个新 token 都把这些 K/V 重算一遍,就是纯粹的浪费。
KV Cache 做的事情简单粗暴:把每个 token 的 Key 和 Value 算一次就存下来,后续生成时直接取用。效果是巨大的——没有缓存时,生成长度为 n 的序列,K/V 的总计算量随 n 平方增长;有缓存后,每个 token 的 K/V 只算一次,总量线性增长。序列越长、对话轮次越多,省得越狠。
用一段伪代码表达这个差异:
# 无缓存:每生成一个 token,重算全部历史的 K/V
for step in range(n):
kv = compute_kv(all_tokens[:step]) # O(step) 次重算
out = attention(q[step], kv)
# 有缓存:历史 K/V 只算一次,逐 token 追加
cache = []
for step in range(n):
k, v = compute_kv(tokens[step]) # 只算当前 token
cache.append((k, v)) # 追加进缓存
out = attention(q[step], cache)
到这里都是工程上的稳赚不赔。但天下没有免费的缓存——KV Cache 存在哪里?答案是把账单从”计算”转移到了”显存”。
二、显存账单:长上下文为什么贵
KV Cache 的大小有一个朴素的估算式:
缓存量 = 2(K 和 V)× 层数 × KV 头数 × 头维度 × 序列长度 × 精度字节数
代入一个典型配置感受一下:一个 7B 级模型,32 层、32 个注意力头、fp16 精度,每生成 1 个 token 就要在显存里躺下约 512 KB 的 K/V;上下文拉到 8K,KV Cache 就是约 4 GB——而模型权重本身也不过十几 GB。
这组数字直接解释了应用层的很多”玄学”:
- 为什么输入 token 也要收费,长上下文更贵:你的每一次请求,服务端都要为它的 KV Cache 分配显存,序列越长占用越久;
- 为什么并发一高服务就紧张:显存要同时装下权重、KV Cache 和激活值,缓存是其中弹性最大的那块,长上下文并发一多,显存立刻见底;
- 为什么各家都在卷推理优化:GQA、MQA 把 KV 头数压缩几倍,PagedAttention 把显存管理做成操作系统式的分页——每一项都是在给这张账单打折。
理解了账单的构成,再看 API 定价就通透了:你付的钱,本质上是在为”算力 + 显存占用时长”付费。
三、Prefix Caching:把前缀变成资产
KV Cache 是单次请求内部的复用,而 Prefix Caching(前缀缓存)把它升级成了跨请求的复用:如果两次请求的前缀完全一致,那么这段前缀的 KV 直接复用,不必重算,于是计费打折、首字延迟下降。
注意”完全一致”四个字——它是逐 token 级别的严格匹配,从第 0 个 token 开始,一旦在某个位置出现不同,从那个位置往后的缓存全部作废。
这个特性对应用层的启示极其具体:
- 稳定的内容放前面。System prompt、工具定义、few-shot 示例,这些每轮对话都不变的内容应该占据上下文的最前面,成为可复用的缓存段;
- 动态的内容放后面。时间戳、随机 ID、用户名这类每轮都变的信息,一旦插在开头,等于每一轮都亲手打碎整条缓存。常见的事故是有人喜欢在 system prompt 第一行写”今天是 YYYY-MM-DD”——就为了这一行日期,后面几千 token 的稳定前缀全部失去缓存资格;
- 多轮对话只追加、不重排。追加式生长的历史,前缀始终稳定;如果每轮都把历史重新排序或改写,缓存就永远命不中。
写到这里你可能已经反应过来了:这三条准则,和上一篇讲的”上下文工程”严丝合缝地咬合在一起。上下文工程不只是注意力预算的管理,同时也直接决定了缓存的命中率——同样的语义,不同的排列,成本和延迟可以差出数倍。
四、把两笔账合起来算
现在可以给出一个完整的视角:一个 Agent 的上下文,同时消耗两份稀缺资源。
第一份是模型侧的注意力预算——内容越乱越多,推理质量越差,这是上一篇文章的主题。第二份是系统侧的显存与算力——token 越多、前缀越不稳定,推理越贵越慢,这是本文的主题。妙处在于,两份资源的优化方向高度一致:
- 精简上下文 → 注意力更聚焦,同时显存占用更低;
- 稳定前缀 → 模型行为更可预测,同时缓存命中率更高;
- 工具结果按需加载 → 噪声更少,账单也更薄。
也就是说,一个把上下文工程做好的应用,天然就是推理成本友好的应用。反过来,那些把上下文窗口当硬盘用的设计,付出的是双重代价:注意力被稀释导致质量下降,显存被占满导致成本上升。
最后留一个练习:回去翻一翻你的 Agent 代码,检查 system prompt 的第一行是不是动态时间戳、工具定义是不是散落在历史里、多轮请求是不是每轮都在重排上下文。这三处,是缓存命中率最常见的三个杀手,也是最容易捡到的三笔优化。
模型价格战还在继续,推理优化的红利最终会落到每一个应用开发者头上——但只有理解账单构成的人,才能真正把红利拿满。













暂无评论内容