Token计费怎么算:把一次大模型 API 调用的账单逐项拆开

博客 / 模型观察

Token计费怎么算:把一次大模型 API 调用的账单逐项拆开

大模型 API 的 token计费,公式本身很简单:费用 = 输入 token 数 × 输入单价 + 输出 token 数 × 输出单价,有缓存的平台再把命中缓存的那部分输入按缓存价单独计算。难的是“输入 token 数”往往比你以为的大得多,因为它不只是你这一轮打的字,还包括系统提示词、全部对话历史和工具定义。这篇文章从接口返回的 usage 字段讲起,把一次调用的账单拆开,最后给一张可以照着填的估算表。

一次调用到底按什么收费?

几乎所有主流接口都会在响应里带一个 usage 对象,它就是计费的原始依据。字段名各家略有不同,常见的有两套:

含义OpenAI 兼容格式Anthropic 格式
输入 tokenprompt_tokens(含缓存命中部分)input_tokens(不含缓存部分)
缓存读取prompt_tokens_details.cached_tokenscache_read_input_tokens
缓存写入一般不单列cache_creation_input_tokens
输出 tokencompletion_tokensoutput_tokens
其中推理 tokencompletion_tokens_details.reasoning_tokens计入 output_tokens

注意两点。第一,OpenAI 兼容格式里 prompt_tokens 通常已经包含了缓存命中的部分,算钱时要先减掉 cached_tokens,再分别乘单价;Anthropic 格式则是三项分开给。第二,推理模型“想”的那部分 token 不一定显示给你,但按输出价计费。字段的确切含义以各家官方 API 文档为准,接入前先核对一遍。

输入 token 里装了哪些东西?

实际账单里,你自己这一轮输入的内容常常只占很小一部分。一次请求的输入通常由这几块拼成:

  1. 系统提示词:每次请求都会完整发送。Agent 类工具的系统提示词可能有几千 token。
  2. 工具定义:开了函数调用或 MCP,每个工具的名称、描述和参数 JSON Schema 都算输入。工具一多,这部分很容易超过系统提示词。
  3. 对话历史:接口是无状态的,模型“记得”上文,是因为客户端把之前所有轮次重新发了一遍。工具调用的返回结果(比如读进来的整个文件)也在历史里。
  4. 当前输入:你这一轮打的字、贴的代码、上传的图片(图片也会折算成 token)。

输出部分则是:

  1. 可见输出:你看到的回答。
  2. 推理输出:推理模型内部的思考过程,按输出价计费。

一个标注为“示例”的计算

下面用假设的数字算一遍。单价取输入 2 美元 / 百万 token、输出 10 美元 / 百万 token,这个组合落在 cdnping 于 2026 年 10 月 17 日从 OpenRouter 公开 API 取得的价格快照区间内;缓存读取价假设为输入价的十分之一,实际倍率请看各家价格页。

组成token 数(示例)计价方式费用(美元)
系统提示词1,500输入
工具定义3,000输入
对话历史12,000输入
当前输入500输入
输入合计17,000无缓存时 × 2 / 1M0.034
可见输出800输出
推理输出1,200输出
输出合计2,000× 10 / 1M0.020
单次合计0.054

假如前 15,000 个输入 token(系统提示词、工具定义和大部分历史)命中缓存:

  • 缓存读取:15,000 × 0.2 / 1M = 0.003
  • 未命中输入:2,000 × 2 / 1M = 0.004
  • 输出不变:0.020
  • 合计 0.027,大约是无缓存时的一半。

这个例子里,你这一轮真正“说的话”只有 500 token,不到输入的 3%。

为什么多轮对话越聊越贵?

因为每一轮都要把之前的全部历史重新发一遍并重新计费。假设固定前缀(系统提示词 + 工具定义)是 4,500 token,每轮问答新增 1,000 token 历史:

  • 第 1 轮输入约 4,500
  • 第 10 轮输入约 13,500
  • 第 20 轮输入约 23,500

20 轮的累计输入是 4,500 × 20 + 1,000 × (0 + 1 + … + 19) = 90,000 + 190,000 = 280,000 token。其中历史部分按平方级增长:轮数翻倍,历史带来的累计输入接近翻四倍。编程 Agent 读文件、跑命令、看日志,每次工具返回都塞进历史,增长更快。

能做的事情:

  • 一个任务完成就开新会话,别在一个窗口里聊一整天。
  • 用客户端的压缩或摘要功能,把旧历史换成短摘要。
  • 只启用当前任务需要的工具,减少每次都要发送的工具定义。
  • 把不变的内容放在请求前部,让缓存能命中,具体做法见 Claude API 提示缓存怎么用才省钱。

max_tokens 管什么,不管什么?

max_tokens(有的接口叫 max_output_tokens 或 max_completion_tokens)只限制输出的上限,不限制输入。

  • 能做到:防止模型一次输出过长,给单次请求的输出费用设一个上限。
  • 做不到:减少输入费用。历史有 10 万 token,设多小的 max_tokens 输入照样按 10 万算。
  • 对推理模型,推理 token 一般也占用这个额度。设得太小,可能推理还没结束就被截断,可见回答是空的,但已经生成的 token 仍然计费。
  • 截断也收钱:响应里 finish_reason 为 length(或 stop_reason 为 max_tokens)时,说明被截断了,这次的输出照常计费。

