跳到主要内容
Cowers://
全部文章
推理范式

Plan-and-Execute 先规划再执行

Plan-and-Execute 把 Agent 拆成两个角色:Planner 先想清楚要做哪几步,Executor 再按步骤逐条执行。

Plan-and-Execute:先规划再执行

阅读提示 面向已经用过 LLM Agent(会写 prompt、调过 tool call)的学习者。 前置概念:ReAct(边想边做的单步循环)。本篇解释它和 Plan-and-Execute 的分工差异。 ⚠️ = 常见陷阱 🆚 = 对比说明 💡 = 选择建议

目录


核心概念 Plan-and-Execute 把 Agent 拆成两个角色:Planner 先想清楚要做哪几步,Executor 再按步骤逐条执行。它管的是「任务序列」这一层控制流,不管单步内部怎么推理。

⚠️ 先立一条边界:Planner / Executor / Replanner 是逻辑角色,不是规定的实现。用几个模型、什么时候重规划、计划长什么样,全部是实现选择。下面凡是给出具体数字或流程的地方,都是常见做法而非定义。

一、一句话定义

先想清楚要做哪几步,再一条条执行。

规划和执行是两个分离的职责,中间隔着一份显式的、可读可改的计划文本。

⚠️ 把「两次模型调用」当成定义 常见的错误概括是:「Plan-and-Execute = Planner 一次调用 + Executor 一次调用,总共两次」。这三个数字都不对:

  • Planner 不一定只调一次:一旦引入重规划,Planner 会被反复调用。
  • Executor 不是一次调用:它逐条执行 N 个步骤,每步至少一次模型调用、常常还有多次工具调用。所以总调用数至少是 1 + N,通常远不止。
  • 不一定是两个不同的模型:分层用不同价位的模型是一种优化手段(也确实常见),但用同一个模型跑两个角色,同样是 Plan-and-Execute。

真正定义它的只有一件事:存在一份先于执行产生、且独立于执行过程的显式计划。

二、最小示例:一次完整 trace

任务:

帮我分析一个 GitHub 项目为什么启动失败。

第一步,Planner 输出计划(此时不碰任何工具):

1. 阅读 README
2. 检查依赖
3. 查看配置文件
4. 运行程序
5. 分析错误日志
6. 修改代码
7. 重新测试

第二步,Executor 逐条执行,每条对应一次或多次工具调用:

Step 1 → read_file("README.md")        → 得到启动命令
Step 2 → read_file("requirements.txt") → 得到依赖列表
Step 3 → read_file("config.yaml")      → 发现 DB_HOST 为空
...

整体数据流(这是最简形态,没有重规划):

User

Planner   ← 产出计划

Plan(显式文本)

Executor → Tool     ← 对每个步骤重复

Result

关键点:计划是一份可以被打印、被人类审阅、被程序修改的中间产物,而不是藏在模型隐式推理里的东西。

三、机制:Planner 和 Executor 为什么要拆开

拆开带来三个实际收益,都是工程上的,不是理论上的:

收益 原因 前提
上下文可能更省 Executor 每步只需看「当前这一步 + 相关结果」,不必带着全部历史 只有真的做了裁剪才成立,见下方陷阱
可审阅 计划是纯文本,执行前可以让人类改一改再放行 计划结构化
可换模型 Planner 用强模型(贵、少调用),Executor 用便宜模型(多调用) 你确实做了分层

⚠️ 以为「每步上下文恒定、一定省 token」 错误操作: 把「省上下文」当成 Plan-and-Execute 的自带属性,不做任何裁剪就认为成本会下降。

实际结果: 大多数朴素实现里,Executor 的 prompt 是「任务 + 完整计划 + 已完成步骤及其结果」,这个东西是随步数增长的,和 ReAct 一样线性膨胀。再加上多出来的 Planner 调用(引入重规划后是多次),总 token 往往比直接 ReAct 更高

原因: 省上下文来自「只喂当前步骤需要的东西」这个设计决定,而不是来自「分成了两个角色」这个结构。结构只是让裁剪变得可能——因为有了显式的步骤边界,你才知道该裁什么。

正确做法: 想拿到这份收益就得显式做:每步只注入「当前步骤描述 + 它依赖的前序结果(按依赖关系挑,不是全塞)+ 必要的全局上下文」,并把长结果摘要化。做完这件事再谈省 token。

计划的表示形式是实现选择 常见做法是让 Planner 输出结构化步骤(编号列表或 JSON 数组),因为 Executor 需要能程序化地「取出第 N 步」「判断还剩几步」。这是很实用的约定,但不是硬性定义——也有实现把计划表示成 DAG(表达步骤间依赖,可并行)、伪代码,甚至可执行代码。

