从 ReAct 到 Plan-and-Execute:让 Agent 先规划,再执行
在基础的 ReAct Agent 中,大模型通常采用“走一步、看一步”的方式工作:
思考下一步 → 调用工具 → 查看结果 → 再思考
这种方式很灵活,适合“查看目录里有哪些文件”之类的简单任务。但任务一复杂,例如“创建项目、编写代码、编译、运行并验证”,每一步都重新调用大模型,会增加等待时间和 Token 消耗。
Plan-and-Execute 提供了另一种思路:先生成完整计划,再按计划执行。
一、基本流程
假设用户要求:
创建一个 Java 项目,编写 Hello 类,然后编译运行。
Planner 可以先生成这样的计划:
task_1:创建 Java 项目
task_2:编写 Hello.java,依赖 task_1
task_3:编译项目,依赖 task_2
task_4:运行程序,依赖 task_3
task_5:验证输出,依赖 task_4
随后,Executor 根据依赖关系依次执行任务。
整个流程是:
用户目标
↓
LLM 生成计划
↓
程序检查计划
↓
按依赖调用工具
↓
失败时重试或重新规划
↓
汇总结果
二、为什么要把步骤建模成 Task
如果任务只存在于聊天记录中,系统很难清楚地知道执行到了哪里。因此,可以为每一步建立一个 Task,并记录:
- 任务 ID 和描述;
- 任务类型;
- 前置依赖;
- 当前状态;
- 执行结果和错误;
- 开始及结束时间。
任务状态一般按照下面的方向变化:
PENDING → RUNNING → COMPLETED
→ FAILED
→ SKIPPED
这样,系统就能查询执行进度,也能在某一步失败后单独重试,不必把已经成功的步骤再执行一遍。
三、用 DAG 管理任务依赖
DAG 是“有向无环图”,可以理解为一张没有死循环的任务依赖图。
例如:
创建项目 → 写代码 → 编译 → 运行
“编译”必须等“写代码”完成,“运行”必须等“编译”完成。
DAG 还有一个重要作用:找出可以并行执行的任务。例如,下面四项互不依赖:
- 读取
pom.xml; - 查看源码目录;
- 查看测试目录;
- 读取
README.md。
它们可以同时执行。最后的“汇总项目情况”任务依赖前四项,因此需要等它们全部完成。
四、Plan-and-Execute 真的只调用一次 LLM 吗
不一定。
只有计划中包含完整的工具名称和参数,执行器才能直接工作。例如:
{
"tool": "write_file",
"arguments": {
"path": "src/main/java/Hello.java",
"content": "public class Hello { ... }"
}
}
如果计划只有一句“编写 Hello 类”,执行器仍不知道文件路径和具体内容,可能还要调用 LLM 来补全。
因此,更准确的说法是:
Plan-and-Execute 可以减少重复的 LLM 决策,但不保证整个过程只调用一次 LLM。
确定性的读文件、写文件和运行命令,可以直接交给工具;需要分析、探索或处理意外的任务,仍可以使用 LLM。
五、ReAct 和 Plan-and-Execute 怎么选
| 模式 | 特点 | 适用场景 |
|---|---|---|
| ReAct | 边执行边判断,灵活性高 | 简单任务、探索性任务、结果不确定 |
| Plan-and-Execute | 先规划后执行,过程清晰 | 多步骤任务、依赖明确、需要并行 |
| 混合模式 | 整体先规划,困难步骤内部使用 ReAct | 真实的复杂 Agent 项目 |
实际项目中,混合模式通常更实用:
- 用 Planner 生成整体任务 DAG;
- 确定性任务直接调用工具;
- 探索性任务在内部使用 ReAct;
- 某一步失败时,选择重试、局部修复或重新规划。
六、实现时还要注意什么
除了生成计划,还要处理以下问题:
- 检查任务 ID 是否重复;
- 检查依赖的任务是否存在;
- 检测循环依赖;
- 防止多个任务同时修改同一个文件;
- 给并行日志加上任务编号;
- 某个任务失败后,跳过依赖它的后续任务;
- 对删除、覆盖和部署等操作增加人工确认;
- 限制命令执行时间和任务重试次数。
这些能力决定了系统是一个简单演示,还是一个真正可靠的 Agent 执行器。
总结
Plan-and-Execute 的重点不是“提前列一张待办清单”,而是把自然语言目标转成一组可检查、可调度、可重试的结构化任务。
可以把它理解为:
LLM 负责规划和处理不确定性
程序负责调度和执行确定性操作
ReAct 像边走边问路,Plan-and-Execute 像先规划路线再出发。复杂 Agent 最合适的做法,通常不是二选一,而是让它们各自处理最擅长的部分。