账单里 input token 是 128 万,本地用 tiktoken 0.7 的 cl100k_base 算出来只有 105 万,差 18%。接口是 OpenAI 兼容网关,模型名写的是 gpt-4o-mini,Python 3.11,请求里带了 6 段 system 和三个 tools 定义,本地计数只把 messages 里的 content 拼起来算。月底对账时财务问这 23 万差额从哪来,按本地数字做的预算也已经超支。
原因不止一个,得分开看。本地只算 content,漏掉 role、name 这些消息包装开销;tools 的 JSON schema 同样计入 input,本地完全没算;网关侧用的是另一套 tokenizer,和 cl100k 词表不同;重试与失败请求也计费,账单把重试一起算进去了。
先按消息结构整体编码而不是只拼正文
python -c "import tiktoken;e=tiktoken.get_encoding('cl100k_base');print(len(e.encode(open('msg.json').read())))"
然后把 tools schema 序列化后单独算一遍
python -c "import json,tiktoken;e=tiktoken.get_encoding('cl100k_base');t=json.load(open('tools.json'));print(len(e.encode(json.dumps(t,ensure_ascii=False))))"
再往下用接口返回的 usage 作为基准逐条比对
curl -s https://gw.local/v1/chat/completions -H 'Content-Type: application/json' -d @body.json | jq '.usage'
剩下的就是把差异按请求类型落到表格里找最大偏差
python diff_usage.py --local local.json --remote usage.jsonl --out diff.csv
恢复的判定其实很直接。抽样 50 条请求,本地估算与 usage 里 prompt_tokens 的偏差压到 2% 以内,账单金额和日志汇总能对得上。
容易忽略的是,计费口径以服务端返回的 usage 为准,本地估算只适合做预算和切片;换模型或换网关之后 tokenizer 必须重新校准,否则差出来的比例会一直跟着预算走。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。