底线是:计划要能被程序拆成可寻址的单元。如果计划是一段散文,就退化成了普通的 CoT 提示,拆分带来的收益全部消失。

四、🆚 和 ReAct 的区别

两者都能完成多步任务,但控制流的粒度完全不同。

维度 ReAct Plan-and-Execute
决策时机 每一步现想下一步 开头一次性想完所有步
全局视野 弱,容易走偏或原地打转 强,一开始就有终点
上下文占用 随步数线性增长 做了裁剪才恒定,否则同样线性增长
应对意外 天然灵活 需要额外的 replan 机制
适合任务 步数少、路径不确定 步数多、流程相对可预期

一句话:ReAct 是走一步看一步,Plan-and-Execute 是先画地图再走。

五、⚠️ 核心陷阱:初始计划一定会错

⚠️ 把计划当成不可变的 错误操作: Planner 生成 7 步计划后,Executor 严格从第 1 步执行到第 7 步,中途不允许修改计划。

实际结果: 第 3 步发现「配置文件根本不存在,项目用的是环境变量」,但后面第 4~7 步仍然基于「有配置文件」这个错误前提继续跑,最后产出一个自信但完全错误的结论。

原因: Planner 是在信息最少的时刻(还没读任何文件)做的规划。它规划时对项目结构的了解为零,计划本质上是一份猜测。执行过程中获得的新信息,恰恰是最能推翻原计划的信息。

正确做法: 加一个 Replan 环节——把「原计划 + 已完成步骤 + 新观察」交回 Planner,让它输出修订后的剩余计划:

Plan → Execute 一步 → Observe → (要不要 Replan?)→ Execute 下一步 → ...

但「每一步都必须 Replan」是过度要求。 每步都重规划意味着每步多一次昂贵的模型调用,而绝大多数步骤执行完并不会带来任何推翻计划的信息(读了个 README,计划照旧)。常见的触发策略:

  • 失败触发:某步执行失败或工具报错时才重规划(成本最低,最常用);
  • 意外触发:观察结果与计划的前提冲突时触发(需要在步骤里写明前提,才判断得了);
  • 定期触发:每 K 步或每个阶段结束时重规划一次;
  • 每步触发:路径高度不可预测时才值得,此时也该考虑是不是直接用 ReAct。

LangChain / LangGraph 的 plan-and-execute 示例里确实画了 Replanner 节点,但那是参考实现,不是协议规定——一个没有 Replanner 的 Plan-and-Execute 仍然是 Plan-and-Execute,只是脆弱。

💡 关于计划步数 经验上把步数控制在 3~8 步比较好用:太多说明粒度过细,Planner 在猜它还不知道的细节;太少说明这个任务未必需要 Plan-and-Execute,直接 ReAct 更省。

但这是经验区间,不是规则——数据迁移、大规模重构这类任务的计划天然就有二三十步,把它硬压到 8 步只会让每一步变成一个含糊的大箱子。真正该看的是每一步是否可独立执行、可判定完成,而不是总数落在哪个区间。

六、什么时候用

用:

  • 步骤多且彼此有依赖的工程任务(代码迁移、数据管道排查、多文件重构)
  • 需要人类在执行前审一眼计划的场景(有副作用、有成本的操作)
  • 想用便宜模型跑大量执行步、省成本的场景

不用:

  • 单跳问答、单次工具调用 → 直接调
  • 路径高度不可预测的探索任务 → Self-Ask 或 ReAct 更合适

七、复习重点

复习重点

  1. 定义:存在一份先于执行产生、独立于执行过程的显式计划。这是唯一的定义性特征——不是「两次模型调用」,也不是「Planner 只调一次」。
  2. 三个角色是逻辑角色:Planner / Executor / Replanner 说的是职责划分,用几个模型、何时重规划、计划长什么样,都是实现选择。
  3. 拆分的真实收益:可人工审阅、可分层用不同价位的模型;省上下文要自己动手裁剪才有——不裁剪的话每步 prompt 照样线性增长,加上 Planner 调用总成本可能更高。
  4. 最容易出错的地方:初始计划是在信息最少时做的猜测,需要 Replan 回路,否则新观察无法推翻旧前提。但触发时机应按需(失败时、前提冲突时、每 K 步),每步都重规划通常是浪费
  5. 选型规则:步数多且流程相对可预期 → Plan-and-Execute;路径不确定、步数少 → ReAct。步数 3~8 是经验区间不是规则。
  6. 它管哪一层:任务序列。单步内部怎么想是 ToTSelf-Ask 的事,做错了怎么办是 Reflexion 的事。

相关笔记:Agent 推理范式总览Reflexion 反思式自我纠错Self-Ask 自问自答多跳推理ToT 思维树