这类问题我又碰上了一次。用 anthropic 0.39 的 SDK 开 stream=True 统计账单,message_delta 事件里的 usage.output_tokens 始终是 0,input_tokens 也只在开头出现一次,月底对账差了近三成。

第一反应是「中途断开没读到 message_stop,最后一次统计被丢掉」,验证下来不成立。

排查方向其实有限,先把可能性排一排。只有 message_start 里的 usage 带输入计数,后续增量事件不带;客户端把每个 chunk 的 usage 累加,实际要取最后一次快照;用了流式但没开 include_usage 等价开关,服务端不返回明细。

第一件事是把事件类型和 usage 原样打到日志里对照

python -c "import anthropic;c=anthropic.Anthropic();s=c.messages.create(model='claude-3-5-sonnet-20241022',max_tokens=64,stream=True,messages=[{'role':'user','content':'hi'}]);[print(e.type,getattr(e,'usage',None)) for e in s]"

然后改成只保留最后一次非空的 usage 快照

python -c "import json;d=json.load(open('events.json'));print([e for e in d if e.get('usage')][-1])"

末了在 message_stop 之后才落账,避免半截数据入库

grep -c 'message_stop' stream.log

手上没有现成的复现脚本,就照着线上请求手工拼了一个。

改完先别撤,确认这几项。同一请求非流式与流式两次统计的 input/output 数量完全一致,message_start 与 message_delta 的 usage 能对上,差额归零。

提醒一点。流式响应的 usage 是快照不是增量,累加会把输出数算成零,账目对不上时先看这一行代码。这类问题最好一次改到位,反复小改反而容易把原因搅混。

作者 9823189