一句话结论:几乎所有云端 LLM API 的服务端缓存,本质都是「前缀缓存(Prefix Caching)」——它由 Transformer 的因果注意力机制决定,只能缓存从序列开头开始、逐字节相同的一段前缀。各家真正的分歧不在「缓存什么」,而在两个维度上:缓存是隐式默认开启还是需要显式声明断点,以及消息流是无状态(客户端每轮重发)还是有状态(服务端维护历史)。本文用具体 API 示例把这两条主线讲透。

为什么上下文管理会成为一个问题

现代 Agent 的工作方式是一个循环(Agent Loop):模型输出一个工具调用 → 客户端执行 → 把结果拼回对话 → 再次请求模型 → 如此往复几十上百轮。编程类 Agent 尤其如此,一次任务里读几十个文件、跑测试、看 diff,上下文轻松涨到几百 KB。

这里藏着一个容易被忽视的成本结构:在无状态 API 下,Agent Loop 的每一轮都要把「到目前为止的全部历史」重新发一遍。第 50 轮请求携带的 tokens ≈ 前 49 轮的全部内容 + 新增的一点点。如果没有缓存,这几百 KB 的前缀会在每一轮都被模型从头「预填充(prefill)」一次——既烧钱,又拖慢首 token 延迟(TTFB)。

缓存机制就是为了解决这个「重复预填充」问题而生的。要理解各家的取舍,得先从底层原理讲起。

缓存的本质:为什么只能缓存「前缀」

因果注意力决定了缓存的形状

Transformer 在自回归生成时使用因果注意力掩码(causal attention mask):位置 N 的 token 只能 attend 到位置 0..N 的 token。这意味着,位置 N 处那份用于加速推理的 Key/Value 张量(即 KV Cache),是由「它自己 + 它前面所有 token」共同算出来的。

由此推出一个刚性约束:

只要前缀里任何一处 token 改变,从该位置往后所有 token 的 KV 全部失效,必须重算。

这也解释了为什么不能缓存消息队列的中间或末尾:一段文字的 KV 依赖于它前面的完整上下文,换一段前缀,同样这段文字算出来的 KV 就不同。缓存只能从「位置 0」开始,向后延伸到某一点为止——它天然是「前缀形」的。

vLLM 的 Automatic Prefix Caching 正是把这个原理固化进了缓存 key 的设计:每个 KV block 的哈希 = 「block 内的 token」+「block 之前全部前缀 token 的哈希」,形成一条链式哈希——block 3 的 hash 含 block 2 的 hash,block 2 又含 block 1……只有 token 历史完全相同的 block 才能共享 KV。

SGLang 的 RadixAttention 更进一步,把所有请求的 KV cache 组织进一棵 radix tree(压缩前缀树),用 LRU 管理,在数据结构层面就只对公共前缀做匹配与复用。这是「前缀缓存」这个概念在工程上的两种主流实现。

message、block 与断点:为什么只标「结尾」

以 Anthropic 的 Messages API 为例,理清几个概念的对应关系:

  • 一条 messagerole(user/assistant)和 content
  • content 可以是字符串,也可以是一个 content block(part)数组,每个 block 有自己的 typetext / image / tool_use / tool_result / document);
  • 缓存断点 cache_control 是打在单个 block 上的,粒度是 block,不是整条 message。

Anthropic 官方文档对断点语义的描述很关键:

Prompt caching references the entire prompt — tools, system, and messages (in that order) up to and including the block designated with cache_control. … Cache writes happen only at your breakpoint.

也就是说,拼接与哈希顺序固定为 tools → system → messages,是一个层级化的前缀;在某个 block 上打断点,含义是「从最开头到包含该 block 的整段前缀,写入一条缓存」。

这就回答了一个常见困惑:为什么断点只需标记「结尾」,不需要标记「开头」? 因为缓存的对象永远是「从位置 0 开始的前缀」,起点恒为 0、是隐含固定的,唯一需要你指定的信息就是「缓存到哪里为止」。标「开头」没有任何意义。

一个完整的、在合适位置打断点的请求示例(把断点放在「稳定前缀的最后一个 block」上,让高频变化的最新用户输入留在断点之后):

{
  "model": "claude-opus-4-8",
  "max_tokens": 1024,
  "tools": [
    {
      "name": "get_weather",
      "description": "查询天气",
      "input_schema": { "type": "object", "properties": { "city": { "type": "string" } } },
      "cache_control": { "type": "ephemeral" }
    }
  ],
  "system": [
    { "type": "text", "text": "你是一个企业知识库助手。" },
    {
      "type": "text",
      "text": "<很长的企业规章/文档,几千 token……>",
      "cache_control": { "type": "ephemeral" }
    }
  ],
  "messages": [
    { "role": "user", "content": "第一轮问题" },
    { "role": "assistant", "content": "第一轮回答" },
    {
      "role": "user",
      "content": [
        { "type": "text", "text": "第二轮的上下文补充……" },
        {
          "type": "text",
          "text": "这是我最新的问题。",
          "cache_control": { "type": "ephemeral" }
        }
      ]
    }
  ]
}

