Gemini CLI 令牌缓存与成本优化:Token Caching 的工作原理、适用认证方式与 /stats 验证源码解析
【免费下载链接】gemini-cliAn open-source AI agent that brings the power of Gemini directly into your terminal.项目地址: https://gitcode.com/GitHub_Trending/gemi/gemini-cli
本篇技术指南基于 gemini-cli 仓库的 token-caching.md 官方文档展开,讲解 Gemini CLI 如何通过令牌缓存(Token Caching)自动降低 API 成本:哪些认证方式支持缓存、缓存如何复用上文的系统指令与上下文,以及如何用/stats命令核实缓存命中量。读完本文,你将能够判断自己的认证配置是否开启了缓存,并在交互式界面与 headless JSON 输出两个层面准确读出缓存收益,同时理解缓存令牌数在 gemini-cli 源码中的完整流转路径。
一、Token Caching 是什么:自动启用的成本优化机制
Gemini CLI 在使用 API 密钥认证(Gemini API key 或 Vertex AI)时,会通过令牌缓存自动优化 API 成本。其核心机制是:复用此前的系统指令(system instructions)与会话上下文(context),从而减少后续请求中需要处理的令牌数量。
从文档表述可以确认几个关键特性:
- 完全自动:官方文档描述为 "automatically optimizes API costs through token caching",CLI 侧没有提供需要用户显式打开的缓存开关。只要认证方式满足条件,缓存行为就由客户端与服务端配合完成,用户无需额外配置。
- 收益来源是"前缀复用":缓存命中的对象是此前已经处理过的系统指令与上下文。也就是说,同一个会话中,越是靠前的、在多轮请求间保持不变的内容(系统提示词、项目上下文等),越可能在后续请求中被识别为"已缓存",按缓存读取计费而不是按全量输入计费。
- 对后续请求生效:文档明确指出缓存是为了"reduce the number of tokens processed insubsequent requests",因此缓存收益通常体现在会话的后续轮次上,而非首轮请求。
需要强调的是,token caching 属于服务端能力:gemini-cli 本身并不维护本地缓存副本,它负责的是在满足条件的认证方式下发起请求,并把服务端返回的用量数据(其中包含缓存令牌数)如实采集与展示。这一点可以从后文的源码链路得到印证。
二、哪些认证方式支持 Token Caching
这是本文档给出的最核心的事实边界,直接决定了你当前的登录方式能否享受到缓存优化:
支持 Token Caching 的认证方式:
- API 密钥用户(Gemini API key):直接使用 Gemini API 密钥接入;
- Vertex AI 用户:前提是正确的project(项目)与 location(区域)配置。从源码结构看,Vertex AI 的接入路径独立于个人账号体系(相关实现集中在
packages/core/src/core/client.ts等核心模块),缓存可用性依赖项目级配置的完整。
不支持 Token Caching 的认证方式:
- OAuth 用户(Google 个人账号 / 企业账号):文档明确说明,Code Assist API 目前不支持创建缓存内容(cached content creation),因此通过 OAuth 个人账号登录的会话不会产生缓存收益。
这意味着一个实际的判断准则:如果你发现会话越长、成本却线性上涨,首先应检查自己是否在用 OAuth 个人账号认证。切换到 Gemini API 密钥或配置好 project/location 的 Vertex AI,是让缓存机制生效的前提。关于各种认证方式的配置细节,可参考仓库中的 认证文档 与 设置参考。
三、用 /stats 命令验证缓存收益
文档给出的核实手段是/stats命令:当存在缓存令牌时,它们会出现在统计输出中。结合源码,/stats的实际能力比文档一句话描述的更丰富。
3.1 命令结构与用法
/stats的实现位于 statsCommand.ts,其中(L84-L145)可以看到:
- 命令注册名为
stats,并带有别名usage,即/usage等效可用; - 支持三个子命令,文档中的用法格式为
/stats [session|model|tools]:/stats(默认)或/stats session:展示会话级统计——会话时长、认证类型、当前模型、配额余量(pooledRemaining/pooledLimit/pooledResetTime)与信用额度等;/stats model:展示模型维度的用量统计,这正是查看缓存令牌的位置;/stats tools:展示工具调用维度的统计。
3.2 模型视图中的 "Cache Reads" 列
模型维度的统计表格由 StatsDisplay.tsx 渲染。从源码(L82-L224)可以看到表头固定为五列:
| 列名 | 数据来源 | 含义 |
|---|---|---|
| Model | 模型名 | 当前统计的模型(含按角色拆分的子行,如main) |
| Reqs | metrics.api.totalRequests | 该模型的 API 请求总数 |
| Input Tokens | metrics.tokens.prompt | 输入令牌总数 |
| Cache Reads | metrics.tokens.cached | 缓存命中读取的令牌数(即缓存收益的核心指标) |
| Output Tokens | metrics.tokens.candidates | 输出令牌总数 |
其中cachedTokens取自metrics.tokens.cached.toLocaleString()(L106 与角色子行 L130),并在表格行中以row.cachedTokens渲染(L214)。判断缓存是否生效的最直接方式就是观察这一列:在 API 密钥 / Vertex AI 认证下,随着会话轮次增加,Cache Reads 列的数值应随之增长;若始终为 0,则多半是认证方式不在支持范围内(例如 OAuth 个人账号)。
3.3 Headless / JSON 输出中的缓存字段
如果你通过 headless 模式(脚本化调用)运行 Gemini CLI,缓存令牌同样会进入结构化输出。从 stream-json-formatter.ts 可以看到结果对象中直接携带cached: modelMetrics.tokens.cached字段;非交互模式的测试快照(如 nonInteractiveCli.test.ts.snap)也印证了结果统计的稳定结构:
{"type":"result","status":"success","stats":{"total_tokens":0,"input_tokens":0,"output_tokens":0,"cached":0,"input":0,"duration_ms":...,"tool_calls":0,"models":{}}}因此自动化脚本可以通过解析stats.cached与stats.input_tokens的比值,量化自己会话的缓存命中率,而无需依赖交互式 UI。
四、源码纵深:缓存令牌数在 gemini-cli 中的完整流转
为了说明"缓存令牌"不是 UI 上的装饰性数字,而是贯穿采集、遥测、录制、输出全链路的真实用量指标,下面梳理其在源码中的流转路径(均为当前仓库中可验证的实现事实):
采集:从模型响应映射出 cachedTokensGemini 的响应用量元数据(
usageMetadata)中带有cachedContentTokenCount字段。事件翻译层将其映射为统一的Usage结构,见 event-translator.ts:export function mapUsage( metadata: { promptTokenCount?: number; candidatesTokenCount?: number; cachedContentTokenCount?: number; }, model?: string, ): Usage { return { model: model ?? 'unknown', inputTokens: metadata.promptTokenCount, outputTokens: metadata.candidatesTokenCount, cachedTokens: metadata.cachedContentTokenCount, }; }对应地,
Usage类型在 agent/types.ts 中声明了可选字段cachedTokens?: number。测试用例(event-translator.test.ts 中expect(usage.cachedTokens).toBe(10))验证了该映射。遥测:缓存令牌作为独立的 token 类型上报
- 在 telemetry/types.ts 中,用量数据被扁平化为
cached_content_token_count: usage_data?.cachedContentTokenCount ?? 0; - 在 telemetry/loggers.ts 中,该字段被转换为
{ count: event.usage.cached_content_token_count, type: 'cache' }—— 即缓存读取与常规输入/输出令牌在遥测层是分类型统计的,这与/stats中单列 "Cache Reads" 的设计一致。
- 在 telemetry/types.ts 中,用量数据被扁平化为
会话录制:缓存令牌写入历史记录聊天录制服务在 chatRecordingService.ts 中将响应用量落盘为
cached: respUsageMetadata.cachedContentTokenCount ?? 0,类型定义 chatRecordingTypes.ts 中也带有注释cached: number; // cachedContentTokenCount。这保证了会话恢复、回放场景下用量数据依然完整。展示与输出:汇入 UI 与 JSON 统计最终这些
cached令牌数进入模型/角色维度的累计指标,驱动第三节所述的 UI 表格列与 JSONstats.cached字段。
这条"模型响应 →mapUsage→Usage.cachedTokens→ 遥测/录制 → UI 与 JSON 输出"的链路,从源码结构上看解释了官方文档中"cached tokens will be displayed in the stats output"这一句:缓存令牌是服务端在用量元数据中回传的,CLI 的职责是忠实采集并展示。
五、实践建议与适用限制
基于文档与源码可以给出的可操作结论:
- 先确认认证方式:只有在 Gemini API 密钥或正确配置 project/location 的 Vertex AI 下,缓存才会生效;OAuth 个人/企业账号(Code Assist API)当前不支持缓存内容创建,此时 Cache Reads 恒为 0 是预期行为而非 bug。
- 收益随会话轮次累积:缓存复用的是"此前的系统指令与上下文",因此长会话、多轮工具调用、反复携带相同项目上下文的场景(大型代码库分析、多文件重构)是缓存收益最明显的用法;缓存自动启用,无需也无法通过配置项手动干预。
- 用
/stats model量化验证:关注 "Cache Reads" 列是否随轮次增长,并与 Input Tokens 对比,直观判断前缀复用程度;headless 场景则解析 JSON 结果中的stats.cached字段。 - 不要混淆"缓存读取"与"免费令牌":缓存命中意味着这部分内容按缓存读取计价而非全量输入计价,属于成本优化而非免费额度;具体单价与折扣以你所用 API 服务的计费文档为准,本文与仓库均未声明具体折扣比例。
适用前提与限制:以上结论均基于当前 gemini-cli 仓库中docs/cli/token-caching.md的文档事实与packages/core、packages/cli下的实现代码;Code Assist API 对缓存的支持状态属于服务端能力,未来可能变化,应以官方 API 文档为准。
参考文件索引
| 内容 | 路径 |
|---|---|
| 官方文档(本文主体依据) | docs/cli/token-caching.md |
| /stats 命令实现 | packages/cli/src/ui/commands/statsCommand.ts |
| 统计表格(Cache Reads 列) | packages/cli/src/ui/components/StatsDisplay.tsx |
| usageMetadata → Usage 映射 | packages/core/src/agent/event-translator.ts |
| Usage 类型定义 | packages/core/src/agent/types.ts |
| 遥测用量扁平化 | packages/core/src/telemetry/types.ts |
| 遥测 token 类型 'cache' | packages/core/src/telemetry/loggers.ts |
| 会话录制缓存字段 | packages/core/src/services/chatRecordingService.ts |
| JSON 输出 cached 字段 | packages/core/src/output/stream-json-formatter.ts |
| 认证方式参考 | docs/get-started/authentication.mdx |
【免费下载链接】gemini-cliAn open-source AI agent that brings the power of Gemini directly into your terminal.项目地址: https://gitcode.com/GitHub_Trending/gemi/gemini-cli
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考