核验清单

  • ✓打开这一次的响应。若 content 是空的且 stop_reason 为 refusal,读 stop_details.category。对照拒答文档里「出字前是否计费」那一列:bio、frontier_llm、reasoning_extraction 记入账,cyber、general_harms 和空类别不记入账。两种都去看速率限制有没有少掉一次额度。
  • ✓如果正文已经流出一段,随后才出现 refusal,按已流出的输入和输出对账,并把这段半截答案丢掉。不要把它当成一次完成的交付。
  • ✓自己重试时,先看有没有 fallback_credit_token。Message Batches 的拒答和 Claude Haiku 5.5 的拒答不发这个令牌。没有令牌,下一次会按回退模型把提示缓存整段重写。
  • ✓OpenAI 若开了 stream_options.include_usage,流在 [DONE] 之前断掉,就不要把缺失的 usage 块记成 0。去用量页对这一段时间。 finish_reason 为 content_filter 表示过滤器省略了内容,它不是 HTTP 错误。
  • ✓网络中断后的续写是一次新请求。工具调用块和思考块不能只接半截。不要把这笔和训练任务的取消账单并成同一列。

1. 出字之前,三类仍入账

拒答与回退把出字前的账单写成一张表,而且写明这套规则各平台都适用。分类器在任何输出之前拒绝时,stop_details.category 为 bio、frontier_llm 或 reasoning_extraction 的,按跑过的那个模型正常计费。页面说明,这是截至 2026 年 9 月误报量被测得较低的几类,用来打断大规模绕过防护的尝试。cyber、general_harms,以及不对应具名类别的空值,出字前不计费。content 在这两种情况下都是空的,usage 里却都会出现 token 数。不计费的那一列,数字只是摆在响应里,不能拿去当发票。计费的那一列,同样的字段才是这次的用量。请求无论哪一列,都仍占用速率限制。

空类别不是「还没分类、过会儿会补」。文档写,类别和说明同时为空,是正常且持久的值,不是占位。对账时把空值并进「其余类别不计费」,不要等一个稍后才出现的类别名。计费类别会随误报测量调整,以表上「出字前是否计费」那一列为准,不要把今天的三行抄成永久价目。

reasoning_extraction 通常来自要求模型把思考写进正文的提示:先填一段草稿再回答、JSON 里的 reasoning 字段、逐字要推理过程。措辞可以藏在系统提示、技能或工具描述里。这一类没有推荐的回退模型,文档要求改提示,而不是原样重试。想看推理过程,应把思考的 display 设为摘要去读思考块。这和看不见的推理 token 仍按输出价计不是同一笔:那边是模型内部推理按输出计价,这边是分类器因为你要求把推理写进答案而拒答,并且在一个字都没出时就可能入账。

2. 流到一半再拒,按已流出的量计

同一页写,拒答可以发生在任何输出之前,也可以发生在流的中途、已经有部分输出之后。两种都要把部分输出当成不完整并丢弃。中途拒答按正常价格计已消耗的输入 token,以及已经流出去的输出。用户屏幕上看到半句然后变成拒答,账单不是「没交付所以为零」,而是「交付被截断,截断前的量照付」。

流式拒答仍是成功的 HTTP 200,stop_reason 为 refusal,stop_details 落在 message_delta 上。只监控 4xx 和 5xx 的告警不会响。Message Batches 里,被拒的请求以 result.type 为 succeeded 返回,同样带 stop_reason: refusal。把它当成批处理错误行去找退款,会找错文件。批处理结果也不铸造回退信用,stop_details 里不会有 fallback_credit_token。

3. 回退信用抵不掉这次拒答

用了回退时,触发回退的那次拒答,只要是流中途,或落在出字前仍计费的类别里,就要和回退请求分开入账。信用补偿的是回退请求的提示缓存未命中,让你不必为同一段对话付两次缓存写入。它不把第一次拒答改成免费。Claude Haiku 5.5 的拒答不带信用,回退之后要按回退模型付整段缓存写入。服务端回退在 Claude API 和 Anthropic 托管的 Google Cloud 上处于 beta,Message Batches 不支持 fallbacks 参数,带上它的批次项会作为错误结果回来。Haiku 5.5 也没有服务端回退:fallbacks 设为 default 时,被拒的请求保持被拒;自己列一份回退模型则会得到 400。

工具还没跑完时,规则再拆一刀。流式拒答若打在一个尚未关闭的工具调用块上(客户端工具、服务端工具或 MCP),这次拒答直接返回,不自动重试。若请求带了 fallback-credit-2026-07-01,信用令牌仍然在,但只能靠续写那份部分响应来兑现。非流式请求不受这一条影响,接口会清掉部分工作再重试,然后才响应。自己拼重试时,令牌过期、或只能靠续写才能兑现的情况,丢掉令牌再跑会把已经完成的服务端工具再计一次。先把这笔成本露给调用方,不要静默重试。

4. 断流丢掉的 usage 不是退款

OpenAI 的 Chat Completions 参考对 stream_options.include_usage 有一句容易被读反的注记:流被中断时,你可能收不到最后那块带整次请求 token 用量的 usage。其余块上的 usage 是空的。客户端把「没收到最终块」记成 0,会和用量页对不上。缺块是传输中断,不是官方写下的免费。对账以用量页这一段时间为准,不要以本地没加总成功为准。

同一份参考把 finish_reason 的 content_filter 定义成:内容因内容过滤器的标记而被省略。这是选项上的结束原因,请求可以已经完成选择,它不是 HTTP 错误码。拿 Claude 的 refusal 去套这个字段,或拿这个字段去套「出字前三类才计费」,都会并错账。训练任务的取消是另一篇:主动取消按已消耗 token 计训练费,其他中断不计训练费。聊天流断掉,不走那张训练表。

5. 续写是新请求,充值改不了类别

流式消息的错误恢复写的是一次新请求,不是把旧计量器拨回去。Claude 4.5 及更早,把已经收到的部分放进一条新的助手消息再续。Claude 4.6 及以后,改成一条用户消息,说明上次在哪里中断、从那里继续。文档写明,工具调用块和扩展思考块不能部分恢复,只能从最近的文本块接着流。续写因此会把已经付过的半截再送回去当输入。这和接上上一轮仍按整段输入计是同一类结构:新打的几个字不是账单的全部。

给控制台充值,发生在这些列扣款之前。链上把稳定币换成能充进账户的资产,不会把 bio 改成不计费的类别,不会让空的 content 变成「这次没占速率限制」,也不会把已经流出的输出抹掉。需要这一步时,可以用无需注册、非托管的 AllSwap 完成链上兑换。兑换成功只说明资产换了手。stop_details.category 和已经流出的 token,仍按这次请求各自的规则计。