可靠性模式
厂商会限流、会超时、会返回畸形的输出。当一个依赖不可靠时,让系统撑住的那些模式。
模型 API 是一种网络依赖:延迟会波动、有限流、偶尔中断,输出还不确定。这里每一个模式都是分布式系统的标准做法,只是用在了一个还会返回不可预测内容的依赖上。
正确地重试
要重试: 429 速率限制、5xx 服务端错误、超时、连接失败。
不要重试: 400 请求畸形、401 认证失败、403 权限错误、上下文长度错误。问题出在请求本身,重复发只是浪费时间和配额。
用带抖动的指数退避。每次尝试把等待时间翻倍,并加入随机量,免得并发的客户端整齐划一地重试,把当初触发限流的那波尖峰再制造一遍。有 retry-after 头就照它来。
给尝试次数设上限——三次或四次。再往上,你只是在给一个已经失败的请求继续加延迟。
大多数 SDK 都实现了这套。要去确认,不要想当然,并且看清楚它们把哪些算作可重试。
熔断
对着一次持续的中断反复重试只会更糟:每个请求都要烧完整个重试预算才失败,于是延迟攀升,线程堆积。
熔断器跟踪失败率,超过阈值就直接失败,不再去调厂商。冷却一段时间后,它放一个试探请求过去,成功就恢复通路。
这把一次缓慢的级联失败变成一次快速干净的失败,而这几乎总是更好的结果。
提前定好降级行为
模型不可用时,总得发生点什么。要主动选定,而不是等出事时才现发现。
切到另一家厂商。 最稳的选项,代价是 prompt 得在两家上都管用——值得定期演练,因为没人跑过的备用方案等于不存在。
降级到更小的模型。 同一家厂商,换一档。质量打折也好过没有响应。
对重复的问题返回缓存结果。
退回非 AI 的行为。 用关键词检索代替语义检索,用模板代替生成文本。这常常是最结实的选项。
老实失败。 有时候这才是对的。直接说这个功能暂不可用,而不是返回一个错的东西。
错误答案则是:悄悄返回降级的输出,还假装一切正常。
把畸形输出当常态处理
不合规的输出是常态,不是例外。参见从 LLM 里取出 JSON。
分层处理:厂商提供 schema 约束生成就用上,但不管怎样,解析之后都要校验;解析前先把代码围栏剥掉;然后把校验错误写进 prompt 重试一次。大多数失败在重试时就好了。
要特别留心截断。一个对象在生成中途被切断,说明你的输出上限设得太低,重试修不好——那是个披着解析错误外衣的配置 bug。
超时与幂等
显式设置超时。 生成要花实打实的时间,偶尔还会挂住。没有超时,一个挂住的请求会无限期占着资源。
让重试幂等。 如果一次调用会触发副作用——发消息、扣费、写数据——超时之后你无法确定它到底完成没有。用幂等键,或者让写副作用的那条路径能容忍重复。
在 agent 场景里这一点最要紧,因为重试的工具调用会把真实动作重复执行。
隔离故障
舱壁隔离。 按功能划分连接池和并发上限,这样一个变慢的摘要接口就耗不光关键路径需要的容量。
非交互的工作走队列。 没人守着等结果的事情,都该进一个带独立重试语义的队列——而这正是批处理接口更便宜的那类流量。
给 agent 设预算上限。 迭代次数、token、墙钟时间。不设上限的循环就是一场没有边界的失败。
该监控什么
按类型分的错误率、重试率、熔断器状态、延迟分位数,以及校验失败率。重试率上升是出问题的先行指标——参见LLM 系统的可观测性。
要点回顾
- 429、5xx 和超时要重试,用带抖动的指数退避;400 和 401 永不重试。
- 熔断把缓慢的级联失败变成快速干净的失败。
- 出事之前就选定降级行为:换厂商、换小模型、用缓存、退回非 AI 方案,或者老实失败。
- 把畸形输出当常态:加约束、做校验、剥围栏、带着错误信息重试一次。
- 设置超时,让重试幂等——工具调用带副作用时这点至关重要。
- 用舱壁隔离,非交互工作走队列,给 agent 设预算上限。
下一篇: Prompt 版本管理