自动化交付

接口报错就重试?文档任务应先分清哪些失败可恢复

报错后再试一次很常见,报错后无限再试就会把小故障放大。权限错误、账户欠费、损坏文件和服务临时繁忙不应该走同一条重试路径。分类之后,再决定等待多久、最多试几次,以及是否需要用户介入。

阅读文本 / Reading text
本文使用合成样本演示处理方法。代码已通过隔离环境中的示例检查;这不代表对所有真实文件或产品效果的保证。处理重要文件前,请保留原件,并按文中的边界与验收项复核。 报错后再试一次很常见,报错后无限再试就会把小故障放大。权限错误、账户欠费、损坏文件和服务临时繁忙不应该走同一条重试路径。分类之后,再决定等待多久、最多试几次,以及是否需要用户介入。 先判断重试会不会产生重复任务 POST 超时最棘手:客户端没收到响应,不代表服务端没有创建任务。没有幂等键或任务查询机制时,盲目重发可能创建多个翻译任务。重试策略应优先服从接口契约,而不是只看 HTTP 状态码。 把次数和等待预算写成纯函数 from enum import Enum class Failure(Enum): TRANSIENT = "transient" AUTH = "auth" BILLING = "billing" INVALID_INPUT = "invalid_input" def delay_for(kind, attempt, replay_safe): if type(attempt) is not int or attempt < 0: raise ValueError("invalid attempt") if not replay_safe or kind is not Failure.TRANSIENT: return None schedule = (1, 2, 4) return schedule[attempt] if attempt < len(schedule) else None assert delay_for(Failure.TRANSIENT, 0, True) == 1 assert delay_for(Failure.TRANSIENT, 2, True) == 4 assert delay_for(Failure.TRANSIENT, 3, True) is None assert delay_for(Failure.TRANSIENT, 0, False) is None assert delay_for(Failure.BILLING, 0, True) is None assert delay_for(Failure.INVALID_INPUT, 0, True) is None 代码只计算计划,不实际等待或发请求。固定的一、二、四秒是合成演示,不是通用服务建议。生产还要考虑服务端 Retry-After 、随机抖动、整体截止时间和并发限制;若服务端要求等待超过本地预算,应停止而不是提前重试。 错误类型对应不同动作 失败 可选动作 不应默认做 临时拥塞 有界退避 所有任务同时密集重发 登录或权限失效 明确提示重新授权 不断重复旧凭据 账户或账单异常 停止并报告 自动充值或换账户 输入结构错误 让用户修复输入 重发完全相同文件 保存逻辑任务与尝试记录 一次用户操作对应一个逻辑任务,下面可以有多个尝试。每次尝试记录开始时间、失败类别和远端任务 ID,最终只选择一份经过验收的结果。这样既能排查重试,也不会把尝试数误算成用户请求数。 翻译体验的稳定来自解释清楚 对接 OmniDoc(产品入口) 时,前端应该明确显示等待、需要重新授权或需要检查账户,而不是把所有情况变成“生成中”。用户愿意等待一个有状态、有出口的任务,不愿意反复点击一个没有反馈的按钮。 参考: Python enum 。 - 1 -
更新于 2026-10-08