前端点停止后浏览器 abort 了流式请求,服务端日志显示生成到 1200 token 才停,但这条请求的 usage 是空的,月底账单比自建统计多 30%。
一开始怀疑是「按生成 token 计费,与是否读完无关」,查了一圈,方向不对。
把日志和配置对完,能解释这个现象的基本就这几处。客户端断开后服务端仍会继续生成一段。还有一种是usage 只在完整响应结束时返回。
不容易想到的是,abort 后网关自动重试,同一请求被计费两次。
上来先抓一次断开时的连接关闭时间
curl -N --max-time 3 http://127.0.0.1:8000/v1/chat/completions -H 'Content-Type: application/json' -d @stream.json
再往下打开 stream_options 让流里带 usage
curl https://api.openai.com/v1/chat/completions -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d @stream_usage.json
随后在服务端监听 disconnect 并主动停止生成
python -c "import asyncio;print('hook request.is_disconnected')"
等前面都确认过,再关掉网关对 POST 的自动重试
curl -s http://127.0.0.1:9901/config_dump | Select-String retry_policy
恢复的判定其实很直接。带上 stream_options 后每条请求都有 usage,服务端在断开后 200ms 内停止生成,账单与自建统计的差异收敛到 3% 以内。
容易忽略的是,用户点停止不等于停止计费,服务端要主动感知断连,客户端统计也要按 usage 而不是按看到的字数。如果同一现象隔几天又出现,说明根因没找到,别当偶发处理。