想控制总成本,要管的是输入那一侧:历史长度、工具数量、缓存命中率。

怎么用 usage 字段自己对账?

平台后台的账单是按天或按月汇总的,出了偏差很难定位到某次请求。最可靠的办法是在自己的代码里把每次的 usage 记下来。

第一步:看一次请求的原始 usage

curl -s https://你的接口地址/v1/chat/completions \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"你的模型","messages":[{"role":"user","content":"你好"}],"max_tokens":50}' \
  | jq '.usage'

流式请求(stream: true)默认可能不返回 usage,OpenAI 兼容接口通常要加 "stream_options": {"include_usage": true},usage 会出现在最后一个数据块里。

第二步:每次调用落一行日志

import json, time

PRICE = {"in": 2.0, "cache": 0.2, "out": 10.0}  # 示例单价,美元/百万 token,换成你实际的

def log_usage(model, usage, path="usage.jsonl"):
    cached = (usage.get("prompt_tokens_details") or {}).get("cached_tokens", 0)
    prompt = usage.get("prompt_tokens", 0)
    out = usage.get("completion_tokens", 0)
    cost = ((prompt - cached) * PRICE["in"] + cached * PRICE["cache"] + out * PRICE["out"]) / 1e6
    row = {"ts": int(time.time()), "model": model, "prompt": prompt,
           "cached": cached, "out": out, "est_cost": round(cost, 6)}
    with open(path, "a") as f:
        f.write(json.dumps(row) + "\n")

Anthropic 格式把字段换成 input_tokens、cache_read_input_tokens、cache_creation_input_tokens、output_tokens,缓存写入要按写入价单独算。

第三步:按天汇总,和后台账单比

jq -s 'group_by(.ts / 86400 | floor) | map({day: (.[0].ts/86400|floor), cost: (map(.est_cost)|add), prompt: (map(.prompt)|add), out: (map(.out)|add)})' usage.jsonl

对账时的检查表:

  • 自己记录的请求数和后台的请求数是否一致(重试、超时后客户端重发都算请求)
  • 流式请求是否都拿到了 usage,有没有漏记
  • 缓存命中的 token 有没有按缓存价算,缓存写入有没有单独算
  • 推理 token 有没有计入输出
  • 单价是否是最新的,平台有没有调价
  • 偏差明显时,挑几条请求逐条核对

如果对不上,常见原因和逐条核对方法在 AI API 账单对不上:token 计数口径与核对方法 里有更细的讲法。单价会变,可以在 cdnping 的 官网动态 页面跟踪各家价格页的逐行变化,模型监测 页面会记录 OpenRouter 目录里的调价;完整的追踪思路见 大模型 API 价格变了怎么第一时间知道。

单价差多少,值得在意吗?

以 cdnping 在 2026 年 10 月 17 日从 OpenRouter 公开 API 取得的快照为例(单日快照,不代表长期价格),我们跟踪的 16 个重点模型里,输入单价从 0.1 到 4 美元 / 百万 token,输出单价从 0.47 到 12 美元 / 百万 token,上下文窗口在约 26 万到 105 万 token 之间。同样一次请求,选不同的模型,费用可以差出几十倍。

另外两个规律:

  • 输出通常比输入贵。快照里多数模型的输出单价是输入的 3 到 5 倍,个别模型的差距更大。所以推理模型“想得多”,费用涨得快。
  • 便宜的模型不一定省钱。如果它要多试几次才能做对,重试的输入和输出都要再付一遍。

按任务难度把请求分到不同价位的模型,是比压提示词更有效的省钱方式,做法见 多模型路由怎么做;比较价格时容易踩的坑见 大模型 API 价格怎么比。

不按 token 计费的方式怎么换算?

有些平台不直接按 token 报价,而是用自己的计量单位。以 AGI Plan 为例(AGI Plan 是 cdnping 的商业合作方):计量单位是 CU,1 CU = 基准模型 1,000 个输出 token;按量付费时 1 元 = 500 CU。其他模型和输入 token 如何折算成 CU,以 https://agiplan.dev/pricing 和 https://agiplan.dev/models 为准。

遇到这类计量方式,换算思路是一样的:先弄清楚单位对应的是输入还是输出、是哪个基准模型,再用自己日志里的 usage 去核对实际扣减。

成本估算表模板

上线一个功能前,把下面这张表填一遍,基本能估出量级。

项目填写说明
系统提示词 token用接口发一次只含系统提示词的请求,看 usage
工具定义 token带工具和不带工具各发一次,相减
平均每轮新增历史 token用户输入 + 模型输出 + 工具返回
平均每会话轮数 n
每会话累计输入前缀 × n + 每轮新增 × n(n−1)/2
预计缓存命中比例没开缓存填 0
平均每轮输出(含推理)看 completion_tokens / output_tokens
输入 / 缓存 / 输出单价抄官方价格页,记下日期
每会话费用按上文公式
每日会话数 × 30月度估算
重试率月度估算 × (1 + 重试率)

估完之后,上线第一周用日志里的真实 usage 回填这张表,如果历史和工具定义比预估的大,先从这两项优化。

相关文章

继续阅读