2019年,Elizabeth Smart在健美比赛舞台上头发被树枝缠住,她扯掉假发继续微笑。后来她说:“我的身体承载了我,我不想再为它感到羞耻。”她把创伤称为“绕路”(detour)——一条你没计划过、也不想要的路,但你可以选择怎么走完它。

作为做Agent系统的开发者,我在这段故事里看到了我们一直在解决的核心问题:当Agent的执行路径被意外中断时,它应该怎么办?

多数Agent框架(比如AutoGPT、BabyAGI)在工具调用失败后只会重试几次,然后卡死或返回错误。但真正有用的Agent,应该像Elizabeth那样——把意外当成重新规划的信号,而不是任务终止。 下面是我在几个生产级Agent项目中验证过的做法。

失败不是错误,是信号

大多数Agent的实现里,规划器生成步骤序列,执行器按顺序调用工具。如果工具返回错误,常见处理是:重试N次、返回默认值、或直接崩溃。

问题在于:工具失败的原因可能是外部环境变化(比如API限流),也可能是原本的规划假设错了。 这时候重试没有意义,需要的是重新规划。

Agent failure recovery detour planning flowchart

我的做法是:在记忆模块里维护一个“失败日志”,每次工具调用失败后,记录三样东西:

  • 失败时的完整上下文(步骤号、输入参数、错误信息)
  • 当前执行到哪一步
  • 失败前成功完成的所有步骤

执行器不直接重试,而是把失败日志传给规划器,让规划器决定是否要“绕路”。

核心实现:DetourPlanner

下面伪代码展示核心逻辑(完整可运行版本在我的GitHub仓库中):

python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
class DetourPlanner:
    def __init__(self, llm, memory):
        self.llm = llm
        self.memory = memory  # 包含成功步骤和失败日志

    def plan_with_recovery(self, objective, failure_log):
        # 1. 将失败日志还原为结构化信息
        failed_step = failure_log["step"]
        error_info = failure_log["error"]
        completed_steps = self.memory.get_completed_steps()

        # 2. 构造新的规划提示:告诉LLM“之前的假设导致失败,需要替代路径”
        prompt = f"""Objective: {objective}
已完成步骤:{completed_steps}
第{failed_step}步因{error_info}失败。
请提供一个替代方案(detour)来继续完成任务,要求:
- 不能依赖失败的步骤假设
- 利用已完成的成果
- 如果失败步骤是工具调用,考虑使用不同工具或改变参数
"""
        new_plan = self.llm.generate(prompt)
        
        # 3. 将新计划插入到原计划中,覆盖失败点之后的部分
        self.memory.update_plan(new_plan, insert_after=failed_step-1)
        return new_plan

# 使用时
try:
    result = execute_step(current_step)
except ToolError as e:
    failure_log = {
        "step": current_step_index,
        "error": str(e),
        "context": get_step_context()
    }
    new_plan = planner.plan_with_recovery(objective, failure_log)
    # 继续执行新计划

关键点:规划器拿到失败日志后,不是简单重试,而是要求LLM生成一条新路径。 这个新路径可以跳过失败工具、换一个API、甚至改变任务拆解方式。

踩坑记录:别让Agent陷入循环

最早版本里,我们没有限制规划器绕路的次数。结果Agent在同一个失败点反复生成新路径,每次都失败,变成死循环。解决方案:在记忆里增加“失败历史计数器”,同一目标下如果超过3次不同的绕路仍然失败,则降级为“最大努力完成”——跳过该步骤,用默认值替代,并在最终汇报里标记异常。

另外,要区分暂时性失败和永久性失败。暂时性失败(如网络超时)应该重试2次后再绕路;永久性失败(如API密钥无效)应直接绕路。判断方式:看错误是否包含“retryable”特征。

更进一步:动态代价感知

现实场景中,绕路可能增加成本(时间/金钱)。我曾在电商Agent中引入代价函数:每次绕路前,估算新路径的执行成本,并与“放弃该子任务”的成本比较。如果绕路成本超过预期收益的30%,就放弃该子任务,重新调整整体目标优先级。

类似Elizabeth选择撕掉假发继续微笑——有时候放弃一部分,比强行修补更优雅。

你的Agent也该学会绕路

下周你写Agent时,可以试试这个改动:

  1. 把失败日志结构化存入记忆;
  2. 失败后让规划器生成替代路径,而不是重试;
  3. 设置绕路次数上限并监控代价。

这样你的Agent就不再是个脆皮对话机器人,而是能像人一样——被绊倒之后,拍拍土,换条路继续走。