streaming-usage-accounting · ZH · 2026-10-14

用量会随最后一个 chunk 到达吗:流式过程中如何跟踪 token 与 USDC 花费

从流式响应的最后阶段出发,解释 usage 字段在什么时候出现、为什么它可能滞后或缺失,以及如何在不额外调用账单接口的前提下,把 token 用量与 ×1.3 的 USDC 扣费对账。

先说结论

流式响应里的 token 用量通常不会在第一个 chunk 出现,而是集中在流的尾部:要么是最后几个 chunk 中携带一个 usage 字段,要么是独立的结束事件(如 message_delta、final 之类)里一次性给出。也就是说,最后一个 chunk 到达,并不等于用量已经到达;用量可能在同一批数据的另一个事件里,也可能被拆成「输出 token 先到、输入 token 后到」两次。

如果你的聚合站按官方价 ×1.3 扣 USDC,那么对账的关键不是「等最后一个 chunk」,而是「确认 usage 事件是否完整」。

为什么用量容易滞后或缺失

几种常见情况:

  • 流式设计本身:很多实现在生成结束后才计算完整用量,所以它天然出现在末尾事件中。
  • 中途断开:客户端超时、连接被中断、提前取消请求,都可能让你拿不到末尾的 usage。
  • 代理或中转层:聚合站作为中间层,有时会改写或重新封装事件流。用量可能在原厂事件里,也可能由中转层在收尾时补发。
  • 按块计费的模型:部分模型或工具调用会拼接多次内部请求,用量可能分散在多段事件中。

遇到「最后一个 chunk 没有 usage」,不一定是接口坏了,先确认流的结束事件是否真的到达。

流式过程中怎么跟踪

一个稳妥的做法是把「内容」和「用量」分开处理:

  1. 逐 chunk 累积文本:只用于展示,不要用它来估算 token。
  2. 监听结束事件:显式识别 finish_reason、stop、done 之类标记,以及携带 usage 的事件。
  3. 把 usage 当作可选字段:先判断是否存在,再读取输入、输出、总量。
  4. 记录请求级快照:给每个请求一个本地 ID,在收到 usage 时把值写回该 ID。

伪代码式流程:

for event in stream:
  if event.delta:
    append(event.delta)
  if event.usage:
    record(request_id, event.usage)
  if event.finish:
    mark_done(request_id)

这样即使最后一个 chunk 不带用量,只要结束事件里带了,你就能在流关闭前抓住它。

什么时候拿不到用量

以下情况要有兜底:

  • 请求被客户端主动取消,流提前关闭。
  • 网络中断,结束事件丢失。
  • 上游返回了错误,但错误事件里不含 usage。
  • 中转层在特定模型上不转发 usage。

兜底策略:把「无 usage 的成功请求」单独标记,而不是当成零消耗。零消耗和未知消耗是两件事。

与 ×1.3 USDC 扣费对账

聚合站按官方价 ×1.3 扣费,贡献 key 的人按 ×1.1(优质 ×1.2)拿返 USDC。这意味着同一份 token 用量,对使用者和贡献者的计价方向不同。对账时可以按这几个层次做:

  • 先对齐 token:确认你拿到的 input / output 是否和官方口径一致(有些实现会把缓存命中、推理 token 单独拆出)。
  • 再对齐单价:把官方价乘以 1.3 得到你的实际扣费口径。
  • 再看币种与时间:USDC 余额变化可能不是实时同步的,扣费时间点和 token 产生时间可能错开。
  • 最后看汇总粒度:是按请求、按小时还是按天聚合,不同粒度下对不上的原因不同。

不要用「官方价 ×1.3 反推 token 数」来做逐请求核对,更稳的方式是先有 token 用量,再用它乘以 1.3 得到预期扣费。

不靠账单接口也能做的自检

如果你不想额外调用账单接口,可以只靠流式数据做轻量核对:

  1. 给每个请求记录:模型、输入 token、输出 token、时间。
  2. 按官方价 ×1.3 算出该请求的预期 USDC。
  3. 在本地累计,和余额变化做周期性比对。
  4. 只对差异较大的请求做人工排查,而不是逐条对。

这样能在不依赖单独账单接口的情况下,发现明显的用量或扣费异常。

实践建议

  • 不要假设「最后一个 chunk = 用量」。把结束事件和用量事件当作两个独立信号。
  • 把 usage 缺失当作一种正常状态处理,并记录下来。
  • 对流式请求保持请求级 ID,方便把用量、扣费和日志串起来。
  • 对账时先看 token,再看 1.3 倍,最后看 USDC 余额变动时间。

做到这几点,即使聚合层不提供额外的账单接口,你也能用流式响应本身把 token 与 USDC 花费基本对上。