Interactions API 用不了 Gemini 显式缓存:只能走隐式那条路
数据截至 2026-08,价格与限额以各官网为准。本文只讲计费与接入机制,不列具体价格数字。
结论先摆出来:Gemini 提供隐式缓存与显式缓存两种,但官方文档明确写着 Interactions API 仅支持隐式缓存、不支持显式缓存;要用显式缓存,必须改用 generateContent API。官方是把这一条写在接口这一层的,不是写成某个开关或某个参数没配对——留在 Interactions API 这条链路上,参数翻来覆去调也变不出显式缓存来。所以真正该做的判断是两件事:一是确认你到底在调哪个 API,二是如果决定留在 Interactions API 上,就把力气全花在提高隐式缓存命中率上。官方给出的提高命中率的办法有两条:把较大且常见的内容放在提示开头,以及在短时间内发送具有相似前缀的请求。命中与否则看响应对象里的 usage.total_cached_tokens。
先把 Gemini 的两种缓存分清楚
Gemini 的缓存不是一个东西,是两个:隐式缓存(implicit)和显式缓存(explicit)。而它们在「哪个接口上能用」这件事上的差别,比名字看起来要大得多。
隐式缓存的特点是不需要你做任何操作。官方说明是:隐式缓存对 Gemini 2.5 及更新型号默认启用,无需任何操作;命中之后会自动返还节省下来的费用。注意这里有个型号限定,官方给的是「2.5 及更新型号」,不是所有模型都默认开,你在更老的型号上没看到缓存效果,先回去核一下自己调的是哪一代。
显式缓存这一侧,我手上的官方材料除了下面那条接口限制,就只留下了「显式缓存(explicit)」这个名字。它具体怎么创建、由谁维护生命周期、创建时要填哪些参数,我没有在官方文档里找到可以直接引用的说明,所以这里不替它补一个定义——名字里带个「显式」就顺手推断出「要手动创建、手动管理」,那是望文生义,不是文档写的。能确定的只有一条,而问题恰恰出在这一条上:官方文档写的是「Interactions API 仅支持隐式缓存,不支持显式缓存;要用显式缓存必须改用 generateContent API」。也就是说,留在 Interactions API 这条链路上,官方并没有给显式缓存留下入口。
至于在 Interactions API 上如果硬传显式缓存相关的参数会得到什么反应——返回哪一个错误码、还是被静默忽略——官方文档里没有找到相关说明。Gemini 的标准请求级错误码表里确实有 invalid_request(请求格式有误或参数无效)和 parameter_unknown(含未知参数,官方推荐处置是移除无法识别的参数后重试)这两个 400 类的码,但官方并没有说这个具体场景会落到哪一个上,我不替官方下这个结论。你自己遇到时,以实际返回的 error 对象里的 code 字段为准。
这条限制写在接口选型这一层:先确认你调的是哪个 API
这个坑之所以容易踩,是因为它跟直觉不太一样。排查缓存不生效,很自然的第一反应是去查模型支不支持、内容够不够长、前缀有没有变。但官方给出的这条限制压根不在那几个维度上——它是写在接口这一层的:留在 Interactions API 上,显式缓存不在支持范围内;要用显式缓存,官方给出的路径是改用 generateContent API。
这里要特别留意官方那句话的方向。它写的是「必须改用」,这是一个必要条件:不换接口,这条路一定走不通。但反过来说「换了接口就一定能用」,官方文档并没有这么写,我也不替它这么写——改用 generateContent 之后显式缓存还有没有别的前提、受不受型号约束,官方文档里没有找到相关说明。而且旁证是现成的:同一节里官方对隐式缓存是明确带型号限定的(2.5 及更新型号默认启用),显式缓存那边有没有类似的限定,材料里一个字都没提。所以正确的用法是拿它当一道否决门槛——「还在 Interactions API 上,就别指望显式缓存」——而不是拿它当一张换接口就兑现的承诺。
Gemini 里这类「按接口划边界」的约束不止这一处。官方在 Batch API 的页首注意事项里同样写着:Batch API 目前仅适用于 generateContent API。这是两条各自独立、都能在官方文档里逐字找到的接口级限制。至于 Gemini 是不是有意把 generateContent 当成功能最全的那条路径、其他接口只覆盖其中一部分,官方文档里没有找到这样的说明,本文不替它归纳这条设计规律。对使用者真正有用的是可操作的那一半:同一件事在不同接口上能不能做,得一条一条回官方文档确认,不能靠「都是 Gemini,那应该都一样」去推。
这个模式带来的实际后果是:做接口选型的时候,不能只看「能不能把对话跑通」。跑通是最低要求,跑通之后你还要用的显式缓存、批量提交这些东西,可能已经在你选接口的那一刻被排除掉了。等到成本压不下来才回头改接口,改的就不只是几行调用代码,还有围绕它建的那一整套请求组织方式。
留在 Interactions API 上,能做的就是把隐式命中率抬起来
如果综合考虑之后你决定还是留在 Interactions API,那显式缓存这条路就别惦记了,把注意力换到隐式缓存上。官方给出的提高隐式缓存命中率的方法有两条,都很具体(以官方文档当前版本为准):
第一条:把较大且常见的内容放在提示的开头。 这条的意思是,那些每次请求都一样的部分——系统指令、固定的背景资料、长文档——要往前排。
第二条:在短时间内发送具有相似前缀的请求。 相似前缀是命中的基础,而「短时间内」这个限定同样不能丢。
这两条合起来其实描述的是同一件事:让请求的前半截尽可能稳定、尽可能长、尽可能密集地重复出现。反过来说,如果你习惯把时间戳、随机 ID、当前用户名这类每次都不同的东西塞在提示最前面,那前缀从第一个 token 起就分叉了,后面内容再一致也没用。
提示的头和尾要分开对待
官方在长上下文那部分的 FAQ 里还给了一条方向相反但完全不冲突的建议:把查询或问题放在提示的末尾,在所有其他上下文之后,尤其当总上下文很长时,这样效果更好。
把这条和缓存那条放一起看,一个完整的提示布局就出来了:大块的、复用的、稳定的内容放开头,每次都变的那个具体问题放结尾。前者服务于缓存命中,后者服务于回答质量,两条官方建议指向的是同一种排布方式。
同一份 FAQ 里另外两条也值得记:不需要传给模型的 token 就别传;有一组要重复使用的相似上下文时,用上下文缓存来降本。第一条听着像废话,但「反正上下文够长,整份文档顺手塞进去」这种写法一旦成了习惯,多出来的那部分 token 每次请求都要重新算一遍钱;更麻烦的是它常常夹在稳定前缀和真正的问题中间,顺带把前缀也搅乱了。
有状态和无状态两种对话模式都在覆盖范围内
还有一点容易被忽略:官方说明里提到,隐式缓存同时支持有状态(用 previous_interaction_id)与无状态两种对话模式。也就是说,你用不用 previous_interaction_id 串对话,不改变隐式缓存本身是否可用这件事。
需要留意的是,隐式缓存有最低输入 token 门槛,而且这个门槛因模型而异。具体数值属于会变的东西,这里不列,以官方文档为准——但机制上你要知道:短请求可能压根够不到门槛,这时候看不到缓存效果是正常的,不是配置错了。
命中没命中,只认 usage.total_cached_tokens
隐式缓存最难受的地方在于它是自动的——自动的意思是,它没命中的时候也不会告诉你。所以必须有一个客观的观测口径。
官方给出的命中量查询字段是响应对象里的 usage.total_cached_tokens(Python 与 JavaScript 都是这个)。这个字段该怎么用,我的建议是别只在调试时看一眼,而是在正常请求路径里就把它记下来,和请求的输入 token 数放在一起。这样你手上就有了一条可以横向比的曲线:改了提示结构之后,缓存 token 占输入的比重有没有往上走。
这比盯账单靠谱得多,原因在下一节。
显式缓存不是白拿的:存储时长要单独计费
有人看到「显式缓存要换接口」,第一反应是那我赶紧换。先别急,得知道显式缓存的账是怎么算的。
Gemini 官方 FAQ 里列的计费依据是四项:输入 token 数、输出 token 数、缓存的 token 数,以及缓存 token 的存储时长。最后这一项是关键——存储本身是单独一项计费依据,不是免费附赠的。官方在长上下文那部分说得更直白:把用户上传的文件缓存起来,是按小时为存储付费,换取重复提问时输入成本的下降。
这个结构决定了显式缓存的收益不是无条件的:它是一笔存储支出换输入支出的交易。缓存里放的东西如果在有效期内被反复命中,这笔交易划算;如果放进去之后没人再问,那就是净支出。所以显式缓存真正适合的,是官方 FAQ 点名的那种场景——有一组要重复使用的相似上下文。
顺便说一句,这也是为什么前面建议你盯 usage.total_cached_tokens 而不是盯账单:Gemini 的结算本身是有延迟的,总费用明细图表最多可能需要一天才更新,你今天改的提示结构,看账单是看不出立刻反馈的。关于跨厂商的缓存计费差异,可以对照缓存计费机制的通用拆解看;把成本监控做成常态化的那套做法,在API 成本监控里讲过。
什么时候才值得为显式缓存改接口
给一个可以直接照着走的判断顺序:
第一步,先确认你现在调的到底是哪个 API。 这一步经常被跳过,尤其是团队里换了人接手、或者代码是照着某个示例改出来的时候。是 Interactions API 还是 generateContent,决定了后面所有的讨论有没有意义。
第二步,如果是 Interactions API,先把隐式缓存的路走完。 把稳定内容前置、把问题后置、把没必要的 token 砍掉、把 usage.total_cached_tokens 埋上。这几件事不用改接口,成本是最低的。走完之后如果缓存 token 的占比已经上来了,那就没必要再动接口了。
第三步,只有当你的场景确实是「同一份大上下文被反复复用」,而隐式缓存又因为请求太分散、间隔太长而抬不上去时,才去考虑改用 generateContent 走显式缓存。理由是显式缓存的收益模型要求命中足够密集,否则那笔存储支出收不回来。
至于从 Interactions API 具体怎么迁到 generateContent、两边的请求体有哪些字段差异、显式缓存创建时有哪些参数——这些官方文档里我没有核到可以直接写出来的原文,别照着记忆去改,去查官方 API 参考。
最后,这件事上最容易栽的坑
这条限制真正麻烦的地方不在于「不知道有它」,而在于踩上了也不会有任何东西提醒你,于是整个排查方向从一开始就偏了。表现是这样的:缓存看着不生效,于是去反复调提示顺序、去怀疑模型型号、去怀疑门槛不够、去怀疑请求间隔太长——查一圈都对得上,就是没效果。而真实原因是你想用的那种缓存在这个接口上根本不存在。
所以把顺序倒过来:遇到 Gemini 缓存不生效,第一件事是确认接口,第二件事才是确认提示结构。 接口这一层是布尔值,对就是对、错就是错,一分钟能查清;提示结构那一层要反复调,能耗掉你一下午。
下一步建议先读同一轴里讲隐式缓存怎么才能命中的那篇,把命中条件这条线补全——那篇覆盖的是命中机制本身,和这篇讲的接口边界正好是两件独立的事,缺哪一头都会让你的排查绕远路。