Gemini Context Caching 成本优化
当一份长文档、代码仓库摘要或大段 system instruction 被重复发送给 Gemini 时,成本浪费往往不在用户每次新增的短问题,而在反复处理不变的前缀。Context Caching 的作用,是让这部分重复输入以缓存形式复用。但“开启缓存”不等于一定省钱:前缀频繁变化、请求次数太少或 TTL 过长,都可能让缓存收益被存储成本抵消。
Google 当前文档区分隐式缓存与显式缓存。隐式缓存由支持的模型自动处理,命中后体现节省,但应用不控制具体缓存对象;显式缓存由应用创建、指定 TTL 并在后续请求中引用。当前 Interactions API 只支持隐式缓存,手动显式缓存需使用支持该能力的 generateContent 路径。API 与模型支持会变化,实施前应核对官方 Context caching 文档。
一、先识别真正可缓存的重复前缀
缓存适合“大而稳定的公共前缀 + 多次较短请求”。典型例子包括:多人反复查询同一份产品手册、对同一段长视频提出不同问题、团队持续分析同一版本代码快照,或大量请求共享复杂系统说明。
| 工作负载 | 缓存价值 | 判断依据 |
|---|---|---|
| 同一文档被连续问几十次 | 高 | 公共前缀大、复用次数多 |
| 每次上传不同合同只问一次 | 低 | 创建后几乎不复用 |
| 系统 prompt 稳定、用户问题短 | 中到高 | 取决于前缀长度与流量 |
| 对话历史每轮整体重排 | 低 | 前缀不稳定,难命中 |
| 同一视频做多维度分析 | 高 | 多模态输入大且内容固定 |
第一步不是调用缓存 API,而是把真实请求按“稳定前缀指纹”聚合。统计每个指纹的输入 token、复用次数、请求间隔和版本变化频率。没有这些数据,就无法判断显式缓存是否值得创建。
二、隐式缓存优化靠稳定前缀
隐式缓存无需创建资源。要提高命中机会,应把大段公共内容放在请求前部,把每次变化的用户问题、时间戳、随机 ID 和追踪信息放到后部或请求 metadata。即使语义完全相同,只要前缀序列变化,缓存复用也可能下降。
常见反模式是在 system instruction 里注入当前时间、请求 ID 或动态用户画像;另一个反模式是由对象遍历顺序不稳定的代码生成工具声明。解决方法是对 prompt 模板、工具数组和 JSON 序列化做确定性排序,并用内容哈希验证同一版本是否真的字节稳定。
隐式缓存属于 best-effort 优化,不应成为业务正确性的依赖。缓存未命中时请求仍应正常完成,应用只把命中看作成本和延迟收益。响应中的 usage 字段可以反映缓存 token,用它计算实际命中,而不是凭“请求很像”猜测。
三、何时值得创建显式缓存
显式缓存适合能够提前确定内容、并预计在 TTL 内多次复用的工作负载。它让应用拥有缓存资源名与到期时间,后续请求明确引用缓存。代价是创建、存储、版本和清理都由应用管理。
可以用一个不依赖具体价格的盈亏公式:
无缓存成本 = N × P × 标准输入单价
显式缓存成本 = P × 创建成本
+ P × TTL × 存储单价
+ N × P × 缓存输入单价
其中:
N = TTL 内预计复用次数
P = 可缓存前缀 token 数
把当日官方价格代入,求出最小 N。再用历史流量的保守分位数判断能否达到,而不是用峰值。若大多数缓存对象在过期前只被调用一两次,显式缓存可能没有经济性。
模型对可缓存输入通常有最小 token 要求,而且缓存 token 仍计入模型上下文上限。最小值、支持模型和价格都可能调整,建议在配置层维护并从官方文档核对,不要把数字写死在业务代码里。
四、缓存内容要按不可变版本管理
显式缓存创建后,不要把它理解成可编辑文档。更可靠的抽象是不可变制品:model + corpusVersion + systemPromptVersion + toolSchemaVersion 共同决定缓存键。任一部分变化,就创建新缓存并把新流量切过去。
const cacheKey = sha256(JSON.stringify({
model,
corpusVersion: "docs-2026-08-22",
systemPromptVersion: "support-v12",
toolSchemaVersion: "tools-v4"
}));
应用数据库保存 cacheKey、供应商缓存名、模型、token 数、创建时间、到期时间、状态和最后访问时间。并发请求发现缓存不存在时,用唯一约束或分布式锁保证只有一个创建者;其他请求等待短时间,或暂时走无缓存路径,避免“缓存惊群”。
发布新知识库时先创建并预热新版本,验证成功后原子切换别名,旧版本保留一个短缓冲期再删除。这样在线请求不会在构建过程中读到一半新、一半旧的内容。
五、TTL 应匹配复用窗口而不是数据寿命
文档一年不变,不代表缓存 TTL 应设一年。TTL 的目标是覆盖高概率复用窗口,同时控制存储成本与数据保留。若用户通常在上传视频后的二十分钟内连续提问,稍长于这段活跃窗口的 TTL 往往比长期保存更合理。
可按访问模式分层:交互式会话使用短 TTL;工作日反复查询的共享手册使用中等 TTL 并按实际访问续期;夜间批处理只覆盖批次预计时长。续期前重新估算未来调用量,不能因为“有人访问过”就无条件延长。
涉及敏感内容时,TTL 还受数据治理约束。删除源文档、用户撤回授权或租户停用后,应主动删除相关显式缓存,不能只等待自然过期。缓存名与源资源建立反向索引,才能完成定向清理。
六、请求结构决定能否复用
缓存内容是后续 prompt 的前缀。稳定的 system instruction、共享资料、工具定义应放入缓存;用户问题、会话即时状态和权限相关信息留在非缓存部分。不要缓存访问控制结果,因为权限可能变化,也不要把多个租户的私有资料合进一个共享缓存。
同一知识库面向不同权限组时,可以缓存公共材料,再在每次请求中附加经过授权的短上下文。若私有材料本身很大,则按租户与权限版本隔离缓存。缓存键必须包含租户边界,服务端也要验证缓存记录归属,不能接受客户端随意传入供应商缓存名。
工具 schema 是否进入缓存取决于稳定性。频繁变动的工具描述会迫使整个缓存换代;可把稳定的核心工具与实验工具拆开,或者在实验阶段先依赖隐式缓存,等接口稳定后再显式创建。
七、命中率要按 token 而不是请求计算
请求命中率会误导:十个短请求命中、一个超长请求未命中,按请求看是 91%,按 token 看可能仍然很差。核心指标应是 cached_input_tokens / total_input_tokens,并同时记录可缓存前缀 token、实际缓存 token、非缓存 token、输出 token 和端到端成本。
按模型、cacheKey、业务场景和模板版本拆分指标。出现命中率下降时,先比较前缀哈希、序列化结果和流量间隔,再检查模型或 API 路径是否变化。详细的监控方法可参考LLM 缓存命中率可观测性。
显式缓存还要监控创建成功率、创建延迟、对象复用次数、到期前收益、孤儿缓存数和提前删除数。每个缓存过期时计算实际节省与预估节省差异,用于调整下一轮 TTL 和创建阈值。
八、用小规模实验验证真实收益
上线前选取代表性请求做三组对照:无缓存、隐式缓存、显式缓存。固定模型、输入与输出要求,比较输入 token 构成、缓存 token、首 token 延迟、总延迟、错误率和实际账单。预热请求与稳定阶段要分开统计。
实验还要包含缓存未命中、过期、删除、模型切换、源文档更新和并发首次创建。应用在任何缓存故障下都应有明确降级:短期走普通请求,或返回可重试错误;不能因为成本优化组件故障而读取错误版本的资料。
最终把缓存当作一项有生命周期的成本优化,而不是永久基础设施。只有当 token 加权命中率、复用次数和实际账单持续证明收益时才扩大覆盖范围;对低复用 workload,保持请求结构稳定并依赖隐式缓存往往更简单。
九、相关阅读
如果你希望在统一入口中观察不同模型的 token 与调用用量,YoTradeApi 可提供兼容常见 SDK 的 API 接入方式,帮助集中管理鉴权和调用记录。