跳到主要内容

Plan-and-Execute

本节定位

ReAct 很适合边走边看。
但当任务变长以后,它会遇到一个典型问题:

  • 每一步都临场决定,容易飘

这时很多系统会换一种组织方式:

先用 planner 拆计划,再由 executor 按计划逐步完成。

这就是 Plan-and-Execute 的核心。

学习目标

  • 理解 Plan-and-Execute 为什么适合长任务
  • 理解 planner 和 executor 的职责分离
  • 通过可运行示例看懂一个最小的“先规划再执行”系统
  • 理解它和 ReAct 的差别与取舍

一、为什么任务一长,就更需要“先规划”?

1.1 边走边想容易丢全局

如果任务只有一步两步,
ReAct 的即兴决策通常够用。

但如果任务变成:

  • 整理一周售后数据
  • 统计高频问题
  • 生成汇报
  • 再给出改进建议

这类长任务就有更强的全局结构。

如果每一步都临时决定,
常见问题会是:

  • 漏步骤
  • 顺序错
  • 做了重复工作

1.2 Planner 的作用:先把大任务变成小任务

Planner 最核心的价值不是“更聪明”,
而是:

  • 先画路线图

它会回答:

  • 一共有哪些步骤
  • 步骤顺序是什么
  • 哪些结果要传给后面

1.3 Executor 的作用:专心把当前步骤做好

把计划拆出来后,
执行器就可以少想一点“战略问题”,
更多关注:

  • 当前步骤怎么完成
  • 当前工具怎么调
  • 当前结果怎么落库

这让系统更稳,也更容易调试。


二、Plan-and-Execute 和 ReAct 的差别到底在哪里?

2.1 ReAct 更像边调查边走

它适合:

  • 信息未知很多
  • 下一步取决于上一轮 observation

2.2 Plan-and-Execute 更像先列施工清单

它适合:

  • 任务结构比较清楚
  • 步骤可以预先拆解
  • 希望减少即兴漂移

2.3 两者不是敌对关系

很多真实系统其实会混用:

  • 高层先 Plan-and-Execute
  • 每个执行步骤内部再用 ReAct

也就是说:

  • 规划负责全局
  • ReAct 负责局部探索

三、先跑一个真正的最小 Plan-and-Execute 示例

下面这个例子会模拟一个“售后周报 Agent”。
用户任务是:

  • 统计售后问题
  • 找出高频意图
  • 生成一份简短总结

我们会明确拆出:

  • planner
  • executor
tickets = [
{"intent": "refund", "text": "订单未发货,可以退款吗?"},
{"intent": "refund", "text": "退款多久到账?"},
{"intent": "password", "text": "忘记密码怎么办?"},
{"intent": "address", "text": "地址填错了还能改吗?"},
{"intent": "refund", "text": "退款为什么还没到账?"},
]


def planner(goal):
return [
{"step": "load_tickets", "description": "读取本周售后工单"},
{"step": "count_intents", "description": "统计各类问题数量"},
{"step": "find_top_intent", "description": "找出最高频问题"},
{"step": "draft_report", "description": "生成简短周报"},
]


def executor(task, context):
name = task["step"]

if name == "load_tickets":
context["tickets"] = tickets
return "已读取 5 条工单"

if name == "count_intents":
counts = {}
for item in context["tickets"]:
counts[item["intent"]] = counts.get(item["intent"], 0) + 1
context["intent_counts"] = counts
return counts

if name == "find_top_intent":
counts = context["intent_counts"]
top_intent = max(counts, key=counts.get)
context["top_intent"] = top_intent
return top_intent

if name == "draft_report":
counts = context["intent_counts"]
top_intent = context["top_intent"]
report = (
f"本周共处理 {len(context['tickets'])} 条售后工单。"
f"最高频问题是 {top_intent},出现 {counts[top_intent]} 次。"
f"建议优先优化 {top_intent} 流程和 FAQ 文案。"
)
context["report"] = report
return report

