usdc-balance-key-continuity · ZH · 2026-10-22

调用进行中余额归零:客户端看到什么、哪些还会计费、同一把 key 上怎么续跑

从一次跑到一半余额归零的请求出发,讲清楚 OpenAI 兼容客户端会看到什么错误、为什么部分 token 仍会计费、以及同一把 key 上最安全的续跑方式(用 request id 对账、缩小 max_tokens、避免盲目重放)。

先区分两件事:请求被拒 vs 请求跑到一半

余额归零发生在不同阶段,客户端的现象完全不同。

  • 调用前预检失败:请求还没进入模型推理就被拒。通常返回 HTTP 402(Payment Required)或 403,body 里是类似 insufficient_quota / 余额不足的错误对象。此时没有 token 被消耗。
  • 推理中途余额耗尽:请求已经开始,正在流式返回或等待上游响应,此时账户余额被扣穿。这时不会抛出一个干净的 4xx,而是表现为连接中断或流在中间停住。

判断依据不是错误文案,而是响应是否已经开始。只要 HTTP 状态码和首个 token 已经发出去,后面就无法再改写成 402。

OpenAI 兼容客户端里,你会看到什么

不同客户端对「连接已建立但没收到完整响应」的处理不一致,常见几种表现:

  • 非流式请求:客户端在等待完整 JSON 时连接被关闭,多数 SDK 抛 APIConnectionError / ConnectionError 或超时异常,而不是一个带 error 字段的响应体。
  • 流式请求:已经收到若干 data: 分块后连接断掉,多数 SDK 表现为迭代器提前结束或抛连接异常;没有 [DONE]。
  • 重试层误判:不少 SDK 对连接错误默认自动重试。这是最容易出问题的地方——见下文。

所以正确的认知是:中途断流不是一个「余额不足」错误,它是一个传输层异常。靠错误文案判断余额状态并不可靠,应该去控制台看余额与用量记录。

哪些还会计费

计费发生在模型侧,不取决于你的客户端有没有收全。

  • 已经送入模型并产生输出的 token,通常都会被计入。输入 token 在请求进入推理时就已确定。
  • 流式输出中途断开,已经生成并返回的部分输出 token 一般会计费,没生成的部分不计。
  • 预检阶段被拒的请求不产生 token 计费。
  • 聚合站在官方价基础上按固定倍率扣费,计费口径跟随上游的用量回报,因此「客户端没收到完整结果」不等于「没有用量」。

实际结论:一次断流请求很可能同时造成部分扣费 + 不完整结果。这也是为什么盲目重放代价高。

最危险的续跑方式:原样重发

很多人第一反应是把同一份 messages 再发一遍。问题在于:

  1. 断流前那部分输入 token 可能已经被计费,重发等于再付一次全部输入。
  2. 如果这次成功,两次调用的输出可能重叠,你在业务侧会看到重复内容。
  3. 如果 SDK 已经自动重试过,你可能已经在不知情的情况下发了好几遍。

所以先做一件事:关掉或收紧 SDK 的自动重试(例如把 max_retries 设为较小值),避免余额边界上的请求被放大成一串。

更安全的续跑策略

按代价从低到高排列:

1. 先确认余额与用量,再决定要不要续

查控制台余额和最近的用量记录,确认那笔请求是否已被计费、扣了多少。有 request id 的话用它对齐。不要凭客户端异常判断。

2. 缩小范围重试,而不是整段重放

如果任务可以切分,把剩余部分单独发一个小请求,而不是把原始长上下文整体重发。上下文越长,重放的输入成本越高。

3. 用 max_tokens 限制单次消耗

给每个请求设置明确的 max_tokens,让单次调用的最坏消耗可预期。这样即使断流,损失也是被限定的,而不是取决于模型想输出多长。

4. 让输出可续,而不是可重来

把长任务拆成多轮:每轮输出有明确边界(例如分段、分条),客户端拿到完整一段就落盘。断在哪里,就从下一个边界继续,不依赖「把上次输出再喂回去重来」。

5. 续跑时在提示里标出已完成部分

如果必须带上历史,明确告诉模型哪些部分已经完成、只要求产出剩余部分,减少重复输出。这不能减少输入 token,但能减少输出浪费和业务侧去重成本。

同一把 key 上的操作顺序

一个稳妥的顺序是:

  1. 停止自动重试,阻止请求被连环放大。
  2. 查余额与用量,确认断流那笔的计费状态。
  3. 充值(本站支持 Base 上 USDC 充值,无 KYC),或换用余额充足的 key。
  4. 用缩小的输入 + 明确的 max_tokens 续跑剩余部分。
  5. 落盘结果,记录 request id,便于后续对账。

设计层面的预防

  • 给业务加余额阈值告警,在接近耗尽前就补,而不是等 402。
  • 长任务默认流式 + 分段落盘,别把一次请求当成一个事务。
  • 把 request id 记进日志,这是断流后唯一可靠的对账依据。
  • 对连接错误和 4xx 分开处理:前者是「可能已计费的不完整结果」,后者才是「没开始」。

一句话总结

余额归零不是一个错误码,而是分阶段的:预检失败不花钱,中途断流会部分计费且结果不完整。续跑的关键不是重发,而是先对账、再缩小请求、把输出做成可续的。