错误恢复
错误恢复层先识别模型调用为什么失败,再选择等待、压缩输入、调整输出预算或使用兼容备用模型。原因没有变化就重试,常会得到同样结果。
搬沙发过门时,门暂时卡住与沙发本身太宽需要不同处理;地址错误则要先改目标。重试策略也必须区分暂态问题、请求问题和需要停止修正的问题。
错误恢复如何工作
本课把恢复分为改变时间、改变请求、停止自动尝试三类。
等待能处理的错误
示例的 529 overloaded 代表暂时的服务压力,真实的 429 rate_limit 也常如此。等待后重发同一请求,环境可能已经恢复。真实 429 也可能是额度或其他限制,不能只看状态码就断言稍等即可;要结合错误详情与服务端建议。
必须重建请求的错误
400 context_length_exceeded 表示本例的输入加预留输出超过窗口,等待不会缩短输入。应压缩旧消息、保留所需近况,再检查新请求大小。
成功响应中的 stop_reason: max_tokens 表示生成触及输出预算,本例把它视为未完成并增加 max_tokens。真实处理还应检查任务是否完整,增加预算也不能超过模型输出上限或输入加输出的总限制。
反复 529 可以触发切换到获准、兼容的备用模型。这是恢复策略,不证明原模型选错或永远不会恢复。备用模型的工具与请求格式也必须兼容。
停止后再修正的错误
本例 400 invalid_request_error 是无效工具 JSON Schema。等待、压缩、盲目切换模型都不修复 schema,因此第一次就停止并报告。修正请求后可以再试;“不自动重试”不等于错误永远无法修。
从哪里识别错误
classify(response) 要读取不同层:HTTP 状态如 429、529、400;错误体判别字段,如本课 Anthropic 风格的 error.type,含 rate_limit_error、overloaded_error、invalid_request_error;成功响应中的停止原因,如 stop_reason: "max_tokens"。不同 API 的字段并不完全相同。
业务成功由 harness 判断,不能只看 HTTP。 截断可以伴随 HTTP 200。只捕获异常会漏掉这种未完成结果。
错误恢复逐步演示
默认请求过长,策略先分类再重试,共八步。
- 组装请求。 模型
primary,输入 203,800 token,max_tokens = 4,096,34 条消息,窗口 200,000。 - 第一次尝试。 提交 203,800-token 输入。
- 拒绝。
400 context_length_exceeded。本例检查203,800 + 4,096 = 207,896 > 200,000,输入本身已经超限。没有生成、超时或过载;原样发送仍不满足限制。 - 分类。 得到
context_too_long,需要改变请求,不能靠等待。 - 压缩。 最旧 28 条消息共 172,300 token,换成 1,450-token 摘要;最近 6 条共 18,900 原样保留;system 与工具定义 12,600 在本策略中不改。新输入 32,950 token、7 条消息,约减少 84%。任务和近况保留,旧消息摘要仍有损。
- 第二次尝试。 模型与
max_tokens不变,提示内容及消息数改变。 - 成功。
32,950 + 4,096 = 37,046,处于窗口内。 - 账目。 2 次尝试,等待 0.0 秒,提交输入合计 236,750 token。这是提交量,不是实际计费;确定性超限先修请求,无需先退避。
切换 blind,每次都发送 203,800,失败后等待 1、2、4、8、16 秒。共 5 次尝试、31 秒退避、1,019,000 token 提交量,没有答案。最后失败后还等 16 秒才退出,是这段朴素循环故意保留的问题。
盲目重试的成本差异
暂态过载中,两策略都在等 1 秒后第二次成功,共提交 49,200 token,分类没有改变这个结果。差异来自不能靠时间改变的失败。
上下文超限时,blind 提交量约为 classify 的 4.3 倍,额外等 31 秒仍失败。无效请求则由 classify 用 1 次、0 秒结束,blind 用 5 次、31 秒结束;前者尝试数与提交量为五分之一,但不能据此称真实速度或价格恰好改善五倍。
压缩本身也有成本。读取 172,300-token 旧历史生成摘要,是额外模型工作,本账本未计。把这个输入加回,分类路径约为 409,000 输入 token 暴露量,和 blind 的提交量比约 2.5 倍,不再是 4.3 倍。这仍不是账单:被拒请求是否计费、缓存、摘要输出与不同模型费率都会改变价格。修复调用也应纳入实际预算。
暂时与持续的 529 初次响应相同。示例先等 1 秒、2 秒,第三次仍失败后切备用模型,第四次成功。重试预算定义“何时换路”,不证明主模型无法恢复。这里不统计真实错误频率,也不能把示例类别当成所有失败的比例。
边界情况
- 压缩还不够。 保留尾部已超窗时,一次摘要无法解决。应缩小保留范围、分块或明确失败,不能无限重复同一修复。
- 备用也不可用。 切换不保证成功,仍需总预算与结束条件。
- 重复副作用。 若先前操作已扣款或发消息,重试可能重复执行。接收方支持并落实幂等键时可复用稳定键,否则需要去重或对账恢复。仅附一个被忽略的键没有保证。
- 输出再次截断。 加倍预算受输出上限和总窗口约束;到上限应缩小任务或分段完成。
- 没有 jitter。 所有客户端按 1、2、4、8、16 同时醒来,可能再次形成请求峰值。真实退避通常加入随机性并遵守服务端建议。
常见错误
- 把失败当一个布尔值。 一个 except 加 sleep 会对无需等待的错误也延迟。读取具体错误类型与响应状态。
- 修好确定性问题后还先等。 压缩后无故等待四秒不再修复任何东西。若服务端另有流量限制,则需按该独立限制处理。
- 最后失败后仍 sleep。 blind 的最后 16 秒不通向新尝试,只增加等待。
- 只捕获异常。 HTTP 200 也可能未完成,应检查停止原因与任务所需完整性。
- 把所有 400 当成同类。 无效 schema 应先修 schema,本课的上下文超限也用 400,却可以压缩后再试。不能只按状态码决定。
- 备用掩盖故障。 每次都悄悄切备用会遮住主模型长期异常,应在日志里记录切换原因、次数和结果。
模型的简化
请求大小、压缩比例、故障序列都是预设,由确定性 harness 计算,没有调用模型,也不是模型录制。等待秒数是示例策略,不是生产默认值。
账本把修复成本计为零:没有摘要模型的 token、输出与延迟,也没计备用路径的冷启动。引用比值时必须保留这些范围,且提交 token 不等于收费 token。
真实恢复还包括 jitter、retry-after、中断流恢复、受支持的幂等机制、熔断、分错误类型的预算,以及客户端令牌桶限流。分类与修复思路可复用,数值只用于核算本课示例。