raise ValueError(f"Unknown step: {name}")


goal = "生成本周售后问题周报"
plan = planner(goal)
context = {}
trace = []

for task in plan:
output = executor(task, context)
trace.append({"task": task["step"], "output": output})

print("plan:")
for item in plan:
print("-", item)

print("\ntrace:")
for item in trace:
print(item)

print("\nfinal report:")
print(context["report"])

3.1 这段代码最关键的价值是什么?

它清楚分开了两件事:

  1. 规划
    确定要做哪些步骤
  2. 执行
    真正把步骤跑完,并把结果放进 context

这就是 Plan-and-Execute 最本质的结构。

3.2 context 在这里扮演什么角色?

它就是执行期的共享状态。

前一步产出的:

  • tickets
  • intent_counts
  • top_intent

都会被后一步继续使用。

所以 Plan-and-Execute 的关键并不只是“有 plan”,
还包括:

  • 中间产物怎样被安全传递

3.3 为什么这比单纯 for step in plan 更值得学?

因为这不是在演示一个循环,
而是在演示:

  • 长任务如何拆分
  • 依赖如何传递
  • 最终结果如何逐步汇总

四、Plan-and-Execute 什么时候特别有价值?

4.1 长任务

例如:

  • 写报告
  • 做研究总结
  • 整理知识库
  • 搭建多步骤业务流程

4.2 需要稳定复现的流程

如果你希望同类任务每次都按相近结构执行,
那显式计划会比完全即兴更稳。

4.3 需要人类审阅计划的场景

有些任务里,
你甚至会先把 plan 给人看一眼,再决定是否执行。

例如:

  • 高风险操作
  • 复杂数据处理
  • 自动化流程变更

五、它最容易出什么问题?

5.1 计划一开始就拆错

如果 planner 把任务理解错了,
后面 executor 再认真也没用。

5.2 计划过死,不会根据新观察调整

这正是 Plan-and-Execute 的典型短板。

如果外部世界变化很快,
过于固定的计划可能会显得僵硬。

5.3 执行器和计划描述脱节

常见情况:

  • planner 写了一个模糊步骤
  • executor 却不知道怎么落地

所以计划步骤最好:

  • 粒度明确
  • 可以执行
  • 输入输出清楚

六、工程上怎样让 Plan-and-Execute 更稳?

6.1 让 plan 结构化

不要只生成一串自然语言。
更好的形式通常是:

  • step id
  • description
  • input
  • output

6.2 每步执行完都写回 context

这样更利于:

  • 调试
  • 回放
  • 重试

6.3 允许必要时 replan

Plan-and-Execute 最稳的版本往往不是:

  • 一次计划,永不修改

而是:

  • 大方向先规划
  • 遇到重大偏差时允许重规划

七、常见误区

7.1 误区一:有了 plan 就一定更聪明

计划能提升稳定性,
但前提是计划本身质量够好。

7.2 误区二:所有任务都要先 planner 再 executor

不一定。
短任务、强交互任务,ReAct 往往更自然。

7.3 误区三:计划只要写出步骤名就够了

真正可执行的计划,还需要:

  • 步骤粒度
  • 状态依赖
  • 产出定义

小结

这节最重要的,不是把 Plan-and-Execute 当成另一个时髦名字,
而是理解它的核心工程价值:

当任务够长、够复杂、需要更稳定复现时,先规划再执行能显著减少即兴漂移,让系统更容易调试、审阅和维护。

只要这一层建立起来,
你后面再看 DAG 规划、多 Agent 分工和任务图调度,就会更顺。


练习

  1. 把示例里的“售后周报”换成“整理知识库回答”或“做竞品调研”,重新写一版 plan。
  2. 为什么说长任务比短任务更需要 planner?
  3. 如果执行到一半发现目标变了,你会如何设计 replan 机制?
  4. 想一想:哪些任务更适合 ReAct,哪些更适合 Plan-and-Execute?