5.2 规划 Planning:任务分解与路径规划
在上一节中,我们系统剖析了 Agent 的四层架构——感知层负责理解用户意图、规划层负责分解任务与制定策略、执行层负责调用工具完成任务、记忆层负责存储和检索上下文。这四层如同一个精密齿轮组,彼此咬合、协同运转。其中,规划层是决定 Agent 智能水平的关键"齿轮":一个没有规划能力的 Agent 充其量是一个"指令执行器",而具备规划能力的 Agent 才真正成为一个"问题解决者"。
本节将深入规划层的内部机制,探讨 Agent 如何将模糊的用户需求转化为清晰的执行路径,并对比不同规划策略的优劣与适用场景。下一节(5.3 记忆系统)我们将转向四层架构的最后一环,看看 Agent 如何在跨会话、跨任务中保持知识的连贯性。
5.2.1 规划层到底在做什么
要理解规划层,不妨用一个日常类比来切入。
项目经理拆解任务——设想你刚入职一家新公司,老板说:"下季度我们要发布一款面向中小企业的智能财务系统,你来牵头。"如果你立刻打开 IDE 开始写代码,那多半会以混乱收场。一个成熟的项目经理会怎么做?
- 理解目标:先搞清楚"智能财务系统"的边界——覆盖哪些业务场景?目标用户是谁?时间节点是什么?
- 任务拆解:把大目标拆成模块——需求调研、技术选型、架构设计、核心开发、测试验收、上线运营,每个模块再细化为具体任务。
- 依赖排序:测试必须在开发之后,架构设计要在编码之前,但前端和后端可以并行推进。
- 资源分配:张三擅长后端就分到 API 开发,李四熟悉 React 就负责前端,CI/CD 流水线交给运维组。
- 动态调整:开发到一半发现第三方接口不稳定,立即调整方案——引入降级策略、重新排期。
Agent 的规划层做的就是这件事:将抽象的、高层的目标,转化为具体的、可执行的步骤序列,并管理步骤间的依赖与调整。区别在于,项目经理靠经验和直觉拆解任务,而 Agent 的规划层依靠的是大语言模型的推理能力加上结构化的规划策略。
用一句话概括:
感知层回答"用户要什么",规划层回答"怎么做",执行层回答"正在做",记忆层回答"之前做过什么"。
规划层承上启下,是 Agent 架构中信息密度最高、设计难度最大的一层。
5.2.2 规划的两个基本流派:静态与动态
在深入具体策略之前,我们需要先理解规划策略最根本的分类维度——什么时候做规划。
静态规划(Static Planning)
在执行开始前,一次性制定完整的计划,然后按计划逐步执行,中途不轻易修改。
静态规划流程:
┌──────┐ ┌──────────────────────┐ ┌──────────────────┐
│ 输入 │───▶│ 制定完整计划 │───▶│ 逐步执行 │
└──────┘ │ Step1→Step2→ │ │ Step1 │
│ Step3→Step4 │ │ ↓ │
└──────────────────────┘ │ Step2 │
│ ↓ │
│ Step3 │
│ ↓ │
│ Step4 → 输出 │
└──────────────────┘优点:全局视角,计划完整;无依赖的子任务可并行;中间步骤无需 LLM 参与,降低成本。
缺点:无法应对执行中的意外情况;初始计划若有误,后续步骤全部偏离。
动态规划(Dynamic Planning)
每执行一步后,根据当前结果重新评估并决定下一步。
动态规划流程:
┌──────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 输入 │───▶│ 规划Step1│───▶│ 执行Step1│───▶│ 评估结果 │
└──────┘ └──────────┘ └──────────┘ └────┬─────┘
│
┌────────────▼──────────┐
│ 任务是否完成? │
│ 是 → 输出结果 │
│ 否 → 重新规划下一步 │
└───────────────────────┘优点:灵活适应变化,出错可及时调整;适合不确定性高的任务。
缺点:每步都需 LLM 参与推理,成本高、速度慢;可能陷入局部最优。
理解了这两个流派,下面介绍的三种规划策略——CoT、ToT、ReAct——本质上都是在"静态"与"动态"光谱上的不同取舍。
5.2.3 三大规划策略:CoT、ToT、ReAct
1. Chain-of-Thought(CoT):线性思维链
CoT 是最朴素的规划策略,核心思想是:让 LLM 把推理过程写出来,而不是直接给答案。
打个比方,CoT 就像让学生"写出解题过程"——哪怕最终答案错了,老师也能看到是哪一步出错,进而纠正。对于 Agent 来说,CoT 的价值在于:它将"黑箱式的一次性回答"变成了"可追踪的推理链"。
工作流程:
用户提问 → LLM 逐步推理(Thought1 → Thought2 → ... → ThoughtN) → 最终答案示例:
问题:一个工厂有3条生产线,每条每天产200件产品。
每件利润5元。问该工厂30天的总利润是多少?
CoT 推理过程:
Thought1:3条生产线,每条每天产200件,总日产量 = 3 × 200 = 600件
Thought2:每件利润5元,日利润 = 600 × 5 = 3000元
Thought3:30天总利润 = 3000 × 30 = 90000元
Answer:90000元CoT 的规划特征:属于静态规划的雏形。它在推理开始前"隐式地"规划了推理路径,但这条路径是线性的、不可回溯的。如果第二步走错了方向,后面的推理全都会跟着偏。
适用场景:数学计算、逻辑推理、多跳问答等需要中间推理步骤的任务。
局限性:线性推进,无法处理需要"试错回溯"的问题——如果走到死胡同,无法折返。
2. Tree-of-Thoughts(ToT):树状探索
ToT 是对 CoT 的进阶。如果说 CoT 是"走一条路走到黑",那 ToT 就是"在每个岔路口都分出几条路,同时探索,保留最优路径"。
类比:想象你在一个迷宫里找出口。CoT 的做法是选一个方向一直走,走到死胡同就失败。ToT 的做法是在每个岔路口同时尝试多条路,哪条路看起来更有希望就继续走哪条,走不通的就回退。
工作流程:
┌─ Thought A1 ──→ Thought A1a ──→ ✓ 成功
Thought A ────────────┤
└─ Thought A2 ──→ 死胡同,剪枝
Thought B ────────────┤
├─ Thought B1 ──→ Thought B1a ──→ ✓ 成功(最优)
└─ Thought B2 ──→ ...每一步,ToT 会:
- 生成候选:为当前节点生成多个可能的下一步思考
- 评估打分:对每个候选项评估"这条路走下去有多大概率成功"
- 剪枝搜索:保留高分候选,丢弃低分候选(BFS 或 DFS 策略)
- 回溯:如果某条路径走到死胡同,回退到上一层尝试其他候选
ToT 的规划特征:介于静态与动态之间。它在规划阶段构建了一棵"思维树",通过搜索找到最优路径后再执行,但搜索过程本身是动态的、自适应的。
适用场景:24点游戏、创意写作、策略博弈等"答案空间大、需要试错"的任务。
局限性:计算成本高——每个节点都要生成多个候选并评估,LLM 调用次数是 CoT 的数倍。
3. ReAct:推理与行动交替
ReAct(Reasoning + Acting)是 Agent 领域最经典的规划策略。它将"思考"和"行动"交织在一起:先想一步,做一步,看结果,再想下一步。
类比:ReAct 像一个边查资料边写报告的研究员——不是提前把所有书读完再动笔(静态规划),也不是盲目地写一段再推翻重写(无规划),而是查一段资料、写一段分析、根据新发现调整方向,循环往复直到报告完成。
工作流程:
ReAct 循环:
Thought(思考)→ Action(行动/调用工具)→ Observation(观察结果)
↑ │
└──────────────── 循环 ──────────────────────┘
│
└── 当 Observation 足够回答问题时 → 输出最终答案示例:
问题:比亚迪2024年销量是多少?和2023年相比增长了多少?
Thought 1:我需要先搜索比亚迪2024年的销量数据
Action 1:search("比亚迪2024年销量")
Observation 1:比亚迪2024年全年销量约427万辆
Thought 2:现在我需要2023年的数据来计算增长率
Action 2:search("比亚迪2023年销量")
Observation 2:比亚迪2023年全年销量约302万辆
Thought 3:计算增长率 = (427-302)/302 ≈ 41.4%
Answer:比亚迪2024年销量约427万辆,较2023年的302万辆增长约41.4%ReAct 的规划特征:典型的动态规划。每一步的行动都基于上一步的观察结果,计划是逐步生成的,而非一次性制定的。
适用场景:需要调用外部工具、信息在执行过程中才能获取的任务(如搜索、数据库查询、API 调用)。
局限性:每步都要 LLM 推理,成本高;如果中间步骤出错,可能连锁影响后续决策。
三种策略对比
| 策略 | 思维结构 | 回溯能力 | LLM 调用次数 | 典型场景 |
|---|---|---|---|---|
| CoT | 线性链 | 无 | 1 次 | 数学推理、逻辑题 |
| ToT | 树状搜索 | 有(剪枝回溯) | 数十次 | 策略博弈、创意写作 |
| ReAct | 交替循环 | 无(但可动态调整) | 每步 1 次 | 工具调用、信息检索 |
一句话总结:CoT 是"想清楚再说",ToT 是"想多条路再选最优",ReAct 是"边想边做边调整"。
5.2.4 工程实现:Plan-and-Execute Agent
理解了规划策略的理论之后,我们来看一个完整的工程实现。Plan-and-Execute 是 LangChain 团队在 2024 年主推的 Agent 规划范式,其核心思想是:先规划,后执行,必要时重新规划。
下面这段代码用 LangGraph 实现了一个完整的 Plan-and-Execute Agent:
from langgraph.graph import StateGraph, END # 引入状态图构建器和终止节点
from typing import TypedDict, List, Annotated # 引入类型标注工具
import operator # 用于状态合并时列表的追加操作
# ── 第一步:定义 Agent 的全局状态 ──
# Agent 的所有节点共享这个状态对象,类似于项目经理手中的"项目看板"
class PlanExecuteState(TypedDict):
input: str # 用户原始输入
plan: List[str] # 规划器生成的步骤列表
past_steps: Annotated[List[tuple], operator.add] # 已完成步骤及其结果
response: str # 最终输出
# ── 第二步:实现规划器节点 ──
# 规划器负责将用户输入拆解为有序的步骤列表
def plan_step(state: PlanExecuteState):
"""根据用户输入生成执行计划(实际项目中这里会调用 LLM)"""
prompt = f"""你是一个任务规划专家。请将以下任务分解为3-5个步骤:
任务:{state['input']}
每行一个步骤,步骤应具体、可执行。"""
# 在实际项目中,这里调用 LLM 生成计划
# response = llm.invoke([HumanMessage(content=prompt)])
# plan = [step.strip() for step in response.content.split("\n") if step.strip()]
# 此处用硬编码计划做演示
plan = [
"搜索相关数据", # 步骤1:信息采集
"分析数据趋势", # 步骤2:数据分析
"生成可视化图表", # 步骤3:结果呈现
"撰写总结报告", # 步骤4:输出交付
]
print(f"📋 生成计划:共 {len(plan)} 步")
for i, step in enumerate(plan, 1): # 遍历计划,逐条打印
print(f" {i}. {step}")
return {"plan": plan} # 将计划写入状态,传递给下一个节点
# ── 第三步:实现执行器节点 ──
# 执行器从计划中取出当前步骤并执行
def execute_step(state: PlanExecuteState):
"""执行计划中的当前步骤"""
plan = state["plan"] # 取出完整计划
past_steps = state.get("past_steps", []) # 取出已完成步骤
current_idx = len(past_steps) # 通过已完成数量推算当前索引
if current_idx >= len(plan): # 所有步骤已完成
return {"response": "所有步骤已完成"}
current_step = plan[current_idx] # 取出当前要执行的步骤
print(f"⚡ 执行步骤 {current_idx+1}/{len(plan)}: {current_step}")
# 实际项目中这里会调用具体工具(搜索API、数据库等)
result = f"✅ 完成: {current_step}" # 模拟执行结果
# 将 (步骤, 结果) 元组追加到 past_steps
# operator.add 确保多次调用的结果自动合并
return {"past_steps": [(current_step, result)]}
# ── 第四步:实现重新规划器节点 ──
# 重新规划器检查执行结果,决定是否需要调整剩余计划
def replan_step(state: PlanExecuteState):
"""评估执行结果,决定继续、调整或结束"""
plan = state["plan"]
past_steps = state.get("past_steps", [])
if len(past_steps) >= len(plan): # 所有步骤执行完毕
print("🎉 所有步骤已完成!")
return {"response": "done"}
# 实际项目中,这里可以让 LLM 根据已执行步骤的结果
# 判断剩余计划是否需要修改
print(f"🔄 继续执行下一步...")
return {"response": "continue"}
# ── 第五步:定义条件路由 ──
# 根据重新规划器的判断结果决定下一步走向
def should_continue(state: PlanExecuteState):
"""路由函数:决定是继续执行还是结束"""
if len(state.get("past_steps", [])) >= len(state["plan"]):
return END # 全部完成,走向终止
return "execute_step" # 未完成,继续执行下一步
# ── 第六步:组装状态图 ──
workflow = StateGraph(PlanExecuteState)
workflow.add_node("plan_step", plan_step) # 注册规划节点
workflow.add_node("execute_step", execute_step) # 注册执行节点
workflow.add_node("replan_step", replan_step) # 注册重规划节点
workflow.set_entry_point("plan_step") # 设置入口:从规划开始
workflow.add_edge("plan_step", "execute_step") # 规划 → 执行
workflow.add_edge("execute_step", "replan_step") # 执行 → 重规划
workflow.add_conditional_edges( # 重规划 → 条件路由
"replan_step",
should_continue,
{"execute_step": "execute_step", END: END}
)
app = workflow.compile() # 编译为可执行的应用
# ── 运行 ──
result = app.invoke({
"input": "分析2024年全球AI Agent市场的三个主要趋势"
})
print("\n📊 最终结果:", result.get("response", "完成"))执行流程图:
入口
│
▼
plan_step(规划)──→ execute_step(执行)──→ replan_step(重规划)
↑ │
└──── 未完成 ────────────┘
│
完成 │
▼
END5.2.5 进阶范式:ReWOO 与 LLMCompiler
Plan-and-Execute 已经比 ReAct 高效很多,但它的执行仍然是串行的——一个步骤完成后才执行下一个。有没有办法进一步提升效率?ReWOO 和 LLMCompiler 给出了各自的答案。
ReWOO:推理与观察分离
ReWOO(Reasoning WithOut Observation)的核心创新是:将推理过程和工具调用彻底分离。传统 ReAct 中,每次工具调用都需要 LLM 先"思考"再"行动";ReWOO 则在规划阶段就一次性生成所有推理步骤和工具参数,然后批量执行。
传统 ReAct(串行,每步都调 LLM):
Thought → Action → Observation → Thought → Action → Observation → ...
ReWOO(规划一次,批量执行):
Plan(生成所有步骤+参数)→ 批量执行工具调用 → 汇总结果关键在于:ReWOO 在规划阶段就预填所有工具参数,无依赖的步骤可以完全并行执行。步骤间通过 #E1、#E2 这样的引用标记来传递数据:
# ReWOO 风格的规划:一次生成完整计划,含工具名、参数和依赖
def rewoo_planner(task: str) -> dict:
"""
生成包含所有推理步骤和工具调用的完整计划。
每个步骤的参数可以引用前面步骤的输出(#E1, #E2...),
从而在执行时自动处理数据依赖。
"""
plan = {
"task": task,
"steps": [
{
"id": "E1",
"tool": "search", # 第一步:搜索市场数据
"args": {"query": "电动汽车市场增长率 2024"},
"depends": [] # 无依赖,可立即执行
},
{
"id": "E2",
"tool": "search", # 第二步:搜索车企信息
"args": {"query": "比亚迪 特斯拉 市场份额"},
"depends": [] # 无依赖,与 E1 并行
},
{
"id": "E3",
"tool": "analyze", # 第三步:分析前两步结果
"args": {"data": ["#E1", "#E2"]}, # 引用 E1 和 E2 的输出
"depends": ["E1", "E2"] # 依赖前两步完成
}
]
}
return plan
# 执行器:根据依赖关系自动调度
def rewoo_executor(plan: dict):
"""按依赖关系执行计划,无依赖的步骤自动并行"""
steps = plan["steps"]
results = {} # 存储每个步骤的输出
# 实际项目中可用 asyncio 实现并行执行
for step in steps:
step_id = step["id"] # 取出步骤ID
tool_name = step["tool"] # 取出工具名
args = step["args"].copy() # 复制参数(避免修改原计划)
# 将参数中的引用标记(如 #E1)替换为实际结果
for key, value in args.items():
if isinstance(value, str) and value.startswith("#"):
ref_id = value[1:] # 去掉 # 前缀
args[key] = results[ref_id] # 替换为实际输出
elif isinstance(value, list): # 处理列表中的引用
args[key] = [results[v[1:]] if v.startswith("#") else v
for v in value]
# 执行工具调用(此处省略工具注册逻辑)
result = f"执行 {tool_name}({args})"
results[step_id] = result # 存储结果供后续步骤引用
print(f" {step_id}: {result}")
return resultsReWOO 的优势:E1 和 E2 无依赖关系,可以并行执行,无需等待。这在需要多次独立搜索的场景下,能显著缩短执行时间。
LLMCompiler:编译器思想
LLMCompiler 由 Google 提出,借鉴了传统编译器的优化思想。它将 Agent 的执行过程建模为DAG(有向无环图),通过依赖分析自动识别可并行的步骤,最大化执行效率。
可以把它想象成编译器对代码做指令重排——把没有数据依赖的指令安排在同一周期执行。LLMCompiler 对规划步骤做同样的事:分析步骤间的数据依赖,把无依赖的步骤分组并行执行。
三种进阶范式对比:
| 范式 | 规划方式 | 并行能力 | LLM 调用 | 代表场景 |
|---|---|---|---|---|
| Plan-and-Execute | 先规划后执行 | 有限(需手动标注) | 1+重规划 | 通用任务 |
| ReWOO | 一次规划含全部参数 | 强(无依赖步骤自动并行) | 1 次 | 批量搜索 |
| LLMCompiler | DAG 依赖分析 | 最强(自动调度) | 1 次 | 复杂并行 |
5.2.6 规划策略选择指南
面对不同的任务,该选择哪种规划策略?以下决策框架供参考:
任务复杂度 vs 规划策略选择
高复杂度 │ Plan-and-Execute + 动态重规划
│ (Multi-Agent + 分层规划)
│
│ ReAct(动态规划,逐步调整)
中复杂度 │ Plan-and-Execute(静态规划)
│
低复杂度 │ ReWOO / 直接调用
│
└──────────────────────────────▶
低不确定性 高不确定性选择时的三个关键问题:
任务是否可预测? 如果步骤明确、变化少,用静态规划(Plan-and-Execute、ReWOO);如果不确定性高、中间结果可能改变后续方向,用动态规划(ReAct)。
成本敏感度如何? 成本敏感的场景优先选择 Plan-and-Execute 或 ReWOO——它们只在规划阶段调用 LLM,执行阶段无需 LLM 参与,调用次数远少于 ReAct。
是否需要并行? 任务中有大量可并行的独立子步骤时,选择 ReWOO 或 LLMCompiler,通过并行执行显著缩短总耗时。
实用速查表:
| 任务特征 | 推荐策略 | 理由 |
|---|---|---|
| 单步问答,无需工具 | CoT | 一次推理即可,无需复杂规划 |
| 多步搜索,结果可预判 | ReWOO | 批量并行搜索,效率最高 |
| 多步搜索,结果不可预判 | ReAct | 需要根据中间结果调整方向 |
| 复杂报告生成 | Plan-and-Execute | 全局规划,避免遗漏关键步骤 |
| 创意发散任务 | ToT | 需要探索多条思路择优 |
| 大规模并行任务 | LLMCompiler | DAG 自动调度,最大化并行 |
5.2.7 常见误区
在实际开发 Agent 规划层时,有几个反复出现的陷阱值得警惕。
误区一:所有任务都用 ReAct
ReAct 是最直觉的规划策略——"边想边做"很符合人类思维习惯,因此很多开发者默认所有任务都用 ReAct。但 ReAct 每步都需要 LLM 推理,一个 5 步任务可能产生 10 次以上的 LLM 调用(每步 Thought + Action),成本是 Plan-and-Execute 的 5~10 倍。
正确做法:先评估任务的可预测性。如果步骤间依赖关系明确,优先用 Plan-and-Execute,只在真正需要动态调整时才降级为 ReAct。
误区二:计划一旦制定就不再修改
静态规划不是"死板执行"。Plan-and-Execute 中的 Replanner 节点存在的意义就是检查执行结果、决定是否需要调整剩余计划。有些开发者把 Replanner 实现为简单的计数器——"还有步骤没执行就继续"——这等于丢弃了重新规划的能力。
正确做法:Replanner 应该接收已完成步骤的结果,让 LLM 判断"基于已获取的信息,剩余计划是否仍然合理"。如果发现偏差,及时修正。
误区三:忽视步骤间的依赖关系
在 ReWOO 或 LLMCompiler 中,依赖关系标注错误会导致严重问题——要么把不该并行的步骤并行了(结果引用了尚未生成的数据),要么把可以并行的步骤串行执行了(白白浪费时间)。
正确做法:规划阶段就明确标注每个步骤的
depends列表,执行前做依赖校验。可以参考数据库事务的"读已提交"隔离级别思想——一个步骤只能引用已经完成的步骤的输出。
误区四:规划粒度过细或过粗
把任务拆成 20 个微步骤,每步只做一件极小的事——这会导致规划开销远超执行开销。反过来,只拆成 2 个粗粒度步骤,等于没有规划——和直接让 LLM 回答没有区别。
正确做法:一般 3~7 个步骤为宜。每个步骤应该是一个"可独立验证的子任务"——能明确判断它是否成功完成。
误区五:把规划层等同于 Prompt 工程
规划策略的选择是架构层面的决策,不是写一个好的 Prompt 就能替代的。一个好的 Plan-and-Execute 架构,即使 Prompt 写得简单,效果也好于用复杂 Prompt 驱动的 ReAct。
正确做法:先确定规划范式(静态/动态/混合),再设计状态图结构,最后优化 Prompt。架构决定上限,Prompt 决定下限。
5.2.8 本节小结
规划层是 Agent 四层架构中最核心的"智能引擎",它的职责是将模糊的用户目标转化为可执行的步骤序列。本节的核心要点如下:
规划的本质是任务分解与路径规划——如同项目经理拆解项目,需要理解目标、拆分子任务、分析依赖、分配资源、动态调整。
两大基本流派:静态规划在执行前一次性制定计划(成本低、速度快但不够灵活);动态规划在每步执行后重新评估(灵活但成本高)。实际工程中常采用混合策略——静态规划为主、关键节点动态重规划。
三大规划策略各有侧重:CoT 适合线性推理(一次调用);ToT 适合需要试错回溯的探索性任务(树状搜索);ReAct 适合需要调用工具的交互式任务(交替循环)。
进阶范式在并行化上不断演进:Plan-and-Execute 是基础范式,ReWOO 通过分离推理与观察实现批量并行,LLMCompiler 借鉴编译器思想用 DAG 自动调度最大化并行。
策略选择取决于三个维度:任务可预测性(决定静态还是动态)、成本敏感度(决定 LLM 调用次数)、并行需求(决定是否需要依赖分析)。
常见误区包括盲目使用 ReAct、忽视重新规划、依赖标注错误、规划粒度不当、混淆规划架构与 Prompt 工程——每一个都可能让 Agent 的实际表现大打折扣。
规划解决了"怎么做"的问题,但 Agent 还需要"记住做过什么"——下一节(5.3 记忆系统)将探讨 Agent 如何在跨会话、跨任务中存储和检索上下文信息,构建真正持久的智能体。
参考文献
- Yao, S. et al. "ReAct: Synergizing Reasoning and Acting in Language Models." arXiv:2210.03629, 2022.
- Wei, J. et al. "Chain-of-Thought Prompting Elicits Reasoning in Large Language Models." arXiv:2201.11903, 2022.
- Yao, S. et al. "Tree of Thoughts: Deliberate Problem Solving with Large Language Models." arXiv:2305.10601, 2023.
- Xu, B. et al. "ReWOO: Decoupling Reasoning from Observations for Efficient Augmented Language Models." arXiv:2305.18323, 2023.
- Kim, S. et al. "LLMCompiler: An LLM Compiler for Parallel Function Calling." arXiv:2312.04511, 2023.
- LangChain Blog. "Plan-and-Execute Agents." https://www.langchain.com/blog/planning-agents, 2024.