Plan-and-Execute模式:让Agent先规划,再执行
上周调试一个多步骤数据解析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输出“置信度”,低置信度的规划可以回退到单步模式
另一个优化点:缓存常见任务的规划结果。用户经常提交相似任务时,没必要每次都重新规划。
六、给工程师的几点建议
-
从简单规划器开始:先实现固定模板规划(比如“解析-验证-转换”三部曲),再升级到LLM动态规划。别一上来就搞全自动。
-
给执行器加超时和回退:某个步骤卡住时,整个Agent不能死等。我们设了默认30秒超时,超后尝试替代方案或触发重规划。
-
规划结果要可序列化:把生成的计划存到数据库或日志里。出问题时能回溯分析,也能用来训练规划评估模型。
-
人工审核环节:对于关键业务,可以在规划后、执行前插入人工审核。我们系统里高风险操作(比如删除数据、调用付费API)的规划会挂起等待确认。
-
监控规划质量:我们统计了“规划步骤数/实际执行步骤数”的比例,发现理想值在0.8-1.2之间。比例过高说明规划太碎,过低说明规划漏步骤。
这个模式最让我欣赏的一点是:它把Agent的“思考过程”外化成了可检查、可调试的规划文本。现在看日志不再是黑盒——我能清楚看到“Agent当时打算怎么做,实际执行时在哪步出了问题”。这种可观测性,在凌晨三点调试时尤其珍贵。
更多推荐



所有评论(0)