三个断点分别缓存了三段递增的前缀:tools / tools + system / tools + system + 到此为止的全部对话。多轮对话时,把断点「滚动」放在当前最新一条消息上,此前的历史就能整体命中缓存。Anthropic 限制每个请求最多 4 个断点

一个实践陷阱:任何让前缀逐字节改变的东西都会静默击穿缓存——system prompt 里插了 datetime.now()、JSON 没有 sort_keys 导致 key 顺序不定、每轮换了工具集……都会让 cache_read_input_tokens 恒为 0。缓存是「精确前缀匹配」,不是「语义相似」。

KV 缓存 vs Prompt 缓存:别把两个层次搞混

很多讨论把 “KV Cache” 和 “Prompt Caching / 上下文缓存” 混为一谈,其实它们是两个层次的东西——后者是建立在前者之上的产品化功能。

KV 缓存(KV Cache)Prompt 缓存 / 前缀缓存
是什么Transformer 推理的底层机制:生成时把已处理 token 的 Key/Value 张量存下来,避免每生成一个新 token 就重算整段注意力建立在 KV 复用之上的产品/API 功能:把某段公共前缀的 KV 跨请求保存并复用
作用范围单次生成内部(intra-request)——一次请求内逐 token 生成时复用跨请求之间(inter-request)——多轮对话/多次请求间复用同一前缀
是否常开永远开启,是 Transformer 高效推理的基础,无所谓「命中率」按策略开启(隐式/显式),有命中/未命中之分
存哪里GPU 显存(HBM),生成结束通常即释放有 TTL;可分层落到 DRAM,甚至 SSD(DeepSeek)
生命周期瞬态,随本次生成结束而消失分钟→小时→天,可持久化
计费不单独计费——它就是「推理本身」的一部分有独立的命中折扣、写入/存储费

一句话理清关系:Prompt 缓存 = 把 KV 缓存从「单次生成内的瞬态加速」升级为「跨请求持久复用」。 你在 API 账单上看到的 cache_read / cache hit,复用的正是「上一次请求为这段前缀算好的 KV 张量」。由此:

  • DeepSeek 说的「把 KV cache 放到磁盘」,字面意思就是把 prompt 缓存要复用的那份 KV 张量持久化到 SSD——它是「prompt 缓存的存储层实现」,本质仍是 KV。
  • Anthropic 的 cache_control 断点,标记的是「这段前缀的 KV 值得跨请求存下来」。
  • 第一节讲的「因果注意力 → 只能缓存前缀」这条约束,对两个层次同时成立:无论单次生成内的 KV 复用,还是跨请求的 prompt 缓存,都只能沿前缀方向复用。

两大流派:隐式缓存 vs 显式缓存

既然大家底层都是前缀缓存,第一个真正的分野就是:这个缓存要不要你手动声明?

显式派:由你标记断点

代表是 Anthropic。它的缓存默认不开启——你必须在 content block 上主动加 cache_control: {"type": "ephemeral"},服务端才会在那个断点写缓存。好处是控制精细:你能精确决定哪几段前缀(工具定义、长文档、对话历史)各自作为独立的缓存层,按不同的变化频率复用;代价是要手动管理断点,且有最小前缀门槛(不同模型 1024 / 2048 / 4096 token 不等,低于门槛静默不缓存)。

针对四家核心提供商,问题「谁需要显式开启缓存」的答案很清晰:OpenAI、Gemini(隐式档)、DeepSeek 都默认自动开启;唯独 Anthropic 需要显式声明 cache_control

隐式派:默认自动、零代码

