子代理应该本地处理瞬时性错误。一个重试就恢复的超时不需要协调者介入。但本地恢复失败时,传播的错误必须包含结构化上下文——没有的话,协调者会默认用最简单(往往是错误的)恢复策略。
本地恢复模式
- 子代理遇到瞬时性错误(超时、限流)
- 本地短暂退避重试(数据源最近还在响应)
- 如果重试成功 → 正常返回结果。协调者不知道发生过什么。
- 如果重试失败 → 向协调者传播,带完整的结构化上下文
大多数瞬时性错误在几秒内恢复。一次本地重试处理了绝大多数超时,不需要协调者介入。
传播时要包含什么
错误做法:{"status": "failed", "message": "Could not complete search"}
协调者有三种策略:(1) 重试同一查询,(2) 重试修改后的查询,(3) 不带结果继续。拿到”could not complete search”后,它每次都默认策略 1——即使对永久性错误重试是徒劳的。30% 的失败是永久性的但协调者分辨不出来。
正确做法:带以下的结构化上下文:
- 失败类型:transient / permanent / validation
- isRetryable:布尔值
- 尝试的查询:试了什么
- 部分结果:失败前获得的任何东西
- 建议替代:其他数据源或方法
这让协调者能匹配失败类型到策略:permanent → 不带结果继续,transient → 重试同一个,validation → 重试修改后的。
静默吞没反模式
子代理捕获所有错误然后以成功状态返回空结果。协调者在假设任务正常完成的情况下做决策。缺失的数据被纳入最终输出,没有任何缺口标识。
这比传播错误更糟——至少错误传播给了协调者适应的机会。静默吞没完全隐藏了失败。
什么时候本地处理 vs 传播
| 错误类型 | 本地处理 | 传播 |
|---|---|---|
| 瞬时性(超时) | 退避重试(1-2 次) | 重试失败时 |
| 限流 | 等待后重试 | 延迟后仍受限时 |
| 校验性 | 如果能确定修复方法 | 不能确定时 |
| 权限性 | 本地无法修复 | 始终立即传播 |
| 业务规则 | 本地无法修复 | 始终立即传播 |
瞬时性错误:先本地试。永久性错误:立即传播(没有重试的意义)。
一句话总结: 瞬时性错误先本地重试再传播——但需要传播时,包含失败类型、可重试性、尝试的查询、部分结果和替代方案,让协调者能选对恢复策略,而不只是默认重试。