指南/三家 Token 字段对照
OpenAI、Claude、Gemini 的 Token 为什么不能直接横向比较
把 OpenAI 的 input_tokens、Claude 的 input_tokens 和 Gemini 的 promptTokenCount 放进同一列,看起来已经完成了对照。问题恰好出在这里:三家使用不同的 tokenizer,也用不同方式表示缓存、思考和工具调用。字段值能相加,不代表相加后的东西相同。
同一句话,先经过不同的 tokenizer
Token 是模型 tokenizer 切分后的单位,不是字数、字符数或一个跨厂商通用的计量单位。换模型后,即使请求文本逐字相同,Token 数也可能变化。Anthropic 的计数文档给了一个很具体的例子:Claude 4.7 及后续模型使用新版 tokenizer,同一段输入相较更早模型大约多 30%,具体差值随内容而变。连同一家厂商的模型代际都不能复用旧计数,更不能拿三家的数字直接比较。
多模态输入会把差异再放大。Gemini 的Token 指南明确把文本、图片、音频和视频都计入输入,并分别规定媒体的换算方式。另一家 API 返回的「1000 个输入 Token」,未必处理了相同分辨率、相同时长或相同的内部表示。
所以第一条规则很朴素:跨厂商比较成本时比较货币金额和完成的任务,不比较裸 Token 数。 Token 数只适合在同一接口、同一模型版本和同一请求结构内做前后对照。
三家的输入字段,包含关系不一样
最容易出错的是缓存。下面只列当前主流生成接口的原生字段;OpenAI Chat Completions、Gemini Interactions API 等接口还会使用另一套命名,落库时要把接口类型一起保存。
| API | 输入总量字段 | 缓存字段 | 归一时怎么读 |
|---|---|---|---|
| OpenAI Responses | input_tokens | input_tokens_details.cached_tokens;GPT-5.6 及后续模型还可见 cache_write_tokens | cached_tokens 是 input_tokens 的明细,不能再加一次 |
| Claude Messages | input_tokens | cache_creation_input_tokens、cache_read_input_tokens | 三项互斥,要相加才是本次输入总量 |
| Gemini GenerateContent | promptTokenCount | cachedContentTokenCount | promptTokenCount 仍表示有效 prompt 总量,已经包含缓存部分 |
OpenAI 的 Responses 用量对象把 cached_tokens 放在 input_tokens_details 下。这个层级表达的是「输入中的缓存部分」。若记录里 input_tokens=12000、cached_tokens=8000,输入总量仍是 12000;未缓存部分可以按 4000 理解。
Claude 采用另一种拆法。Prompt caching 文档明确给出:
total_input_tokens = input_tokens + cache_creation_input_tokens + cache_read_input_tokens
其中 input_tokens 只计算末端缓存断点之后、既没有读取缓存也没有写入缓存的输入。若把 Claude 的 input_tokens 直接对应到 OpenAI 的同名字段,缓存命中越多,Claude 看起来越像「输入突然消失」。
Gemini GenerateContent 的 UsageMetadata 定义又回到包含式口径:设置了缓存内容时,promptTokenCount 仍是有效 prompt 的总大小,cachedContentTokenCount 只是其中的缓存部分。这里也不能相加。
输出文字和思考用量也要分开
模型返回 800 个汉字,不等于只生成了屏幕上这些 Token。推理模型可能先产生内部思考,再给出可见答案。各家的字段表达如下:
| API | 输出总量或正文 | 思考明细 | 包含关系 |
|---|---|---|---|
| OpenAI Responses | output_tokens | output_tokens_details.reasoning_tokens | reasoning 是输出明细,包含在 output_tokens 内 |
| Claude Messages | output_tokens | output_tokens_details.thinking_tokens | output_tokens 是含思考用量的计费总数 |
| Gemini GenerateContent | candidatesTokenCount | thoughtsTokenCount | totalTokenCount 明确等于 prompt、thoughts 与 candidates 的合计 |
Claude 的 Messages 参考把 output_tokens 定为计费的权威总量,thinking_tokens 只是只读拆分。Google 则在 GenerateContent 参考里把正文候选和思考分列;totalTokenCount 才把 prompt、thoughts、response candidates 合到一起。
因此不能用同一条公式处理三家。一个常见的重复计算是把 OpenAI 的 reasoning_tokens 再加到 output_tokens;另一个是只看 Gemini 的 candidatesTokenCount,漏掉计费所依据的思考 Token。
工具调用让「总量」更难对应
Gemini GenerateContent 还有 toolUsePromptTokenCount,用于记录工具调用 prompt 中的 Token。OpenAI Responses 会把工具调用表示为输出 item,Claude Messages 则把工具定义与内容块纳入各自的输入、输出计量。字段结构对应的是各家的执行模型,不能把某一个字段强行映射成另外两家的同名列。
若目标是做成本归因,可以保留四类数据:
- 原始
usage:完整保存响应对象,不因当前报表用不到就丢字段。 - 厂商计费输入:按各家包含关系还原,用于核对缓存读写和输入价格。
- 厂商计费输出:保留正文与思考明细,同时记录官方定义的权威总量。
- 任务指标:例如成功处理的文档数、有效回答数或工具调用完成数,用来回答「花的钱完成了什么」。
这四类数据没有必要压成一个所谓「标准 Token」。一旦抹掉厂商和接口信息,后续无法判断数值变化来自 prompt、缓存、模型 tokenizer,还是字段语义变了。
一套能复查的对照步骤
第一步,保存供应商、接口、模型的完整标识和原始响应。模型别名与后端行为可能变化,只有「OpenAI 兼容」四个字不够定位口径。兼容接口会怎样改写字段,见“兼容 OpenAI API”到底兼容到哪一层。
第二步,用各家的原生 Token 计数接口测输入。Claude 文档提醒计数结果是估算,实际 Messages 请求可能有少量差异;因此计数接口适合做发送前预算,最终账务仍以生成响应和官方账单为准。
第三步,先在厂商内部归一,再做跨厂商报表。OpenAI 和 Gemini 的缓存数是输入总数的子集,Claude 的三个输入字段需要求和。公式必须带上供应商与接口版本。
第四步,分开显示「处理总量」和「按不同费率计价的部分」。缓存命中验证应看什么字段、怎样排除短 prompt 和前缀变化,见 Prompt Cache 到底命中了没有。
usage;要核对渠道账单,需要固定请求、官方端点和账户扣费三个数据源,步骤见 Token 虚标与倍率对账。跨厂商报表应保留各自口径
三家的 Token 都能用来计算各自请求的用量,但它们不是同一把尺。可靠的报表会保留原始字段,在供应商内部按官方口径还原缓存和思考用量,跨供应商只比较金额、任务结果与同条件下的变化。
LinkyMonitor 保存每次请求的原始用量字段和协议版本,在同一模型、同一接口内建立基线,避免把不同口径压成一个失真的总数。