其余大多数家走的是隐式前缀缓存:无需任何参数,服务端自动识别高频复用的前缀并缓存、命中即打折。

  • OpenAI:对 ≥1024 token 的 prompt 自动生效,以 128 token 为增量递增匹配前缀。可选 prompt_cache_key 参数把同类请求路由到同一缓存以提高命中率。(文档
  • Gemini:2.5 系列默认开启隐式缓存(Flash 门槛 1024 / Pro 门槛 2048 token),2025-05-08 上线。(发布博客
  • DeepSeek:2024-08-02 上线的「Context Caching on Disk」,官方原文 “runs automatically… no code or interface changes”,从 token 0 起自动做最长公共前缀匹配。(公告
  • xAI Grok:所有 Grok 模型自动缓存未变化的前缀,推荐用 x-grok-conv-id 提高命中。(文档
  • 智谱 GLM:「隐式缓存,智能识别重复的上下文内容,无需手动配置」。(文档

有意思的中间态:Qwen 的双轨,与 Kimi 的历史转向

两家值得单独拎出来,因为它们体现了这个设计空间的边界。

阿里 Qwen / 百炼同时提供隐式与显式两套,文档写得最清楚(Context Cache 文档):

维度隐式缓存(默认)显式缓存
开启方式自动开启,无法关闭手动,用 cache_key 控制
创建/写入费输入标准价的 100%输入标准价的 125%
命中折扣0.2×(省 80%)0.1×(省 90%)
最小 token2561024
TTL不确定,定期清理5 分钟,命中后重置
命中确定性best-effort确定性命中

这张表精确地量化了显式缓存的取舍:多付 25% 的写入费,换来更深的命中折扣(0.1× vs 0.2×)与确定性命中——本质上是「用一次性成本买缓存保证」。

月之暗面 Moonshot / Kimi 则展示了整个行业的演化轨迹。它在 2024-07-01 首创了一套罕见的「显式付费存储」模型(公测博客):你要主动创建 cache 对象、用 ttl(秒)设生存期、可 reset_ttl 续期,是有状态的。计费分三段:

  • 缓存创建:24 元 / M tokens
  • 缓存存储:10 元 / M tokens / 分钟(按存储时长计费——这是全行业几乎独一份的设计)
  • 缓存调用:0.02 元/次 + 增量 token 原价

官方案例:9 万字文档存 10 分钟内问 40 个问题,费用约 11.88 元、省约 92%,首 token 延迟从 ~30s 降到 ~1s。

但到 2026 年,Kimi 官方文档已经转向自动/隐式前缀缓存现行文档):「系统自动识别并缓存高频使用的初始上下文」,无需创建对象、无需管理 TTL,前一请求 prompt tokens > 256 即自动缓存。Moonshot 从「显式付费存储」收敛到「隐式前缀缓存」,正是整个行业向自动化路线趋同的一个缩影。

⚠️ 别把 2024 的「创建 24 元 + 存储 10 元/M/分钟」当成现价——那套定价已被归档到旧文档路径。引用时务必标注为「Moonshot 2024 年的历史做法」。(顺带澄清一个常见混淆:Moonshot 与 Kimi 是同一家公司,本文统一记为「月之暗面 Moonshot / Kimi」。)

更多双轨玩家:MiniMax 与百度

MiniMax 也走「隐式 + 显式」双轨(Prompt 缓存文档Anthropic 兼容缓存):

  • 隐式:官方就叫「Prompt 缓存」,被动、默认开启、无需改接口,基于 messages 前缀自动匹配;触发门槛输入 ≥ 512 token,命中按标准输入价约 20% 计费(省约 80%)。适用 M2.5 / M2.7 / M3 等系列。
  • 显式:直接兼容 Anthropic 的 cache_control 手动打点({"type": "ephemeral"}),生命周期 5 分钟、命中自动续期不额外收费;响应字段也照搬 Anthropic 的 cache_read_input_tokens / cache_creation_input_tokens

两者都是无状态的。官方缓存文档只覆盖 M2 / M2.1 / M2.5 / M2.7 / M3 系列,abab6.5、MiniMax-Text-01、MiniMax-M1 未列入,确切上线年月官方也未公布。

百度的情况最特别——它的两套缓存分属不同产品、面向不同模型家族文心 prompt cache千帆前缀缓存):

  • 文心侧「prompt cache」(隐式):对所有用户默认开启、无需改代码,命中 token 按 prompt_tokens 单价 40% 计费(即省 60%);但官方明确只列了 ERNIE-4.0-Turbo-8K 一个模型,TTL 未给具体值。
  • 千帆侧「前缀缓存」(显式):需主动调 POST /v2/caching 创建缓存对象、拿 cache_id 引用,ttl 秒级可控且每次调用滑动续期;命中折扣按模型 10%~25%。但适用列表里只有 DeepSeek 系列(v3.2 / v3.1 / v3 / r1 等),不含 ERNIE

百度的对话主接口 POST /v2/chat/completions 是无状态的;显式「前缀缓存」提供的是一个有状态的缓存对象(服务端存内容、cache_id 引用,含 create/query/delete 全套),但这仍是「缓存对象」而非「有状态多轮会话」——未见百度提供类似 OpenAI Responses 那样按 conversation id 续聊的有状态对话接口

⚠️ 两处需标注不确定:① 有第三方称 ERNIE-4.5-Turbo / X1-Turbo 也支持上下文缓存,但百度官方「前缀缓存」文档正文只列 DeepSeek、未直接确认 ERNIE 型号;② 百度是否有有状态对话 API,官方文档未见对等能力。

横向大对比:缓存策略

把上面这些散点汇成一张表(数据来源见文末;标注「—」表示官方未公开确切数值,请以各家定价页为准):

提供商默认开启?隐式/显式命中折扣写入溢价存储费最小前缀上线时间
OpenAI隐式gpt-4o 50% / gpt-4.1 75% / GPT-5 ~90%旧模型无;GPT-5 系 1.25×10242024-10
Anthropic❌ 需显式显式 cache_control~0.1×(省 90%)5m TTL 1.25× / 1h TTL 2×1024/2048/40962024-08
Gemini 隐式隐式2.5 系 90%(2.0 为 75%)Flash 1024 / Pro 20482025-05
Gemini 显式❌ 需创建显式 CachedContent90%(有保证)~$0.50/M 写入~$1.00/M/小时
DeepSeek隐式(磁盘 KV)~0.1×(省 90%)64 token 粒度2024-08
xAI Grok隐式~80–85%(cached ≈$0.20/M)未公开
智谱 GLM隐式文档示例 ≈50%~512
Qwen 隐式✅ 强制隐式0.2×256
Qwen 显式显式 cache_key0.1×25%1024
MiniMax 隐式隐式 Prompt 缓存~0.2×(省 80%)512未公开
MiniMax 显式显式(Anthropic 兼容 cache_controlread 优惠价未公开
百度·文心隐式 prompt cache0.4×(省 60%)2025-04(仅 ERNIE-4.0-Turbo-8K)
百度·千帆❌ 需创建显式(cache 对象)0.1×~0.25×2026-01(仅 DeepSeek 系)
Kimi(2026)隐式见定价页2562024→2026 演化
Kimi(2024 历史)显式有状态增量原价 + 0.02元/次24元/M 创建10元/M/分钟2024-07

几个值得注意的观察:

  1. 命中折扣普遍在 90% 左右(0.1× 输入价),这是「缓存读只需从显存/存储取 KV、无需重算」的成本反映。OpenAI 发布时的 50% 是早期偏保守的数字,新模型已追平到 75%–90%。
  2. 只有两类设计会额外收费:写入溢价(Anthropic 长 TTL、OpenAI 的 GPT-5、Qwen 显式)与存储费(Gemini 显式、Moonshot 2024)——它们都对应「更强的缓存保证」。
  3. 绝大多数隐式缓存不收写入费、不收存储费,因为它们是 best-effort 的性能优化,缓存随时可能被 LRU 驱逐,厂商不为一个「不保证存在」的东西向你收存储费。

TTL:KV 缓存与消息历史,能不能由你控制?

这里要区分两种不同的「存活时间」,它们常被混为一谈:

  • KV 缓存的 TTL:那份加速用的 KV 张量在服务端能活多久。
  • 消息历史的 TTL:在有状态 API 下,服务端替你保存的对话原文能活多久。

KV 缓存的 TTL:基本不可控(Anthropic 是例外)

大多数隐式缓存的 KV TTL 是服务端自动管理、用户不可控的:

  • OpenAI:不活跃 5–10 分钟清除,最长约 1 小时;部分「扩展保留」模型可达 24 小时。GPT-5.6+ 引入了 prompt_cache_options.ttl,但目前唯一可选值就是 "30m"——所以实质上仍不可自定义。
  • DeepSeek:未使用的缓存条目「几小时到几天」后按需清除,无参数可控。
  • Qwen:隐式档不确定;显式档固定 5 分钟,命中后重置计时。
  • Gemini 隐式:best-effort,不承诺 TTL。

Anthropic 是唯一把 KV TTL 做成一等公民、可由用户显式选择的

"cache_control": {"type": "ephemeral"}              // 5 分钟 TTL(默认)
"cache_control": {"type": "ephemeral", "ttl": "1h"} // 1 小时 TTL

代价体现在写入溢价上:5 分钟 TTL 写入 1.25× 基础输入价,1 小时 TTL 写入 2×,而命中读取都只要 ~0.1×。经济学很直白:5 分钟档两次请求就回本(1.25× + 0.1× < 2×),1 小时档需要至少三次读取才划算——长 TTL 适合有间隔的突发流量,用双倍写入费换「跨越流量间隙不失效」。

消息历史的 TTL:只在有状态 API 下才存在

而「消息历史 TTL」这个概念,只对有状态 API 才有意义——这就自然引出了第二条主线。

无状态 vs 有状态:消息流模型的分野

无状态:客户端每轮重发全部历史

这是绝大多数 API 的经典设计,包括 OpenAI Chat Completions、Anthropic Messages、DeepSeek、以及所有走 OpenAI 兼容 chat/completions 的国产 API

其契约是:服务端不替你保存任何对话状态,客户端必须在每次请求前,把「到目前为止的全部 messages」重新拼好发过去。服务端的前缀缓存只是底层性能优化(命中即降价 + 降延迟),并不构成「有状态会话」——缓存可能被驱逐、可能跨机器不可用,它是加速层,不是存储层。

# 无状态:每一轮都要携带完整历史
messages = [
    {"role": "system", "content": "..."},
    {"role": "user", "content": "第一个问题"},
    {"role": "assistant", "content": "第一个回答"},   # 上一轮的输出,客户端自己存
    {"role": "user", "content": "第二个问题"},         # 新增
]
resp = client.chat.completions.create(model="...", messages=messages)
# 第三轮:把 resp 的输出再 append 回 messages,整个数组重发

有状态:服务端维护历史,客户端只传增量

2025–2026 年,几家先后推出了有状态的新 API,改变了这个契约:

  • OpenAI Responses API(2025-03-11 发布):默认 store=true,服务端自动保存响应对象;下一轮只需传 previous_response_id + 新增消息,无需重发完整 transcript。它还跨轮次保留模型的推理状态(reasoning items)——这是 Chat Completions 做不到的,因为后者每次调用之间会丢弃推理过程。官方称因保留推理,缓存利用率提升 40–80%。(博客
  • xAI Grok 也提供了对齐 OpenAI 的 Responses API,同样用 previous_response_id 接续。
  • Google Gemini 在 2026 年推出了 Interactions API(类比 Responses):store=true 让服务端保存对话,用 previous_interaction_id 续接;store=false 退回无状态。而旧的 generateContent 本身是无状态的。(Interactions 文档
# 有状态(OpenAI Responses):只传增量 + 上一条响应 id
r1 = client.responses.create(model="...", input="第一个问题")     # store=true(默认)
r2 = client.responses.create(
    model="...",
    previous_response_id=r1.id,      # 服务端自己接上历史
    input="第二个问题",              # 只传新增,不重发历史
)

服务端到底存了什么:原文,还是只存哈希?

一个精妙的问题:既然缓存靠的是「前缀哈希」,那有状态 API 的服务端,是不是只存一个「用于查 KV cache 的滑动窗口哈希」就够了?

答案是否定的——服务端存的是原始消息内容(输入 + 输出 + 中间工具结果),不是只存哈希。 OpenAI 的 Conversation state 指南 与 Azure OpenAI 文档一致表明:store=true持久化 response 对象本身(默认保留 30 天,可在 dashboard 查看、可用 previous_response_id 检索取回)。

从「重建 prompt 必须要有原文」的角度,只存哈希在原理上不可行:

  1. 下一轮请求要把历史拼回完整 prompt 再送进模型,而 KV cache 可能早已被 LRU 驱逐、或跨机器不可用——缓存是尽力而为的优化,不是可靠存储。
  2. 哈希是单向的,无法从哈希还原出原始 token/文本,也就无法用它重建 prompt。
  3. 正确的分层是:原始消息(真相来源,必须存原文)+ KV/前缀哈希(可选的性能加速索引,可失效、可重建)。哈希只是加速前缀匹配的索引,绝不能替代原文存储。

这也顺带回答了「消息历史 TTL」的问题:在 Responses / Interactions 这类有状态 API 下,服务端存的原文有明确的保留期(OpenAI 默认 30 天),且这是可由 API 控制的(store 开关)——这与那个不可控、随时可能被驱逐的 KV 缓存 TTL 是两码事。

DeepSeek 的磁盘 KV:一个被 MLA 解锁的选项

DeepSeek 的 Context Caching on Disk 有两个反常识的设计,值得单独剖析,因为它们回答了两个「为什么别家不这么做」。

为什么别家不把 KV Cache 放到 SSD 里?

直觉上,把 KV 缓存放进廉价的 SSD、扩大缓存容量、从而让更多请求命中低价,似乎人人都该做。但这在大多数模型上并不划算,核心是 KV 体积与 IO 带宽的经济学

  • KV offload 是否值得,取决于「从外部存储加载能否比 GPU 重算更快」,读写带宽是决定因素
  • 小传输块严重浪费带宽:对典型 512-byte 的 KV entry,有效带宽可跌到峰值的 <6%。即便用 GPU Direct Storage,从 SSD 恢复 KV 造成的 GPU 气泡也可占总推理延迟 70% 以上(见 KV Cache 综述 arXiv:2412.19442 及后续 SSD-backed KV 工作)。

一句话:KV 越大,磁盘方案越容易「加载比重算还慢」,得不偿失。

而 DeepSeek 能这么做,是 MLA(Multi-head Latent Attention)架构的直接红利。MLA 对 KV 做低秩联合压缩,只缓存一个压缩后的 latent 向量:DeepSeek-V3 约 70 KB/token,而标准 GQA 类模型约 192–328 KB/token,压缩约 2.7–4.7×(DeepSeek-V2 技术报告 arXiv:2405.04434)。DeepSeek 官方博客直接点明了这层因果:

made possible by the MLA architecture in DeepSeek V2, which significantly reduces the size of the context KV cache, enabling efficient storage on low-cost disks.

每个 token 的 KV 被压得足够小,从分布式磁盘读回来的带宽/延迟才算得过账——这也是它敢把命中价压到未命中 1/10 的技术底座:cache hit = $0.014/M vs cache miss = $0.14/M(人民币口径 0.1 元 vs 1 元),差整整一个数量级。GQA(如 Llama)靠减少 KV 头数省显存,MLA 靠压缩每个 KV 的内容省——只有后者把每 token 数据量压到临界点以下,「落盘」这个选项才在带宽上翻盘。所以这不是别家想不到,而是没有 MLA 就没有可行的落盘经济性

顺带一提,压缩 KV 不止 MLA 一条路。MiniMax 的 MiniMax-01 / M1 用了 Lightning Attention(线性注意力)混合架构(每 8 层 7 层线性 + 1 层 softmax),线性注意力让 KV 的存储开销降到常数级(不随序列长度线性增长),官方称 100k token 场景显存降约 83%。这是与 MLA 并列的另一条「让 KV 变小」的架构路线。但要诚实区分:这是模型架构层面的 KV 效率,与「API 是否提供前缀缓存折扣」是两回事——官方并未把二者挂钩,别把架构红利直接当成缓存功能的成因。

为什么 DeepSeek 不引入 Anthropic 那样的显式断点?

既然 DeepSeek 已经把 KV 落了盘,为什么不像 Anthropic 那样提供 cache_control 让用户精细控制?

因为二者解决的是不同层次的问题,且路线相互排斥:

  • DeepSeek 走的是「全自动、从 token 0 起最长公共前缀匹配」(最小 64 token 粒度,TTL 几小时到几天,无写入费、无存储费、无参数)。它把缓存做成一个对用户完全透明的底层优化——你什么都不用改,命中就自动降价。响应里用 prompt_cache_hit_tokens / prompt_cache_miss_tokens 两个字段告诉你命中了多少。
  • Anthropic 的显式断点,是为「同一段前缀有多个变化频率不同的层」而设计的——工具定义、长文档、对话历史各打一个断点,各自复用。这套精细控制的前提是「用户愿意管理断点、且愿意为长 TTL 付写入溢价」。

DeepSeek 的产品哲学是「缓存应该像 CDN 一样隐形」:既然磁盘容量足够大、命中匹配足够激进,就没必要把复杂度暴露给用户。显式断点在「精细控制」上更强,隐式全自动在「零心智负担」上更强——这是一个清晰的产品取舍,而非能力差距。

延伸:压缩 KV 的四条架构路线,各家怎么走

到这里为止,讨论的都是 API / 推理服务层的缓存策略。但「让 KV 变小、撑住长上下文」这件事,根子在模型架构层——前面反复出现的 MLA,其实只是其中一条路。跳到架构层看,主流玩家分布在四条路线上(外加一类不改 KV 结构的长上下文旁路):

路线做法对 KV 的影响代表
① GQA(分组查询注意力)多个 query 头共享一组 KV 头KV 按分组比例缩小,仍是精确注意力Qwen 主线、Grok-1、gpt-oss、Gemma、Llama
② MLA(潜在注意力)把 KV 压成低秩 latent 向量KV 大幅压缩,仍是精确 softmaxDeepSeek、Kimi K2
③ 线性 / 混合用固定递归状态替代增长的 KVKV 近似常数、线性复杂度,但是近似MiniMax Lightning、Kimi KDA、Qwen3-Next
④ 稀疏 / 滑窗每个 query 只 attend 选中的块或窗口降计算与显存峰值Kimi MoBA、gpt-oss 滑窗、Gemma 局部层、GPT-3
(旁路)长上下文技巧位置重映射 / 压缩记忆,不改 KV 结构本身撑长上下文,不直接减 KVQwen DCA、Google Infini-attention / Titans(研究)

这一层比 API 缓存更隐蔽,因为闭源厂商几乎都不公开架构。下面按「公开程度」把各家摊开——凡官方未披露的一律标注清楚,不拿第三方推测充数:

厂商架构公开度已知路线关键事实
DeepSeek✅ 开源+论文V2/V3 MLA,KV ~70KB/token
Kimi / Moonshot✅ 多数开源②+③+④K2=MLA;MoBA 块稀疏;Kimi Linear/KDA 混合线性(3:1),K3 放大到 2.8T
MiniMax✅ 开源+论文③(后撤)M1 用 Lightning 线性;M2 退回全注意力
Qwen✅ 开源+论文①主线 +③ +④主线 GQA;Qwen3-Next Gated DeltaNet 线性混合(3:1);Qwen2.5-1M DCA+稀疏
OpenAI⚠️ 仅开源版①+④(gpt-oss);旗舰未披露GPT-3 稀疏;gpt-oss GQA+滑窗/全注意力交替+attention sink;GPT-4/5 未披露
Google Gemini⚠️ 仅 Gemma+研究①+④(Gemma);旗舰仅确认 MoEGemma2 GQA+1:1滑窗;Gemma3 GQA+5:1滑窗(显式为降 KV);Gemini 旗舰注意力未披露
xAI Grok⚠️ 仅 Grok-1①(Grok-1 GQA);旗舰未披露Grok-1 GQA(48:8)+RoPE;Grok-1.5/2/3/4 未披露
Anthropic❌ 全不公开未知任何一代 Claude 的注意力/KV 均无官方披露,也无高效注意力论文

开源可查的四家:路线分化明显

  • DeepSeek 稳守 MLA(②)——压缩 KV、保留精确注意力,也正是它磁盘缓存的架构底座。
  • Kimi / Moonshot 最「全」,一家横跨三派:主力 K2 用 MLA(②),自研块稀疏 MoBA(④,已上线支撑长上下文),2025-10 又推出混合线性 Kimi Linear / KDA(③,与 MLA 全注意力层按 3:1 交错),K3 更把 KDA 放大到 2.8T。
  • Qwen 从最保守的一端起步:主线 Qwen2/2.5/3 全用 GQA(①,从未用 MLA),长上下文靠 DCA + 推理期稀疏(旁路)撑到 1M,直到 2025-09 的 Qwen3-Next 才用 Gated DeltaNet 进了线性混合派(③)。
  • MiniMax 演了一出「进而复退」:M1 用 Lightning Attention(③)激进押注线性,到 M2 反而退回全注意力,官方称线性在生产环境难稳定匹配质量。

一个值得记住的行业信号:线性注意力这条路,Moonshot 在加注、MiniMax 在撤退、Qwen 刚进场、DeepSeek 不碰——2025–2026 年远未达成共识(有评论直接说「Nobody Agrees on Attention Anymore」)。

闭源四家:只能靠开源版和论文侧写

  • OpenAI:旗舰 GPT-4/4o/5/o 系架构全不披露(GPT-4 技术报告明说出于竞争考虑不给细节)。但公开碎片不少:GPT-3 论文写明用「交替 dense + 局部带状稀疏」;2019 的 Sparse Transformer 是稀疏注意力的奠基工作;2025 开源的 gpt-oss 完整公开了架构——GQA(64 query : 8 KV 头)+ 滑动窗口(带宽 128)/全注意力逐层交替 + attention sink + MoE。但 OpenAI 从没说 gpt-oss 等于旗舰内部结构,别外推
  • Google Gemini:旗舰只确认是 Sparse MoE,注意力/KV 具体结构未披露。但开源的 Gemma 给了最好的一手实例:Gemma 2 用 GQA + 局部滑窗/全局注意力 1:1 交替(窗 4096),Gemma 3 改成 5:1、窗缩到 1024,且技术报告明确说动机就是压 KV cache(只有约 1/6 的层需存全序列 KV)。另外 Google 发了一批相关研究论文——Griffin/Hawk(门控线性循环)、Infini-attention(压缩记忆)、Titans(测试时记忆)——但没有任何证据表明 Gemini 旗舰用了它们,研究与产品要分开看。
  • xAI GrokGrok-1(2024 开源)是 GQA(48 query : 8 KV)+ RoPE 的标准 MoE;Grok-1.5 及以后架构未披露(Grok-2.5 有权重但无技术报告)。
  • Anthropic最彻底的黑箱——任何一代 Claude 的注意力机制、头数、KV 方案全程零官方披露,Anthropic 也没发过高效注意力 / KV 压缩方向的架构论文(其公开研究集中在可解释性与对齐)。所以「Claude 用了 GQA 还是 MLA」这类说法目前没有任何官方依据,纯属推测。

回扣前缀缓存:哪条路线对缓存友好

这一整层架构差异,最终会回落到本文主题——前缀缓存上:

路线对前缀缓存
① GQA、② MLA、④ 稀疏/滑窗✅ 友好——仍是逐 token、可按前缀寻址的标准 KV,能跨请求复用、能落盘
③ 线性 / 混合⚠️ 别扭——历史压进不增长的递归状态,不像 KV 那样能切片精确匹配;但混合架构里那 1/4 全注意力层(Kimi 甚至就是 MLA)仍是标准 KV,缓存主要落在那几层

换句话说:谁保留了「可寻址的逐 token KV」,谁就和当今云 API 的前缀缓存 / 计费体系天然兼容(GQA、MLA、稀疏都算);谁用固定递归状态取代 KV,谁在极长上下文上更省、但缓存复用更别扭(纯线性)。这也解释了为什么走 MLA 的 DeepSeek 能把磁盘缓存做成招牌产品,而重度依赖线性注意力的模型,缓存故事天然更难讲。

⚠️ 本节可信度分档:可当架构事实的仅限有开源权重 / 技术报告的部分(DeepSeek MLA、Kimi K2/MoBA/KDA、MiniMax Lightning、Qwen GQA/Qwen3-Next、gpt-oss、Gemma 2/3、Grok-1);Google 的 Griffin/Infini-attention/Titans 是研究论文,不代表 Gemini 实现;GPT-4/5、Gemini 旗舰、Grok-1.5+、以及全部 Claude 的注意力架构均为官方未披露,网传参数/机制(如「GPT-4 1.76T」「Claude 用 MLA」)属推测,勿当事实。

Token 级传输:为什么云 API 几乎都不支持

还有一个绕过「重发文本历史」的激进思路:能不能直接传 token IDs,而不是文本 messages? 毕竟服务端反正要分词,客户端缓存好 token 岂不省事。

分层核实的结论是:本地/自托管推理支持,主流云聊天 API 基本不支持。

  • llama.cpp server(本地)/completionprompt 可以是「字符串,或字符串与数字(token)混合的数组」,直接喂 token IDs。
  • OpenAI 旧版 Completions API(GPT-3 时代)prompt 官方定义就接受 “string, array of strings, array of tokens, or array of token arrays",历史上确实可以直接传 token id 数组。Embeddings 的 input 至今沿用这一约定。
  • 现代主力 API(OpenAI Chat/Responses、Anthropic Messages)messages / input 只接受文本及多模态 part,不接受 token id 数组。OpenAI 现代 API 里唯一直接暴露 token id 的口子是 logit_bias{"50256": -100} 封禁某 token)。Anthropic 甚至不提供可本地运行的 tokenizer,只给 count_tokens 接口。

云 API 普遍不暴露 token IDs 接口,背后是几个明确的架构考量:

  1. tokenizer 版本耦合:token id ↔ 文本的映射随模型/分词器版本变化。暴露 id 会把客户端硬绑定到某个具体模型版本,厂商无法平滑升级分词器。
  2. 抽象层:API 承诺的输入契约是「文本/多模态」,分词是服务端内部实现细节。暴露 id 会击穿这层抽象,增加维护面。
  3. 安全/一致性:任意 token id 数组可构造非法/越界序列、绕过内容处理管线;由服务端统一分词更可控。
  4. 多模态:图像/音频等 part 无法用「文本 token id」表达,统一走结构化 content 更自洽。

有状态的代价与收益:为什么厂商没有统一到「服务端存历史」

回到最初的痛点:编程 Agent 的上下文动辄几百 KB,无状态 API 每轮重发既浪费带宽又拖慢延迟。既然有状态 API 能只传增量,为什么没有全行业统一到这个模式来一劳永逸?

先量化收益。 无状态下,第 N 轮请求要上传「前 N-1 轮的全部历史」。几百 KB 的上传本身有网络耗时,且服务端要重新解析、重新哈希这个大 prefix 去查缓存——即便命中缓存省掉了 prefill 计算,「上传 + 重新哈希前缀」这段固定开销依然存在,并直接计入首 token 延迟。有状态 API 则把历史留在服务端,客户端只传一个 id + 新增消息,这段开销被消掉,长上下文下 TTFB 优势明显(这也是 OpenAI 宣称 Responses 缓存利用率提升 40–80% 的一部分来源)。

但代价也很实在,导致无状态仍是主流:

  1. 服务端存储与合规负担:有状态意味着厂商替你持久化对话原文(OpenAI 默认存 30 天)。这对**零数据保留(ZDR)**需求、GDPR/数据驻留合规是直接冲突——很多企业客户明确要求「服务端不得留存对话」,无状态是唯一满足方式。
  2. 控制权与可移植性:无状态下,对话历史完全在客户端手里,随时可编辑、裁剪、压缩、跨供应商迁移。有状态把历史锁在某家服务端,换供应商、做上下文工程(如中途删除某些工具结果、压缩历史)都更受限。
  3. 缓存已经吃掉了大部分成本红利:隐式前缀缓存让无状态的「重发历史」在计费上已经很便宜(命中部分 ~0.1×)。它没解决的只是「上传 + 重新哈希」这段延迟,而这对多数应用不是瓶颈。当缓存把 90% 的经济痛点消除后,迁移到有状态的动力就没那么强了。
  4. 无状态天然幂等、易水平扩展:每个请求自包含,可路由到任意后端、易重试、易做无共享架构。有状态引入了会话亲和性(session affinity)等分布式复杂度。

所以现实是一种分层共存:无状态 API + 隐式/显式前缀缓存作为默认主力(简单、合规友好、缓存已足够便宜),有状态 API(Responses / Interactions)作为面向复杂 Agent、追求极致延迟与推理状态保留的进阶选项。两者不是替代关系,而是覆盖不同需求象限。

总结:一张选型速查表

维度无状态 + 隐式缓存无状态 + 显式缓存有状态 API
代表OpenAI Chat / DeepSeek / Gemini 隐式 / 国产多数Anthropic / Qwen 显式 / Gemini CachedContentOpenAI Responses / Gemini Interactions / xAI Responses
客户端负担每轮重发全部历史每轮重发 + 管理断点只传增量 + response id
心智负担最低(零代码)中(需规划断点/TTL)低(服务端托管)
缓存控制无(全自动)精细(断点 + TTL)自动
合规/ZDR✅ 最友好✅ 友好⚠️ 服务端留存原文
可移植性✅ 历史在客户端⚠️ 历史锁在供应商
长上下文延迟一般(每轮传大 prefix)一般✅ 最优(增量传输)
推理状态保留✅(Responses reasoning items)

给不同场景的建议:

  • 一次性调用 / 简单对话:任何无状态 + 隐式缓存的 API 都够用,选最便宜/最快的即可。
  • 共享大前缀、高频复用(RAG、长 system prompt、few-shot):显式缓存(Anthropic cache_control 1h TTL、Gemini CachedContent、Qwen 显式)能给确定性命中;若复用频次不够高,注意别被写入/存储费吃掉收益。
  • 合规敏感 / 需 ZDR / 要跨供应商可移植:坚持无状态 API,靠隐式缓存降本。
  • 长上下文编程 Agent、追求极致 TTFB、需保留推理链:考虑有状态 API(Responses / Interactions),用增量传输 + reasoning 复用换延迟。

底层原理只有一条——因果注意力决定了缓存只能是前缀形的;而各家所有花样,都是围绕「谁来标记这个前缀」和「谁来保存这段历史」两个问题,在成本、延迟、控制权、合规之间做的不同权衡。


参考资料

底层原理

OpenAI

Anthropic

Google Gemini

DeepSeek

xAI Grok

国产厂商

模型注意力架构(延伸阅读)

数据准确性说明:本文价格/折扣/上线时间均以各家官方文档为准并标注来源。以下几处官方未给硬数据、或随模型迭代变动,落地前建议二次核对:① Gemini 显式缓存的存储费($1.00/M/hr)与写入费($0.50/M)确切金额、以及隐式折扣「75% vs 90%」按模型代际取值;② xAI 缓存与 Qwen/智谱 Context Cache 的确切上线年月;③ 智谱缓存的最小 token 与精确折扣;④ DeepSeek 当前在售模型(如 V4 系)的现价——但「命中比未命中便宜约一个数量级」这一结论稳定;⑤ MiniMax 缓存的确切上线年月,以及 abab6.5 / MiniMax-01 / M1 是否纳入该 API 级缓存(官方缓存文档只列 M2/M3 系);⑥ 百度 ERNIE-4.5-Turbo / X1-Turbo 是否支持上下文缓存(官方文档只列 ERNIE-4.0-Turbo-8K 与千帆侧 DeepSeek 系),以及百度是否有有状态对话 API(官方文档未见对等能力)。