OpenAI API 怎样读取模型返回的使用量字段
来源:17golang原创
时间:2026-10-26 22:23:52 234浏览 收藏
OpenAI API 返回的使用量不在模型文本里,而是在响应对象的 usage 字段中。当前最容易混淆的是两套命名:Responses API 读取 input_tokens、output_tokens 和 total_tokens;Chat Completions 读取 prompt_tokens、completion_tokens 和 total_tokens。先判断接口类型,再读取对应对象,就不会把统计逻辑写进 choices 或输出文本解析器。
官方文档:https://developers.openai.com/api/reference/resources/responses/methods/create
Chat Completions 文档:https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create
- Responses API 的使用量入口是
response.usage,Chat Completions 的入口是completion.usage。 cached_tokens和reasoning_tokens是总量中的明细,不要再加回input_tokens或output_tokens。- 流式 Chat Completions 只有开启
include_usage后才会出现最终统计块;中断时要允许 usage 缺失。
先分清两种响应对象的字段名
如果请求走 Responses API,SDK 返回的是 response 对象,统计字段位于 response.usage。如果旧代码仍使用 Chat Completions,则对象通常叫 completion,字段名也随之改为 prompt_tokens 和 completion_tokens。两者的 total_tokens 都表示本次请求的总 token 数,但不能把两套字段混写。
| 接口 | 输入总量 | 输出总量 | 常见明细 |
|---|---|---|---|
| Responses API | input_tokens | output_tokens | input_tokens_details.cached_tokens、output_tokens_details.reasoning_tokens |
| Chat Completions | prompt_tokens | completion_tokens | prompt_tokens_details.cached_tokens、completion_tokens_details.reasoning_tokens |
Responses API 的官方接口地址是:https://api.openai.com/v1/responses。下面的代码只读取 SDK 已返回的对象,不把示例中的数字当成固定结果。
import os
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
# 用环境变量切换模型,避免把模型名写死在统计逻辑里。
model=os.getenv("OPENAI_MODEL", "gpt-5"),
input="用一句话说明什么是幂等请求。",
)
usage = response.usage
if usage is None:
# usage 可能为空,调用方要把它当作“未知”而不是 0。
raise RuntimeError("响应没有返回 usage,不能生成 token 统计")
input_detail = usage.input_tokens_details
output_detail = usage.output_tokens_details
stats = {
"input_tokens": usage.input_tokens,
"output_tokens": usage.output_tokens,
"total_tokens": usage.total_tokens,
# 这些是总量中的拆分项,不要再次累加到总量。
"cached_tokens": getattr(input_detail, "cached_tokens", None),
"reasoning_tokens": getattr(output_detail, "reasoning_tokens", None),
}
print(stats)

把总量和明细映射成自己的统计记录
接入日志或成本统计时,建议先统一成应用自己的字段,再交给后续存储。最重要的判断是:缓存 token 已包含在输入总量中,推理 token 已包含在输出总量中;它们适合做拆分分析,不是额外消耗。字段不存在时保留 None,这样能区分“接口没有提供”与“确实为零”。
def read_chat_usage(completion):
# Chat Completions 使用 prompt/completion 命名,不要套用 Responses 字段。
usage = completion.usage
if usage is None:
return None
prompt_detail = getattr(usage, "prompt_tokens_details", None)
completion_detail = getattr(usage, "completion_tokens_details", None)
return {
"input_tokens": usage.prompt_tokens,
"output_tokens": usage.completion_tokens,
"total_tokens": usage.total_tokens,
"cached_tokens": getattr(prompt_detail, "cached_tokens", None),
"reasoning_tokens": getattr(completion_detail, "reasoning_tokens", None),
}
completion = client.chat.completions.create(
# 兼容仍使用 Chat Completions 的项目。
model=os.getenv("OPENAI_MODEL", "gpt-5"),
messages=[{"role": "user", "content": "解释一次 API 重试的风险。"}],
)
print(read_chat_usage(completion))
这层映射还可以顺手记录模型名、请求 ID 和时间,但不要把 total_tokens 通过输入加输出再次计算后覆盖原值。官方返回的总量应作为本次请求的主统计字段。
流式响应要等最后一个 usage 块
非流式响应通常在整个响应对象中直接读取 usage。Chat Completions 流式请求则不同:需要设置 stream_options={"include_usage": True},中间块的 usage 通常为空,额外的最后一块才携带整次请求的统计。网络中断或主动取消时可能收不到最后一块,因此业务记录必须接受未知值。
stream = client.chat.completions.create(
# include_usage 会在结束前追加一个携带整次统计的块。
model=os.getenv("OPENAI_MODEL", "gpt-5"),
messages=[{"role": "user", "content": "用三点说明如何记录 API 用量。"}],
stream=True,
stream_options={"include_usage": True},
)
final_usage = None
for chunk in stream:
# 文本增量和 usage 是两条独立信息,先保存最后一个非空统计块。
if chunk.usage is not None:
final_usage = chunk.usage
if final_usage is None:
# 中断时不要伪造 0;保留“未收到统计”的状态供重试或补记。
print("usage unavailable")
else:
print({
"prompt_tokens": final_usage.prompt_tokens,
"completion_tokens": final_usage.completion_tokens,
"total_tokens": final_usage.total_tokens,
})
如果统计对计费或配额很重要,可以同时记录请求是否完整结束,并把缺少最终 usage 的记录标记为待补偿状态。不要只因为流中已经收到文本,就认为本次 token 统计也完整。

上线前用四个判断避免统计失真
- 入口判断:Responses 读
response.usage,Chat Completions 读completion.usage。 - 空值判断:usage 为空时记录未知,不要自动写成 0。
- 明细判断:缓存和推理字段是总量拆分,不能重复加入总量。
- 流式判断:只在收到最终 usage 块后提交完整统计;中断记录应保留缺失原因。
官方 token 统计说明还指出,报告的输出 token 不只等于用户可见文本,某些模型的格式、工具调用或其他内部结构也可能计入输出量。因此不要用字符串长度或可见词数反推 API 返回值。
常见问题
为什么 response.usage 是 None?
usage 是可选结果,流式响应尚未结束、请求被中断,或当前响应形态没有返回统计时都可能为空。把它记录为未知比填 0 更可靠。
cached_tokens 要不要加到 input_tokens 上?
不要。cached_tokens 是 input_tokens 的明细,应该用于分析缓存命中,而不是作为额外输入再次相加。
为什么输出文字不多,output_tokens 却更大?
API 统计的是生成的总输出 token,可能包含不可见的结构、工具调用或推理相关 token,不能只按最终展示文本估算。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
292 收藏
-
378 收藏
-
455 收藏
-
科技周边 · 人工智能 | 58分钟前 | python · 人工智能 · snapshot_download Hugging Face Hub HF_HUB_CACHE HF_HOME 模型缓存 huggingface_hub247 收藏
-
113 收藏
-
174 收藏
-
327 收藏
-
131 收藏
-
268 收藏
-
349 收藏
-
297 收藏
-
437 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习