上周调试一个多步骤数据解析Agent,半夜被报警叫醒——日志里堆满了“Action failed: context exceeded”。Agent试图一次性把整个JSON解析、校验、转换流程塞进单个Prompt,结果token爆炸。这种“一杆子捅到底”的写法,在简单任务里还能蒙混过关,一旦流程复杂,立刻现原形。

痛定思痛,我把架构推倒重来,引入了Plan-and-Execute模式。今天我们就聊聊这个让Agent“先想清楚再动手”的实战套路。

一、什么是Plan-and-Execute?

简单说,就是把“规划”和“执行”拆成两个阶段。规划器(Planner)先把任务分解成步骤清单,执行器(Executor)再按清单一步步跑。这听起来像废话——人类不都这么干活吗?但很多初学者写Agent时,偏偏喜欢把所有步骤揉进一个Prompt里。

老项目里常见的反模式是这样的:

# 别这样写!这是典型的“一锅炖”Agent
prompt = """
请执行以下操作:
1. 解析用户上传的JSON数据
2. 验证每个字段的类型
3. 转换日期格式为ISO标准
4. 计算数值字段的平均值
5. 生成摘要报告

JSON数据:{json_data}
"""

当json_data很大时,这个Prompt很容易超长。更糟的是,一旦某步出错,整个流程全废。

二、拆开规划与执行

我们重构后的架构长这样:

class Planner:
    def generate_plan(self, task_description):
        # 这里用LLM生成步骤列表,实际项目可以加约束条件
        plan_prompt = f"""
        请将以下任务分解为可执行的步骤:
        任务:{task_description}
        
        要求:
        - 每个步骤应该是原子操作
        - 标注步骤间的依赖关系
        - 输出格式:1. [步骤描述] (依赖:[前置步骤编号])
        """
        # 调用LLM生成规划
        return self.llm(plan_prompt)

class Executor:
    def execute_step(self, step, context):
        # 根据步骤描述选择工具执行
        tool = self.select_tool(step)
        result = tool.run(context)
        # 这里可以加重试、回退逻辑
        return result

规划器输出的可能长这样:

1. 解析JSON结构,提取字段列表 (依赖:无)
2. 验证必填字段是否存在 (依赖:1)
3. 转换所有日期字段为ISO格式 (依赖:1)
4. 计算数值字段统计量 (依赖:1)
5. 生成验证报告 (依赖:2,3,4)

三、实战踩坑记录

坑1:规划太抽象
初期规划器常输出“处理数据”“分析内容”这种模糊步骤。后来我们在Prompt里加了约束:“每个步骤必须对应一个可调用的工具名”。现在步骤描述里必须包含tool:xxx字样。

坑2:循环依赖
某次规划器输出“步骤A依赖步骤B,步骤B依赖步骤A”——死循环了。我们在解析依赖关系时加了环路检测,现在遇到这种情况会让规划器重新生成。

坑三:上下文丢失
执行器每步只看到当前输入,可能忘记之前步骤的结果。我们维护了一个共享的context_dict,每步执行后把结果用step_id作为key存进去,后续步骤按需读取。

# 这是简化后的执行循环
context = {}
for step in plan:
    # 把依赖步骤的结果注入当前上下文
    step_input = self._prepare_input(step, context)
    result = self.execute_step(step, step_input)
    context[step.id] = result  # 存下来给后续步骤用

四、进阶技巧:动态重规划

复杂任务中,执行时可能发现规划不合理。我们在每个步骤执行后加了个评估器:

def should_replan(current_step, result, context):
    # 如果结果包含“无法执行”“缺少信息”等关键词
    # 或者执行时间远超预期
    # 就触发重规划
    if "error" in result.lower():
        return True
    return False

触发重规划时,我们把当前上下文和失败信息喂给规划器,让它生成调整后的计划。这个机制救过我们好几次——特别是处理用户临时变更需求时。

五、性能权衡

Plan-and-Execute模式当然有代价。多轮LLM调用意味着更高的延迟和成本。我们的经验是:

  • 任务步骤超过3步,或者涉及条件分支,用这个模式划算
  • 简单查询、单步操作,直接执行就行
  • 可以在规划时让LLM输出“置信度”,低置信度的规划可以回退到单步模式

另一个优化点:缓存常见任务的规划结果。用户经常提交相似任务时,没必要每次都重新规划。

六、给工程师的几点建议

  1. 从简单规划器开始:先实现固定模板规划(比如“解析-验证-转换”三部曲),再升级到LLM动态规划。别一上来就搞全自动。

  2. 给执行器加超时和回退:某个步骤卡住时,整个Agent不能死等。我们设了默认30秒超时,超后尝试替代方案或触发重规划。

  3. 规划结果要可序列化:把生成的计划存到数据库或日志里。出问题时能回溯分析,也能用来训练规划评估模型。

  4. 人工审核环节:对于关键业务,可以在规划后、执行前插入人工审核。我们系统里高风险操作(比如删除数据、调用付费API)的规划会挂起等待确认。

  5. 监控规划质量:我们统计了“规划步骤数/实际执行步骤数”的比例,发现理想值在0.8-1.2之间。比例过高说明规划太碎,过低说明规划漏步骤。

这个模式最让我欣赏的一点是:它把Agent的“思考过程”外化成了可检查、可调试的规划文本。现在看日志不再是黑盒——我能清楚看到“Agent当时打算怎么做,实际执行时在哪步出了问题”。这种可观测性,在凌晨三点调试时尤其珍贵。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