看到模型价格表里的 Input、Output 和 Cache 分别代表什么?啥叫缓存命中率?缓存如何帮你省钱的?
1. 三种价格分别买了什么
与Agent的一次聊天请求通常包含 系统提示词 、 工具定义 、 历史消息 和 本轮问题 ;模型随后逐步生成回答。
普通输入是这次需要处理的输入 Token;输出是这次生成的 Token;缓存读取是输入中在 prefill 阶段命中了可复用前缀的部分。它依然在模型的上下文里,也依然可能计费,只是通常按另一档价格算。输出没有因为输入命中缓存而自动免费。
最简单的账单可以写成:
费用 = 普通输入量 × 输入单价 + 缓存读取量 × 缓存读取单价 + 输出量 × 输出单价
但这不是所有平台的完整公式。有些厂商还对缓存写入单独计费;显式缓存还可能有存储时长费用。例如 Claude 的缓存定价区分写入与读取;Gemini 的显式缓存还考虑缓存的 Token 数和保存时长。中转站显示的 Cache Price 可能只对应其中一项,最好对照其账单字段,而不是从一个数字猜上游如何收费。
输出一般比输入贵,是因为生成会一个 Token 接一个 Token 进行,而大量输入的预处理能更并行地完成。但具体比例仍由模型和服务商定价决定。
2. Prompt Cache 和 KV Cache 的区别
模型处理一段输入时,会算出注意力机制要用到的 Key、Value 中间状态,简称 KV。在同一次生成中,保存这些状态可以避免每生成一个新 Token 就把前文重新计算一遍;这就是常说的 KV Cache。
Prompt Cache 是面向 API 用户的“重复前缀复用”能力与计费概念:下一次请求如果有相同且仍可用的输入前缀,服务端可能复用先前为它计算的状态。实现上常借助 KV 状态或前缀缓存,但用户能看到的是缓存命中 Token 和相应价格,而不是自己管理 GPU 内存。vLLM 的前缀缓存设计就是一个直接复用 KV 块的实现例子。
这里缓存的不是“问题的答案”。相同的系统提示词和文档后面接上不同问题,模型仍要处理新的部分并重新生成回答。它也不等于长期记忆或向量数据库。
3. 缓存的命中条件
把两次请求排在一起看,命中的关键通常是从开头连续相同:
系统提示词、工具定义、长文档保持原样,新问题放在后面,这段公共前缀才有复用机会。若把时间戳、随机 ID 或每轮都变的摘要塞在最前面,共同前缀会提前断开。多模态输入、工具定义顺序和模型参数也可能参与匹配;最终以提供商的实现为准。
“前缀一样”还不保证命中,至少要注意下面四道门槛:
| 条件 | 实际含义 |
|---|---|
| 最低长度 | 太短的前缀通常不缓存。比如 OpenAI 文档列出 GPT-5.6 及以后模型的可见输入前缀门槛为 1,024 Token;Claude 文档显示门槛因模型而异;Gemini 2.5 Flash的隐式缓存门槛为 2,048 Token。 |
| 有效期 | Claude默认 5 分钟,可选择收费不同的 1 小时;OpenAI的期限按模型与保留设置变化;Gemini 显式缓存的默认 TTL 为 1 小时且可设置。不要把某家的 TTL 套到所有平台。 |
| 服务端容量与路由 | 缓存会占服务端资源,可能过期、被逐出,或者请求落到没有该缓存的机器。OpenAI 文档也说明机器位置与路由会影响复用。 |
| 上下文窗口 | 缓存不扩大模型的上下文上限。缓存内容仍计入请求的上下文 Token;把一大篇无关资料长期塞进去,只因为能命中缓存,仍可能拖累质量并挤掉真正需要的信息。Gemini 文档明确说明其缓存 Token 计入 Token 限额。 |
有人会问“缓存占多少 MB”。这没有一个由输入 Token 数直接决定、跨模型通用的常数:KV 状态的大小还和层数、隐藏维度、注意力结构、精度及服务端实现有关。作为 API 用户,通常也看不到自己占了多少显存。更实用的两项指标是:输入前缀有多少 Token,以及响应里实际命中了多少 Token。缓存容量与驱逐属于服务端资源管理,不等于你的上下文窗口大小。
4. 怎样整理请求,才能提高命中率和降低总价
先做一件最有效的事:把长期稳定且确实必要的内容放前面,把本轮变化的内容放后面。比如把角色规则、固定工具定义和同一份参考文档作为公共前缀;用户本轮问题、当前时间和检索结果放在尾部。让工具列表、JSON 序列化和文档版本保持稳定,避免无意义的重排。
多轮对话尽量采用“保留原历史、在末尾追加”的方式。若每一轮都改写开头的系统提示词,或者在最前面重写一份滚动摘要,原本可复用的前缀就会缩短。需要压缩历史时,可以在一个清楚的边界做一次摘要,接受那次缓存重新建立,随后让新前缀稳定下来。
长文档问答、Coding Agent 和固定工具集的任务往往更有收益,因为同一段前缀会被反复读取。短促、互不相关的请求,或间隔远超 TTL 的请求,通常不会因为“打开缓存”就显著省钱。频繁切换模型或中转站上游渠道,也可能让请求不再复用同一处缓存。
价格上还要算写入成本。举个只计算公共前缀的假设:普通输入为每百万 Token 10 元、缓存写入 12.5 元、缓存读取 1 元;一段 1 万 Token 的公共前缀连用两次。不缓存的前缀费用是 0.01 × 10 × 2 = 0.20 元;第一次写入、第二次读取是 0.01 × 12.5 + 0.01 × 1 = 0.135 元。这个例子省钱,但只用一次就不会。新问题与输出的费用在两种方案里相同,所以这里没算进去。真实平台还可能有存储费、不同门槛或不同写入规则。
缓存命中率不是唯一目标。如果为追求高命中率,把不相关的 5 万 Token 固定前缀带到每次请求中,即使读取单价很低,总费用仍可能高于只发送必要的 2 千 Token。优先减少无用上下文,再优化重复部分的命中。
5. 上下文管理中,哪些操作容易把缓存“弄坏”
下面这些做法值得在应用代码里逐一检查:
- 前缀开头插入动态值:时间戳、请求 ID、实时余额、随机示例放在最前面,会让后面相同内容难以成为可复用前缀。
- 无意义地改写旧消息:每轮都重新排版、裁剪或重排历史消息,可能改变 Token 序列;在末尾追加更稳定。
- 工具定义顺序漂移:同一组工具由无序集合生成,或 schema 字段顺序随进程变化,会破坏稳定部分。
- 频繁替换系统提示词:措辞微调、动态署名、把本轮检索结果拼在开头,都会让缓存失效范围扩大。
- 把摘要当作可无损压缩:摘要能减少 Token,但会丢细节;需要复用精确信息时,应保留原文或重新检索。缓存命中也不能替代上下文取舍。
- 只看价格表,不看 Usage:中转站可能切换上游、改变字段或统计口径。价格表写了 Cache Price,不代表当前请求一定命中。
还有一个容易混淆的边界:缓存命中不等于请求可以省略原始内容。自动前缀缓存一般仍要求客户端发送对应输入,服务端识别后复用计算;显式缓存则可能让你通过缓存对象 ID 引用那段内容。两种接口方式不要混用。
6. 用 Usage 和账单验证,而不是凭感觉
先固定模型、上游渠道和一段足够长的公共前缀,连续发两次请求,只修改末尾问题。保存每次响应的输入、输出、缓存写入与缓存读取 Token 计数,再和账单对照。比如 OpenAI 返回缓存输入计数,Claude 区分 cache_creation_input_tokens 与 cache_read_input_tokens,Gemini 在 usage_metadata 中报告缓存命中;具体字段随接口版本而变化,先看你实际收到的响应。OpenAI · Claude · Gemini
可以把“缓存读取 Token ÷ 总输入 Token”当作观察指标,再同时记录每次总费用和回答质量。如果第二次读取数仍为零,按顺序查:前缀长度、内容和顺序是否一致、是否过期、模型或渠道是否变了、接口是否支持缓存、Usage 是否被中转站完整透传。
说到底,Prompt Cache 让模型省掉重复读同一段前文的计算;它不会替你决定哪些前文值得带上。真正省钱的上下文管理应该是:删掉不需要的内容,让需要重复的内容保持稳定。