跳到正文
上新GPT Image 2.6 Flare / Sunburst 已上线:支持动图 GIF、矢量 SVG,官方价 7.5 折查看模型
HopBase
← 返回博客

大模型 API 用量对账与部门分摊:实扣金额、明细验算、按 Key 分摊 | HopBase

大模型 API 用量对账与部门分摊:实扣金额、明细验算、按 Key 分摊 | HopBase

先说答案:大模型 API 的账要对得上,靠的不是服务商的「透明计费」口号,而是三层能自己复算的数据。第一层是单个任务:在 HopBase,视频、异步生图等异步任务结束后,查询接口的响应里直接带 usage.cost_usd(这一单从 USD 余额实际扣掉的金额,与使用记录一致),失败且未扣费的任务报 0,用一条 curl 就能核。第二层是逐笔明细:控制台「使用记录」可按日期、Key、成员、部门、创建人筛选后导出 CSV,每行都带官方费用、折扣、折算率和扣费金额,可以按「官方费用 × 折扣 ÷ 折算率 = 扣费金额」逐行验算。第三层是分摊:每把 Key 显示创建人、可以按 Key 看消耗趋势图,部门负责人能看、能管本部门成员的 Key 和用量,额度可以按 Key、成员、部门分别设。三层对齐了,财务按部门分摊就只是一次筛选加导出。

为什么大模型账单难对

一家公司同时用对话、生图、视频模型时,计费单位至少有四种:对话按 token,生图多按张,视频按秒或按 video token,还有一些按次收费。再叠加几个现实问题,账就很难对上:

  • 异步任务的钱在「结束时」才确定:视频提交时只是预估,真正扣多少要等任务完成;失败的任务是否扣费、扣多少,各家口径不同。
  • 失败请求的口径:请求失败一般不计费,但如果客户端在输出已经生成之后才断开,已生成的部分通常要计费。账单里如果不标清楚,就会出现「明明报错了还扣钱」的争议。
  • 多币种:官方牌价多是美元,账户余额可能是另一种币种,中间差一个折算率;少了这个除数,任何人都没法复算。
  • 多人共用:一把 Key 被多个项目共用,事后就分不清是谁花的。

所以对账的目标很具体:任何一笔钱都能追到「哪个任务 / 哪次请求、哪把 Key、谁建的 Key、属于哪个部门」,并且金额能用公开牌价复算出来。

第一层:单个任务,查询响应里直接拿实扣金额

异步任务(视频、异步生图等)结束后(完成、失败或取消),单任务查询接口的响应根对象会多出一个 usage 对象:

  • usage.cost_usd:这个任务从 USD 余额实际扣掉的金额,和使用记录里的扣费金额是同一个数,对账以它为准;
  • usage.cost:同一笔费用的人民币金额;usage.currency 恒为 CNY,只标注 cost 这个字段的币种,不代表余额币种;
  • 失败且没有扣费的任务报 0;个别生图任务在扣费结算之后才失败(比如模型没有返回可用图片),报的是实际扣费;
  • 任务进行中不返回这个字段;而且只有创建任务的那把 Key 查得到。

覆盖的查询路径:GET /v1/video/tasks/{task_id}(Seedance、Grok Imagine、可灵、MiniMax、Midjourney)、GET /api/v1/tasks/{task_id}(万相、快乐马)、GET /v1/images/tasks?task_id=...(GPT Image 与 Gemini 生图的异步任务)。自己核一下只要一条命令:

# 用提交任务的那把 Key 查询;jq 只挑出状态和费用
curl -s https://api.hop-base.com/v1/video/tasks/$TASK_ID \
  -H "Authorization: Bearer $HOPBASE_API_KEY" \
  | jq '{status: (.task.status // .status), usage}'

# 任务结束后的输出形如(金额仅作示意):
# { "status": "completed", "usage": { "cost": 2.4, "cost_usd": 0.352941, "currency": "CNY" } }

Seedance 的响应里 task.usage 是计费用的 token 数,根对象的 usage.cost_usd 才是金额,两者别混;MiniMax、万相这类本来就有根 usage 的,cost_usd、cost 与 currency 会合并进去。这一层的用处是:业务系统可以在任务结束时把实扣金额直接写进自己的订单或工单,不用再定时去控制台翻记录,也不用拿提交时的预估当成本。

第二层:逐笔明细,导出 CSV 并逐行验算

