深入解析KV Cache,以及怎么让你的 Agent 更省钱?
大模型API的计费机制背后藏着精妙的缓存设计。本文深入解析KV Cache与Prompt Cache两大核心技术逻辑,揭示为何缓存命中价格能低至常规输入的十分之一。从Transformer架构原理到实际API调用策略,手把手教你优化prompt结构,让你的AI应用成本直降90%。

如果你经常调用大模型 API,应该会注意到一个很有意思的现象:很多平台都会把输入 token 分成普通输入、缓存创建、缓存命中等不同类型,并且缓存命中的价格往往明显低于普通输入价格。
有些平台的缓存命中价格,甚至可以低到普通输入价格的十分之一左右。比如 Anthropic 的 Claude Prompt Caching 文档中,cache read tokens 按基础输入 token 价格的 0.1 倍计费;OpenAI 的 Prompt Caching 文档也提到,缓存可以显著降低延迟和输入 token 成本。
这就带来一个问题:
为什么同样是 token,有的按正常价格收费,有的命中缓存后价格却能低这么多?
更进一步,如果我们自己在做 Agent、编程助手、RAG 应用、自动化工作流,能不能主动利用这个机制,让 API 调用更便宜、更稳定?
要讲清楚这个问题,需要先区分两个概念:KV Cache 和 Prompt Cache。
一、先区分两个缓存:KV Cache 和 Prompt Cache
很多人看到“缓存命中”时,会下意识把所有缓存都理解成一种东西。但在大模型推理里,至少要区分两类缓存。
第一类是 KV Cache。
KV Cache 主要发生在一次模型生成过程中。模型在生成文本时,不是一次性把整段回答都生成出来,而是一个 token 一个 token 地往后预测。
每生成一个新 token,模型都需要回看前面已经出现过的上下文。如果每次都重新计算前面所有 token 的中间结果,成本会非常高。所以模型会把历史 token 在每一层 attention 中计算出来的 Key 和 Value 保存下来,后面继续生成时直接复用。
简单说:
KV Cache 是同一次生成过程里,用来避免重复计算历史上下文的缓存。
第二类是 Prompt Cache。
Prompt Cache 更接近我们在 API 计费里看到的“缓存命中”。它主要解决的是跨请求的重复前缀问题。
比如一个 Agent 每次调用模型时,都会带上:
- 固定 system prompt
- 工具说明
- 输出格式要求
- 项目背景
- 代码规范
- 用户当前问题
其中前面的大段内容可能每次都一样,只有最后的用户问题、工具结果、检索内容在变化。如果平台发现多个请求拥有完全相同或高度一致的前缀,就可以复用之前已经计算过的缓存,从而降低 prefill 成本。
OpenAI 文档中也提到,Prompt Caching 会自动作用于 API 请求,并且建议把静态内容放在 prompt 前面,把动态内容放在后面,以提高缓存命中率。 Google Gemini API 的 context caching 文档也把典型场景描述为:反复向模型传入相同 input tokens 时,可以通过缓存优化性能和成本。
简单说:
Prompt Cache 是跨请求复用相同前缀,减少重复 prefill 计算。
所以二者的区别可以这样理解:
- KV Cache:一次请求内部,生成后续 token 时复用历史 K/V。
- Prompt Cache:多次请求之间,相同前缀命中后复用已计算结果。
从开发者视角看,我们通常没法直接控制底层 KV Cache,但可以通过 prompt 结构设计,提高 Prompt Cache 的命中率。
二、如果不想看硬核原理,可以直接记住这句话
KV Cache 的原理相对硬核,如果你对这里的深入解释不感兴趣,可以直接看这一段总结:
大模型生成下一个 token,需要最后一个 token 的最终隐藏状态;
为了得到这个隐藏状态,模型要经过很多层 attention;
每层 attention 都需要当前 token 的 Q 去查询历史 token 的 K/V。
因为历史 token 看不到未来,所以它们的 K/V 算完后不会再变。
把这些 K/V 保存起来,后面生成新 token 时直接复用,就能避免重复计算。
这就是 KV Cache 的宏观原理。
三、稍微深入一点:KV Cache 到底在缓存什么?
Transformer 模型可以理解成很多层 Transformer Block 堆叠起来的结构。每一层里通常都有 attention、MLP、残差连接和归一化等模块。
在 attention 里,每个 token 的隐藏状态会被转换成三个向量:
- Q:Query,当前 token 想查询什么信息
- K:Key,历史 token 如何被找到
- V:Value,历史 token 能提供什么信息
生成下一个 token 时,模型会用当前最后一个 token 的 Q,去和它能看到的历史 token 的 K 做匹配,得到注意力权重,然后根据这些权重读取对应的 V。经过很多层这样的处理后,最后一个 token 的隐藏状态就融合了足够多的上下文信息。最后,模型把这个隐藏状态送入 lm_head,得到下一个 token 的概率分布。
这里有一个关键点:GPT 这类自回归模型有因果注意力机制。也就是说,一个 token 只能看到自己和它之前的 token,不能看到未来 token。
比如:
我 喜欢 吃 苹果
在这个序列中:
“我”只能看到“我”
“喜欢”能看到“我、喜欢”
“吃”能看到“我、喜欢、吃”
“苹果”能看到“我、喜欢、吃、苹果”
这意味着,当“吃”这个 token 的隐藏状态、K、V 算出来后,后面再生成“苹果”“。”或者更多内容,都不会改变“吃”原来的 K/V。
所以,历史 K/V 是可以被安全缓存的。
而且大模型通常有很多层 attention,每一层都有自己不同的 K/V。所以 KV Cache 缓存的不是最后一层的一个结果,而是:
- 每一层
- 每个 attention head
- 每个历史 token
- 对应的 K 和 V
这也是为什么长上下文会非常吃显存:上下文越长、层数越多、模型越大,KV Cache 占用就越高。
四、为什么不缓存 Q?
因为 Q 是“当前问题”,K/V 是“历史资料库”。
当模型生成第 101 个 token 时,需要的是第 101 个 token 自己的 Q,去查询前 100 个 token 的 K/V。
历史 token 的 Q 是它们当时作为“查询者”时用过的,完成那一层 attention 计算后,未来基本不再需要它们。
所以 KV Cache 不叫 QKV Cache,而是只缓存 K 和 V。
可以用一个比喻:
- Q = 当前提问
- K = 资料标签
- V = 资料内容
你现在有一个新问题,需要去资料库里找答案。资料库里需要保存的是标签和内容,而不是以前别人问过的问题。
五、那 Prompt Cache 为什么能让 API 价格下降?
Prompt Cache 省的是另一笔成本:重复 prefill 成本。
很多 Agent 请求的前缀很长,而且高度重复。比如编程 Agent 会反复带上系统提示词、工具说明、代码规范、项目结构、输出格式等内容。每次请求如果都重新 prefill 一遍,成本很高。
如果平台发现你这次请求的前几千、几万 token 和之前某次请求完全一致,就可以复用前面已经算好的缓存。这样,这部分输入 token 的成本就可以明显降低。
这也是为什么官方 API 往往会在统计里显示:
- cache creation tokens
- cache read tokens
- cached tokens
缓存创建表示这次新建了缓存;缓存命中表示这次复用了已有缓存。命中的部分通常会比普通输入便宜很多。
但要注意,Prompt Cache 不是永久记忆。缓存通常有生命周期,适合短时间内连续调用。你半个月后再调用同一个 Agent,不能指望上次的缓存还在。
所以它更适合这种场景:
- 启动 Agent
- 连续调用模型几十次
- 固定 system prompt 和工具说明不变
- 每次只在后面追加用户问题、工具结果、当前任务
而不是:
- 今天用一次
- 半个月后再用一次
- 还希望命中上次缓存
长期知识应该靠本地文件、数据库、RAG、项目索引保存;Prompt Cache 更像是短期加速器。
六、开发者真正应该注意什么?
官方 API 通常已经帮我们做了缓存命中处理,所以我们不需要自己手动保存底层 K/V。真正要做的是:让自己的请求结构更容易被命中。
最重要的一条原则是:
稳定内容放前面,变化内容放后面。
比如一个 Agent 的 prompt 可以这样组织:
固定前缀:
1. system prompt
2. agent 角色说明
3. 工具使用规则
4. 输出格式规范
5. 项目固定背景
6. 代码风格要求
7. 工具 schema
动态后缀:
1. 当前用户问题
2. 本轮检索结果
3. 工具返回结果
4. 当前时间
5. 临时任务参数
不要把时间戳、随机 ID、工具返回、RAG 检索结果放在最前面。因为只要前缀发生变化,缓存命中率就会下降。
还有几个实用建议:
第一,system prompt 不要频繁改。 哪怕只是多一个空格、换一种工具顺序、加一行动态时间,都可能影响前缀匹配。
第二,多轮对话尽量 append-only。 不要每一轮都把历史记录重新总结、重排、改写。如果摘要每次都变化,虽然语义差不多,但 token 序列已经不同,缓存命中会变差。
第三,RAG 内容放后面。 检索结果每轮都不同,不要让它污染固定前缀。
第四,工具 schema 尽量稳定。 Agent 工具列表、参数结构、输出格式最好固定,避免每次动态生成一套不同的工具说明。
第五,批量任务按相同前缀分组。 比如处理 20 个文件,不要每个文件都构造完全不同的 prompt,而是保持前面的系统规则、项目背景、输出格式一致,只替换后面的文件内容。
第六,关注连续工作过程中的命中率,不要太在意隔很久后的命中。 缓存的价值主要在一次连续任务中体现,比如 Claude Code、Codex、Cursor、OpenClaw 这类 Agent 连续调用模型时,稳定前缀会带来很明显的收益。
七、为什么有些“token 经销商”反而更贵?
很多人会发现,直接使用官方 API 时,缓存命中率很高;但换成某些第三方路由 API 或中转 API 后,明明标价看起来不高,实际消耗却更快。
这背后可能有几个原因。
第一,路由 API 可能把请求打散到不同上游。
同样一个模型名称,背后可能接的是不同供应商、不同区域、不同账号、不同实例。Prompt Cache 往往依赖同模型、同服务环境、相同前缀以及稳定路由。请求如果每次随机落到不同上游,缓存就很难连续命中。
第二,路由层可能没有透传缓存参数。
有些厂商支持专门的缓存控制字段,比如 Anthropic 的 prompt caching 机制。如果中转服务为了统一接口,把这些字段丢掉或改写,缓存收益就可能下降。
第三,路由层可能改写你的 prompt。
比如它在请求前面加自己的系统提示、时间戳、安全规则、供应商标识或兼容层包装。只要这些内容出现在前缀位置,就可能破坏原本的精确前缀匹配。
第四,即使上游命中了缓存,折扣也不一定返还给你。
第三方服务商可能拿到了上游缓存优惠,但仍然按自己的统一价格向用户收费。所以判断一个路由 API 是否真的省钱,不能只看模型单价,还要看它是否透明展示:
cache creation tokens
cache read tokens
cached tokens
缓存命中率
是否支持固定 provider
是否支持透传 cache_control / prompt_cache_key
如果一个平台只告诉你总 token 和总费用,却不展示缓存命中,那么它的缓存收益就是不可控的。
八、最后总结
大模型 API 的缓存命中价格之所以低,是因为模型厂商确实减少了重复计算。
KV Cache 解决的是一次生成内部的重复计算:历史 token 的 K/V 算过后不会再变,后面生成新 token 时可以直接复用。
Prompt Cache 解决的是多次请求之间的重复前缀:如果 system prompt、工具说明、项目背景等静态内容完全一致,就可以复用之前 prefill 的结果,从而降低输入成本和首 token 延迟。
对开发者来说,不需要手动管理底层 KV Cache,真正应该做的是优化 prompt 结构:
稳定内容放前面
- 动态内容放后面
- 系统提示词少改
- 工具 schema 保持一致
- RAG 和工具结果不要污染前缀
- 连续任务中保持会话结构稳定
- 优先选择官方 API 或支持缓存透明统计的路由 API
一句话总结:
缓存命中不是玄学,而是大模型推理成本结构的直接体现。谁能让重复前缀更稳定,谁就更容易拿到低价缓存命中;谁的请求被路由、改写、打散,谁就更容易按普通输入价格付费。
本文由 @爱研究的乐声 原创发布于人人都是产品经理。未经作者许可,禁止转载
题图来自Unsplash,基于CC0协议

起点课堂会员权益





如果用户在多轮对话中反复微调系统提示词里的某个规则,比如修改工具输出格式,那么前缀就会频繁变,是不是意味着这类场景基本告别缓存了?有没有办法做语义级别的缓存匹配?