Skip to content

5.2 规划 Planning:任务分解与路径规划

在上一节中,我们系统剖析了 Agent 的四层架构——感知层负责理解用户意图、规划层负责分解任务与制定策略、执行层负责调用工具完成任务、记忆层负责存储和检索上下文。这四层如同一个精密齿轮组,彼此咬合、协同运转。其中,规划层是决定 Agent 智能水平的关键"齿轮":一个没有规划能力的 Agent 充其量是一个"指令执行器",而具备规划能力的 Agent 才真正成为一个"问题解决者"。

本节将深入规划层的内部机制,探讨 Agent 如何将模糊的用户需求转化为清晰的执行路径,并对比不同规划策略的优劣与适用场景。下一节(5.3 记忆系统)我们将转向四层架构的最后一环,看看 Agent 如何在跨会话、跨任务中保持知识的连贯性。


5.2.1 规划层到底在做什么

要理解规划层,不妨用一个日常类比来切入。

项目经理拆解任务——设想你刚入职一家新公司,老板说:"下季度我们要发布一款面向中小企业的智能财务系统,你来牵头。"如果你立刻打开 IDE 开始写代码,那多半会以混乱收场。一个成熟的项目经理会怎么做?

  1. 理解目标:先搞清楚"智能财务系统"的边界——覆盖哪些业务场景?目标用户是谁?时间节点是什么?
  2. 任务拆解:把大目标拆成模块——需求调研、技术选型、架构设计、核心开发、测试验收、上线运营,每个模块再细化为具体任务。
  3. 依赖排序:测试必须在开发之后,架构设计要在编码之前,但前端和后端可以并行推进。
  4. 资源分配:张三擅长后端就分到 API 开发,李四熟悉 React 就负责前端,CI/CD 流水线交给运维组。
  5. 动态调整:开发到一半发现第三方接口不稳定,立即调整方案——引入降级策略、重新排期。

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 会:

  1. 生成候选:为当前节点生成多个可能的下一步思考
  2. 评估打分:对每个候选项评估"这条路走下去有多大概率成功"
  3. 剪枝搜索:保留高分候选,丢弃低分候选(BFS 或 DFS 策略)
  4. 回溯:如果某条路径走到死胡同,回退到上一层尝试其他候选

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:

python
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(重规划)
                         ↑                        │
                         └──── 未完成 ────────────┘

                                              完成 │

                                                END

5.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 这样的引用标记来传递数据:

python
# 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 results

ReWOO 的优势:E1 和 E2 无依赖关系,可以并行执行,无需等待。这在需要多次独立搜索的场景下,能显著缩短执行时间。

LLMCompiler:编译器思想

LLMCompiler 由 Google 提出,借鉴了传统编译器的优化思想。它将 Agent 的执行过程建模为DAG(有向无环图),通过依赖分析自动识别可并行的步骤,最大化执行效率。

可以把它想象成编译器对代码做指令重排——把没有数据依赖的指令安排在同一周期执行。LLMCompiler 对规划步骤做同样的事:分析步骤间的数据依赖,把无依赖的步骤分组并行执行。

三种进阶范式对比:

范式规划方式并行能力LLM 调用代表场景
Plan-and-Execute先规划后执行有限(需手动标注)1+重规划通用任务
ReWOO一次规划含全部参数强(无依赖步骤自动并行)1 次批量搜索
LLMCompilerDAG 依赖分析最强(自动调度)1 次复杂并行

5.2.6 规划策略选择指南

面对不同的任务,该选择哪种规划策略?以下决策框架供参考:

任务复杂度 vs 规划策略选择

高复杂度  │  Plan-and-Execute + 动态重规划
         │  (Multi-Agent + 分层规划)

         │  ReAct(动态规划,逐步调整)
中复杂度  │  Plan-and-Execute(静态规划)

低复杂度  │  ReWOO / 直接调用

         └──────────────────────────────▶
            低不确定性        高不确定性

选择时的三个关键问题

  1. 任务是否可预测? 如果步骤明确、变化少,用静态规划(Plan-and-Execute、ReWOO);如果不确定性高、中间结果可能改变后续方向,用动态规划(ReAct)。

  2. 成本敏感度如何? 成本敏感的场景优先选择 Plan-and-Execute 或 ReWOO——它们只在规划阶段调用 LLM,执行阶段无需 LLM 参与,调用次数远少于 ReAct。

  3. 是否需要并行? 任务中有大量可并行的独立子步骤时,选择 ReWOO 或 LLMCompiler,通过并行执行显著缩短总耗时。

实用速查表

任务特征推荐策略理由
单步问答,无需工具CoT一次推理即可,无需复杂规划
多步搜索,结果可预判ReWOO批量并行搜索,效率最高
多步搜索,结果不可预判ReAct需要根据中间结果调整方向
复杂报告生成Plan-and-Execute全局规划,避免遗漏关键步骤
创意发散任务ToT需要探索多条思路择优
大规模并行任务LLMCompilerDAG 自动调度,最大化并行

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 四层架构中最核心的"智能引擎",它的职责是将模糊的用户目标转化为可执行的步骤序列。本节的核心要点如下:

  1. 规划的本质是任务分解与路径规划——如同项目经理拆解项目,需要理解目标、拆分子任务、分析依赖、分配资源、动态调整。

  2. 两大基本流派:静态规划在执行前一次性制定计划(成本低、速度快但不够灵活);动态规划在每步执行后重新评估(灵活但成本高)。实际工程中常采用混合策略——静态规划为主、关键节点动态重规划。

  3. 三大规划策略各有侧重:CoT 适合线性推理(一次调用);ToT 适合需要试错回溯的探索性任务(树状搜索);ReAct 适合需要调用工具的交互式任务(交替循环)。

  4. 进阶范式在并行化上不断演进:Plan-and-Execute 是基础范式,ReWOO 通过分离推理与观察实现批量并行,LLMCompiler 借鉴编译器思想用 DAG 自动调度最大化并行。

  5. 策略选择取决于三个维度:任务可预测性(决定静态还是动态)、成本敏感度(决定 LLM 调用次数)、并行需求(决定是否需要依赖分析)。

  6. 常见误区包括盲目使用 ReAct、忽视重新规划、依赖标注错误、规划粒度不当、混淆规划架构与 Prompt 工程——每一个都可能让 Agent 的实际表现大打折扣。

规划解决了"怎么做"的问题,但 Agent 还需要"记住做过什么"——下一节(5.3 记忆系统)将探讨 Agent 如何在跨会话、跨任务中存储和检索上下文信息,构建真正持久的智能体。


参考文献

  1. Yao, S. et al. "ReAct: Synergizing Reasoning and Acting in Language Models." arXiv:2210.03629, 2022.
  2. Wei, J. et al. "Chain-of-Thought Prompting Elicits Reasoning in Large Language Models." arXiv:2201.11903, 2022.
  3. Yao, S. et al. "Tree of Thoughts: Deliberate Problem Solving with Large Language Models." arXiv:2305.10601, 2023.
  4. Xu, B. et al. "ReWOO: Decoupling Reasoning from Observations for Efficient Augmented Language Models." arXiv:2305.18323, 2023.
  5. Kim, S. et al. "LLMCompiler: An LLM Compiler for Parallel Function Calling." arXiv:2312.04511, 2023.
  6. LangChain Blog. "Plan-and-Execute Agents." https://www.langchain.com/blog/planning-agents, 2024.