同步的对话请求不产生任务:非流式响应体同样带 usage.cost_usd / cost / currency,并有响应头 X-HopBase-Cost-USD;流式响应没有这些字段(扣费要等流结束后才确定),金额以使用记录为准。控制台「使用记录」页的导出按钮会按页面当前的筛选(日期区间、Key、成员、部门、Key 创建人)导出 CSV,单个文件最多 5 万行、最长 400 天。每行 13 列:

  1. 时间、模型、用量(输入+缓存读写+输出合计 token);
  2. 官方牌价币种、官方费用(原币);
  3. 折扣、折算率、扣费金额(USD);
  4. 说明;
  5. API Key、密钥状态、使用者、部门。

中间四列从左到右正好读成验算式:官方费用(原币)× 折扣 ÷ 折算率 = 扣费金额。折算率为 1 时留空,等式退化成「官方费用 × 折扣 = 扣费金额」。官方费用可以拿厂商官方价格页自己算,折扣就是你所在分组的折扣,所以每一行都能独立复算,不需要相信任何人的汇总数。以下两类行这四列留空(不写 0):没有牌价快照的早期记录、按张/按次固定价计费的调用。

说明列会标注三类行:失败请求写「请求未成功,未计费」;客户端中途断开、但输出已经生成的请求写「请求中断,按已产生用量计费」;按次计费的非文本调用写「非文本调用,按次计费」。这正是最容易起争议的两类行,把口径写在明细里,比事后解释省事得多。需要自动化时,GET /v1/usage 用 API Key 就能查余额和这把 Key 的额度用量。

第三层:按 Key、按部门分摊

分摊的前提是「一个成本中心一把 Key」。HopBase 的企业账户里有成员和部门,几个事实决定了分摊能不能落地:

  • 每把 Key 显示创建人:成员自己建的、企业主或部门负责人代建指派给成员的,都分得开;导出也能按创建人筛。
  • 按 Key 看消耗图表:控制台可以按 Key 查看消费趋势、请求数和模型分布,异常消耗一眼能看出来是哪把 Key。
  • 部门负责人的范围:部门负责人能看到并管理本部门成员的 Key 和用量,能给本部门成员建 Key;看不到别的部门。企业主看全公司。成员调岗后,负责人的范围随之变化。
  • 分层额度:Key、成员、部门都可以设额度,任何一层用完都会拦截;超额时返回 402,信息里写明是 Key 额度、成员额度还是部门额度用完,不会只丢一个笼统的「余额不足」。

月底给各部门出账,就是在使用记录里选部门、选月份、导出;每个部门拿到的是自己那份明细,并且可以用上一节的等式自己复算。

一份可以拿去核任何服务商的对账清单

  1. 异步任务结束后,能否从 API 直接拿到这一单的实扣金额,而不是预估或基础成本?
  2. 明细能否导出,是否包含官方费用、折扣、折算率,能否逐行复算?
  3. 失败请求与「中途断开但已生成」的请求,明细里是否分开标注?
  4. 金额字段有没有写明币种?牌价币种与余额币种不同时,折算率是否可见?
  5. 能否按 Key、成员、部门筛选导出,Key 有没有创建人?
  6. 额度能否分层设置,超额报错能否看出是哪一层?
  7. 你付费调用的是不是你以为的那个模型?这一项可以用API 保真检测方法自己测。

这份清单每一项都能用 curl 或导出文件验证,拿去评估任何服务商都适用,包括我们。

常见问题

任务查询里的 usage.cost_usd 和控制台的扣费金额一样吗?

一样。usage.cost_usd 取的就是这笔任务使用记录里的实扣金额,也就是 USD 余额真正减少的数,可以直接拿来和导出明细逐笔对;usage.cost 是同一笔的人民币金额,仅供参考。

为什么任务还在跑的时候看不到 usage.cost_usd?

异步任务的最终费用要等任务结束才确定,进行中返回的任何数字都只能是预估。所以只在完成、失败或取消之后返回;进行中的任务响应里没有这个字段,按原来的逻辑继续轮询即可。

失败的任务会扣费吗?

视频任务失败一律不扣费,查询里 usage.cost_usd 报 0。生图任务极少数情况下会在扣费结算之后才失败,这时报的是实际扣费,使用记录里也能看到同一笔。同步请求失败不扣费;客户端中途断开但输出已经生成的,按已生成的部分扣费,并在导出明细里单独标注。

换一把 Key 查同一个任务,为什么没有费用字段?

费用只对创建任务的那把 Key 返回。这是为了让「一个成本中心一把 Key」的分摊边界不被打破,也避免同账户下其他 Key 看到不属于自己的消耗。

对话请求有没有类似的单笔金额?

非流式有:响应体带 usage.cost_usd,响应头带 X-HopBase-Cost-USD。流式没有(扣费要等流结束后才确定),请看控制台使用记录,导出 CSV 可以逐行验算;按 Key 或部门汇总,用筛选加导出即可。