请求超时后,先记账再决定重试
一次模型请求超时,客户端没有收到答案,却不能据此认定服务端没有完成生成。直接重发可能恢复页面,也可能触发第二次执行。要让带退避的重试不重复收费,关键不只是等待多久,而是能否识别同一项任务、确认上次结果,并阻止重复执行。
下面把两种实现并排记下:一种把异常当作重发信号;另一种把异常当作需要分类的状态。后者更谨慎,但“绝不重复计费”仍需要服务端提供明确保证,不能由客户端单方面承诺。
§ 第一处:先判断失败,还是结果未知
- 直接重发:捕获超时、连接中断或服务端错误,统一等待一秒再试。代码简单,却把“没有收到响应”误当成“没有发生执行”。
- 可核对的重试:区分本地校验失败、明确拒绝和结果未知。请求发出前的参数错误应先修正;收到限流响应时按接口约定处理;请求发送后的读取超时则标记为未知,不立即创建新执行。
服务器错误也不自动等于未计费。错误可能发生在生成之后,甚至发生在返回结果的途中。只有接口文档明确说明的语义,才能作为重试依据。
§ 第二处:退避负责降压,不负责去重
- 直接重发:所有客户端固定间隔重试。短暂拥堵后,请求又同时回来,形成下一轮峰值。
- 可核对的重试:对允许重试的失败使用指数退避和随机抖动。例如第几次重试,就在零到该次等待上限之间随机取值;上限逐次翻倍,最高不超过十六秒。若接口返回有效的 Retry-After,则按其语义安排下一次尝试。
同时限制总尝试次数和整个任务的截止时间。退避能减少拥堵,却不能撤销已经完成的生成;等待十秒后重新执行,仍然可能产生第二笔费用。
§ 第三处:任务标识不等于幂等保证
- 直接重发:每次生成新的请求标识,或者随手加上一个幂等请求头,便认为重复收费已经解决。
- 可核对的重试:为业务任务保存稳定标识,并确认服务端是否支持幂等键、适用哪些接口、保留多久、并发重复请求如何处理,以及重放结果是否再次计费。只有明确支持的字段才具有约定中的作用。
Vana API 在 https://api.vanaapi.com 提供 OpenAI 兼容入口,也支持 Anthropic Messages 和 Gemini 原生协议。但协议兼容本身不能证明存在跨协议的幂等或计费去重保证。上线前应核对当前文档或向支持确认,不要把某个 SDK 的行为当作服务端承诺。
§ 第四处:有记录,才有恢复依据
- 直接重发:只保存最终文本。进程重启后不知道上次请求是否已经发送,于是再次提交。
- 可核对的重试:发送前持久化任务标识、请求摘要、模型与参数,以及待发送、执行中、成功、失败或未知状态。收到响应时,记录可用的请求标识、用量和结果;恢复任务时先检查已有记录。
如果服务提供结果查询或账单核对能力,未知状态应优先走核对流程。若没有幂等或查询能力,安全选择是暂停自动重发并提示结果待确认。这会牺牲即时恢复,但比暗中启动另一笔可能收费的执行更诚实。
§ 第五处:流式中断不能直接续接
- 直接重发:收到半段答案后断线,重新请求,再把两段文本拼在一起。用户看到的重复可能被隐藏,第二次生成的成本却仍然存在。
- 可核对的重试:保存已收到的片段,把任务标记为流式中断。除非接口明确支持恢复,否则新请求就是新的执行,不应被描述为免费续传。
还要检查 SDK、应用和后台队列是否各自开启重试。三层重试叠加,会让一次操作变成多次调用。最好由一层统一管理次数、退避和截止时间,其他层采用与之协调的配置。
§ 上线前的底线
Vana API 按实际 token 计费、没有月费,因此记录每次实际执行比只统计用户点击更有意义。可以创建一个 Vana API 账户,用小额度测试发送前断线、发送后超时和流式中断,逐一核对请求记录与用量。
最终应写进运行手册的是:明确未执行的请求可以按约定重试;结果未知的请求先核对;需要“绝不重复计费”的业务,必须取得覆盖执行与计费的服务端幂等保证。指数退避只是这套流程中的等待机制,不是收费保险。