Grok 提示缓存是怎么工作的
数据截至 2026-08,价格与限额以各官网为准。本文只讲计费与接入机制,不列具体价格数字。
一句话说完:Grok 的提示缓存是「从 messages 数组开头往后逐条比对」的前缀缓存,只要开头那几条消息和上一次请求逐字一致,这段前缀就可以从缓存里取;命中的那部分 token 按低于标准输入的费率计费,具体比例看官方定价页。它由 xAI API 自动执行,你不需要显式开启,但官方明确写了「缓存不是 100% 保证」——缓存条目会因为内存压力被淘汰,请求也可能被路由到另一台服务器,所以官方建议设置 x-grok-conv-id 这个 HTTP 头来提高命中率。理解这套机制的关键只有一句:任何对历史消息的编辑、删除、重排,都会从改动那一条开始把后面全部打掉。
缓存是从数组开头开始比对的,不是从任意位置
这是最容易想歪的一点。很多人下意识以为缓存是「把这段长文本存起来,以后出现就复用」,于是把长文档塞在用户提问后面,觉得反正内容一样就能命中。
xAI 官方文档 prompt-caching/how-it-works 的原话是:缓存从你的 messages 数组的开头开始工作。请求到达时,系统检查开头有多少条消息与之前的请求完全匹配,匹配上的那部分就是「前缀」,这部分从缓存中提供。
「从开头」和「完全匹配」这两个限定加起来,含义就很硬了:前缀是连续的、从第 0 条开始的一段。中间任何一条对不上,从那条开始往后就全都算新内容,得重新计算。所以静态的东西——系统提示、few-shot 示例、参考文档——要放在最前面,让它们构成一段稳定的前缀,这也是官方最佳实践里明写的一条(front-load static content)。
官方文档给的三步流程
文档把一次完整的缓存生命周期拆成三步,很短,但每一步都有信息量:
- 第一次请求:完整的提示被处理并缓存下来
- 后续请求:如果提示前缀匹配,被缓存的那部分会被复用,这就是一次缓存命中(cache hit)
- 计费:命中的 token 按降低后的费率计费
注意第一步隐含的代价:建立缓存的那次请求本身不享受降低后的费率,官方对第一步的描述就是完整提示被处理并缓存下来,它是全额计算的。所以如果你的场景是「每个用户只问一次就走」,前缀再长也不会有收益,因为永远停在第一步。缓存真正有价值的地方是同一段前缀被反复喂进去的场景:多轮对话、批量跑同一套系统提示、Agent 循环里反复带上同一份工具定义。
官方在 prompt-caching 索引页给出的收益说法有两条:一是跳过重复计算带来的首 token 时间更快,二是命中 token 按更低费率计费。这是文档的表述,具体的费率比例请以官方定价页为准。
官方给的那个例子,值得逐行看
文档里的示例很朴素,但它把「前缀」这个概念讲得比任何解释都清楚。
第一次请求:
[system] "You are a helpful assistant."
[user] "What is the capital of France?"
[assistant] "The capital of France is Paris."
第二次请求:
[system] "You are a helpful assistant." ← 命中缓存
[user] "What is the capital of France?" ← 命中缓存
[assistant] "The capital of France is Paris." ← 命中缓存
[user] "What about Germany?" ← 新内容
前三条与第一次请求逐条相同,于是从缓存取;只有新追加的那条需要计算。
这个例子还顺带说明了一件容易被忽略的事:assistant 的回复也是前缀的一部分。也就是说,你把模型上一轮的回答原样追加回 messages 数组里,它同样能进缓存。反过来,如果你在客户端对模型回复做了「美化」「截断」「去掉思考过程」再存回历史,那一条就变了,缓存从那里断掉。
怎么确认这次到底命中了没有
不要靠感觉,要看返回体里的字段。两套 API 的字段路径不一样,这一点官方 usage-and-pricing 页写得很明确:
- Chat Completions API:命中的 token 出现在
usage.prompt_tokens_details.cached_tokens - Responses API:命中的 token 出现在
usage.input_tokens_details.cached_tokens
判定规则官方也列成了表:cached_tokens 为 0 表示未命中,整个提示都是从头算的,这在首次请求或缓存被淘汰之后是预期行为;大于 0 表示命中,数值就是被复用的 token 数;等于 prompt_tokens 表示整个提示都来自缓存,官方说这种情况少见,通常发生在重复发送完全相同的请求时。
用 gRPC 接口的话字段名又不一样,官方 Python SDK 示例里读的是 response.usage.cached_prompt_text_tokens。三套接口三个名字,写监控的时候别只对一个。
日常做法很简单:把 cached_tokens 打进你的调用日志,按会话聚合一下。多轮对话里正常的形态是这个数随着轮次推进逐步上升;如果它一直是 0,那就是前缀出问题了,官方最佳实践里的排查提示是先核对会话 ID 和消息顺序。关于按什么维度记账,可以参考跨厂商的缓存计费通用机制那篇。
官方为什么反复强调「不保证」
文档里有一个 WARNING 块,原话是:提示缓存不是 100% 保证的,缓存条目可能因内存压力被淘汰,请求也可能被路由到不同的服务器,建议使用 x-grok-conv-id 来最大化缓存命中率。
这两个失效原因性质不同,值得分开理解。
淘汰是时间维度的:缓存条目在服务端会因为负载或重启随时被清掉,官方 FAQ 里回答「缓存条目能存多久」时没有给任何时长承诺,只说了随时可能被淘汰。所以别去设计任何依赖「缓存还在」的逻辑。
路由是空间维度的,也是更隐蔽的那个:缓存条目是按服务器存储的。你的第二次请求哪怕前缀一字不差,如果被分配到另一台机器上,那台机器上根本没有这份缓存,照样是 miss。x-grok-conv-id 这个 HTTP 头的作用就是把相同会话 ID 的请求路由到同一台服务器,Responses API 对应的是请求体里的 prompt_cache_key 字段,官方说它的作用与前者相同。具体怎么设、gRPC 侧怎么传 metadata,见怎么把 Grok 的缓存命中率做上去。
由此推出官方最佳实践里的最后一条:优雅地处理缓存未命中。你的应用必须在完全没有缓存的前提下也能正常工作——不能因为这次 cached_tokens 是 0 就报错或重试,那只会把成本推得更高。
哪些改动会把缓存打掉
官方专门有一页叫 What Breaks Caching,开头一句就是结论:对更早的消息做任何改动都会破坏缓存,只应该在末尾追加新消息。页面里给出的失效场景有三类:
- 编辑:把某条历史消息的内容改短、改写,从那条开始前缀失配
- 删除:把某条历史消息整条移除
- 重排:交换消息顺序,比如把 user 消息挪到 system 消息前面
第三条特别值得警惕,因为很多框架会在序列化时对消息做规整,顺序变了你自己都不知道。
还有一条是推理模型专属的,官方用了加粗的 must:对于推理模型,你必须把上一轮响应里的 reasoning_content 带回去,省略它是缓存未命中的首要原因。官方给的两条路径是:要么把加密的 reasoning content 原样回传,要么改用有状态的 responses,用 previous_response_id 自动续接对话。
这条坑之所以普遍,是因为很多人做上下文管理时会顺手把「思考过程」剥掉——看起来是省了 token,实际上把整段前缀的缓存资格也一并剥掉了。通用层面的取舍思路可以看缓存命中率优化的通用做法。
官方 FAQ 里已经回答过的边界问题
这几条经常被反复问,文档 best-practices 页里其实都有明确答复,直接照录:
- 缓存会影响输出质量吗:不会。缓存只加速提示处理阶段,无论提示来自缓存还是从头计算,模型输出是一致的
- 能不能主动制造一次未命中:可以,换一个不同的
x-grok-conv-id,或者干脆不带这个头,请求就可能被路由到没有该缓存的服务器 - 流式请求能用缓存吗:可以,流式与非流式都支持;官方说流里的第一个空 token 对应的就是缓存查找与预填充阶段
- 工具调用能用缓存吗:可以,可缓存的前缀包含直到工具调用结果在内的所有消息,只要前缀不变,后续请求同样受益
- 哪些模型支持:官方写的是所有
grok语言模型都可用,具体哪些模型支持以及各自的缓存 token 定价,指向官方定价页
最后一条尤其要注意口径:模型清单和费率都是会变的,别把它硬编码进你的成本模型里,用的时候回官方页面确认。
最容易栽的坑
按坑出现的频率排,第一名是在客户端偷偷改历史消息。做上下文裁剪、做摘要压缩、把长回复截断——这些优化动作单看都合理,但它们全部落在「修改更早的消息」这一类里,会把缓存整段作废。真要压缩上下文,就得接受那一次必然 miss,并且把压缩后的新前缀当成一个新的稳定起点用下去,而不是每轮都压一次。
第二名是会话 ID 不稳定。官方建议用 UUID 或者应用自己的 session ID,重点在「稳定」两个字上。如果你每次请求都现生成一个 ID,等于把路由亲和性彻底关掉了,看着是设了这个头,其实一次都命中不了。
第三名是把缓存当成确定性优化写进预算。它是尽力而为的机制,没有任何时长与命中率承诺。做成本估算时把它当作一个上限收益、而不是一个必然收益,账才不会算飞。