第08章 规划模式(Plan-and-Execute、Chain-of-Thought、Reflection)

分享

「任务助手 Agent」获得了"战略眼光"——从第07章的"边想边做"(ReAct 一步一步线性推进), 进化为"先规划全局、再分步执行、执行完还要反思改进"。 这是让 Agent 能处理复杂、多阶段任务的关键能力。

TL;DR

30 秒速读:Plan-and-Execute 让 Agent 先把任务分解成步骤列表(用 response_format=json_object 强制结构化输出),再逐步执行,最后汇总结果;Reflection 让 Agent 审视自己的输出并改进。

如果只记一件事:用 response_format={"type": "json_object"} + Pydantic 校验,让模型输出结构化的步骤列表,这是规划模式的技术基础。


本章目标

学完本章,你将理解:

  1. 复杂任务为何需要先规划:减少返工、提高质量、降低成本
  2. Plan-and-Execute 模式:先分解任务成步骤列表,再逐步执行,最后汇总
  3. Chain-of-Thought 深度推理:让模型在规划阶段做充分的"思考"
  4. Reflection / Self-Critique:让 Agent 审视自己的输出并改进
  5. 何时该用规划——简单任务别过度规划(反模式)
  6. 反模式:简单任务强加规划、规划后不验证

1. 复杂任务为何需要先规划

1.1 ReAct 的局限:边想边做容易"跑偏"

第07章的 ReAct 是线性推理——每一步只看到上一步的结果,然后决定下一步。这对简单任务(查天气、算算术)足够,但对复杂任务会出问题。

例子:让 Agent 写一篇《AI Agent 调研报告》

用 ReAct(无规划):

Step 1: Thought: 先搜一下 AI Agent 是什么。Action: search[AI Agent 定义]
Step 2: Thought: 搜到了定义。再搜应用。Action: search[AI Agent 应用]
Step 3: Thought: 应用有了。搜框架。Action: search[AI Agent 框架]
Step 4: Thought: 框架有了。我想想还要搜什么... 哦,发展趋势。
        Action: search[AI Agent 趋势]
Step 5: Thought: 好像漏了挑战和风险。Action: search[AI Agent 挑战]
Step 6: Thought: 信息够了,开始写。Final Answer:(写报告)...

问题: - "想到哪搜到哪":每步的 Thought 都在临时决定下一步搜什么,缺乏全局视角 - 容易遗漏:写到一半发现"漏了某个维度",返工 - 难以并行:步骤之间隐式依赖,无法预先批量检索 - 质量不稳定:不同运行可能走完全不同的路径

1.2 规划的价值:先想清楚再动手

用 Plan-and-Execute(先规划):

Phase 1 (Plan):
  一次性分解任务:
    1. 搜集 AI Agent 的定义与核心特征
    2. 搜集 AI Agent 的典型应用场景
    3. 搜集主流 AI Agent 框架
    4. 搜集 AI Agent 的发展趋势与挑战
    5. 综合以上信息撰写调研报告

Phase 2 (Execute):
  按计划逐步执行(每步可以并行检索,也可以串行)

Phase 3 (Synthesize):
  汇总所有步骤结果,生成最终报告

好处: - 全局视野:规划阶段一次性看清任务全貌,减少遗漏 - 可预测:用户/开发者能审查计划,确认方向正确再执行 - 可并行:步骤独立时可以并发执行(检索任务尤其适合) - 质量更高:综合阶段有完整信息,报告更全面

💡 类比:这就像写代码。ReAct 是"不设计就开写,遇到问题改设计";Plan-and-Execute 是"先写设计文档,评审通过再编码"。后者返工率低得多。


2. Plan-and-Execute 模式

2.1 三阶段架构

Plan-and-Execute 把任务处理分成三个明确的阶段:

┌─────────────────────────────────────────────────────────┐
│  Phase 1: Plan(规划)                                   │
│  输入:用户任务                                          │
│  输出:步骤列表 [step1, step2, ..., stepN]              │
│  实现:用 response_format=json_object 强制结构化输出     │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│  Phase 2: Execute(执行)                                │
│  输入:步骤列表                                          │
│  对每个步骤:调用工具 / 子查询 / 子 LLM                  │
│  输出:每步的执行结果 [result1, result2, ..., resultN]   │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│  Phase 3: Synthesize(汇总)                             │
│  输入:所有步骤结果                                      │
│  输出:综合的最终输出                                    │
└─────────────────────────────────────────────────────────┘

2.2 Phase 1:用结构化输出生成计划

规划阶段的核心是让模型输出一个结构化的步骤列表,而不是自由文本。

from pydantic import BaseModel

class Plan(BaseModel):
    """任务分解计划。"""
    steps: list[str]  # 有序的步骤列表

# 用 response_format 强制 JSON 输出
response = client.chat.completions.create(
    model=cfg.model,
    messages=[
        {"role": "system", "content": PLAN_SYSTEM_PROMPT},
        {"role": "user", "content": task},
    ],
    response_format={"type": "json_object"},  # 强制 JSON
)

# Pydantic 解析 + 校验
plan = Plan.model_validate_json(response.choices[0].message.content)
for i, step in enumerate(plan.steps, 1):
    print(f"步骤 {i}: {step}")

关键点: - response_format={"type": "json_object"} 强制模型输出合法 JSON(第02章学过) - Pydantic Plan{steps: list[str]} 做类型校验,保证拿到的是字符串列表 - PLAN_SYSTEM_PROMPT 约束步骤格式(如"每步要具体、可执行、3-6 步")

2.3 Phase 2:逐步执行

执行阶段遍历步骤列表,对每个步骤做处理。每个步骤可以: - 调用一个工具(如搜索、计算) - 做一次子 LLM 调用(如"针对这个子问题写一段分析") - 组合多个操作

results = []
for i, step in enumerate(plan.steps, 1):
    print(f"OUT:execute:step{i}: 执行: {step}")
    result = execute_step(step)  # 调工具或子查询
    results.append(result)
    print(f"OUT:execute:step{i}: 结果: {result[:80]}")

与 ReAct 的区别: - ReAct 每步都问模型"下一步干什么"——动态决策 - Plan-and-Execute 步骤在规划阶段就定好了——执行阶段只是机械执行 - 这让执行阶段可并行、可预测、可回放

2.4 Phase 3:汇总

汇总阶段把所有步骤结果综合成最终输出。这通常是另一次 LLM 调用:

synthesis_prompt = f"""
你是一个综合助手。用户任务:{task}
已经完成了以下步骤,收集到以下信息:
{format_results(results)}

请基于以上信息,完成用户的原始任务。
"""

final = client.chat.completions.create(
    model=cfg.model,
    messages=[{"role": "user", "content": synthesis_prompt}],
)

3. Chain-of-Thought:深度推理

3.1 CoT 是规划的地基

第02章我们学过 Chain-of-Thought——让模型"一步一步思考"。在 Plan-and-Execute 中,CoT 发生在规划阶段

一个好的 PLAN_SYSTEM_PROMPT 会引导模型先做 CoT 再分解:

你是一个任务规划助手。用户会给你一个复杂任务,你需要:

1. 先理解任务的本质(这个任务到底要解决什么问题)
2. 思考完成任务需要哪些信息/操作(CoT 推理)
3. 把任务分解成 3-6 个有序的、具体的、可执行的步骤

输出 JSON 格式:{"steps": ["步骤1", "步骤2", ...]}

示例:
任务:写一篇 AI Agent 调研报告
思考:调研报告需要覆盖定义、应用、框架、趋势。每个维度单独检索,
最后综合。
步骤:
1. 检索 AI Agent 的定义与核心特征
2. 检索 AI Agent 的典型应用场景
3. 检索主流 AI Agent 开发框架
4. 检索 AI Agent 的发展趋势与挑战
5. 综合以上信息撰写调研报告

注意:虽然 prompt 里写了"思考",但因为用了 response_format=json_object, 模型最终的输出只是 {"steps": [...]} JSON。CoT 发生在模型内部(推理过程被压缩进步骤质量里)。

3.2 CoT vs Plan-and-Execute 的关系

不要混淆:

Chain-of-Thought Plan-and-Execute
是什么 一种推理引导技术(Prompt 技巧) 一种任务分解架构(流程设计)
粒度 单次 LLM 调用内的"逐步思考" 跨多次调用的"先规划再执行"
输出 自由文本推理 结构化步骤列表
关系 CoT 是 Plan 阶段的地基(帮模型想清楚) Plan-and-Execute 是 CoT 的工程化(把思考变成可执行流程)

一句话:Plan-and-Execute 的规划阶段用 CoT 引导模型思考,但把思考结果固化为结构化步骤。


4. Reflection / Self-Critique:让 Agent 审视自己

4.1 为什么需要反思

即使有规划,Agent 的第一次输出也可能不完美: - 遗漏了重要信息 - 逻辑有漏洞 - 结构不清晰 - 准确性存疑

Reflection 的思路简单却强大:让同一个 Agent 审视自己的输出,指出不足,然后改进。

这模拟了人类的工作方式——写完初稿后"回头看一眼",发现能改进的地方。

4.2 三轮反思流程

┌──────────────────────────────────────────────┐
│  Round 1: 生成初版                            │
│  输入:用户任务                               │
│  输出:draft(初版答案)                      │
└──────────────────────────────────────────────┘
                    ↓
┌──────────────────────────────────────────────┐
│  Round 2: 反思 / 自我批评                     │
│  输入:用户任务 + draft                       │
│  角色:你现在是个严格的审稿人                 │
│  输出:critique(指出 2-3 个不足)            │
└──────────────────────────────────────────────┘
                    ↓
┌──────────────────────────────────────────────┐
│  Round 3: 改进                                │
│  输入:用户任务 + draft + critique            │
│  输出:revised(改进版答案)                  │
└──────────────────────────────────────────────┘

4.3 关键:反思 Prompt 的设计

反思阶段要用一个不同角色的 Prompt,强制模型从"批评者"视角看自己的输出:

REFLECTION_PROMPT = """\
你是一个严格的审稿人。请审视以下初版答案,指出它的不足。

用户问题:{question}
初版答案:{draft}

请从以下维度批评:
1. 完整性:有没有遗漏重要信息?
2. 准确性:有没有事实错误或逻辑漏洞?
3. 结构:组织是否清晰?

只指出不足(2-3 点),不要给出完整改进版。
"""

为什么有效: - 角色切换(从"作者"变"审稿人")让模型用不同视角审视 - 显式列出批评维度(完整性/准确性/结构)避免泛泛而谈 - "只指出不足"防止模型直接重写(那样就跳过了诊断)

4.4 改进阶段

改进阶段把初版和反思一起喂给模型:

REVISE_PROMPT = """\
用户问题:{question}
初版答案:{draft}
审稿意见:{critique}

请根据审稿意见改进初版答案,输出最终版。
"""

4.5 Reflection 的代价

Reflection 不是免费的: - 延迟翻倍:初版 + 反思 + 改进 = 3 次 LLM 调用(是单次的 3 倍延迟) - 成本翻倍:3 次调用的 token 总和 - 边际收益递减:第 2 轮反思改进通常明显,第 3、4 轮收益很小

实践建议:对质量敏感的任务(写作、代码审查、重要决策)用 Reflection;对速度敏感的任务(简单问答、分类)不要用。


5. 何时该用规划(决策框架)

任务特征 是否规划 示例
单步任务(一次查询/计算) ❌ 不规划 "北京天气?"、"2+2=?"
2-3 步,步骤隐式明确 ❌ 不规划 "北京和上海哪个更热?"(ReAct 够用)
4+ 步,步骤需要设计 ✅ Plan-and-Execute "写调研报告"、"制定旅行计划"
输出质量要求高 ✅ + Reflection "写重要邮件"、"生成代码"
任务结构未知,需探索 ⚠️ ReAct 更合适 "调试这个 bug"(步骤未知)

经验法则: - 如果你能一眼看出任务需要哪些步骤 → 不规划(直接做或用 ReAct) - 如果步骤需要想一想才能确定 → 用 Plan-and-Execute - 如果输出质量比速度重要 → 加 Reflection


6. Plan-and-Execute vs ReAct vs Reflection 对比

维度 ReAct(第07章) Plan-and-Execute Reflection
核心思想 边想边做 先规划再执行 做完反思改进
流程 Thought→Action 循环 Plan→Execute→Synthesize Draft→Critique→Revise
决策时机 每步动态决策 规划阶段一次决策 执行后回顾决策
适合任务 步骤未知、需探索 步骤明确、多阶段 质量敏感的生成
延迟 中(取决于步数) 中高(规划+执行) 高(3 次调用)
可预测性 低(路径不确定) 高(计划可审查)
可并行 难(步骤间依赖) 易(独立步骤可并发) 不适用

它们不是互斥的——可以组合: - Plan-and-Execute + Reflection:规划→执行→汇总→反思→改进(本章 Demo 2 体现) - ReAct + Plan:用 ReAct 执行每个计划步骤(复杂任务的高级玩法)


7. 反模式(什么不该做)

❌ 反模式 1:简单任务强加规划

# 坏:给"查天气"这种单步任务强加规划
task = "北京今天天气怎么样?"
plan = generate_plan(task)
# plan.steps = ["1. 查北京天气", "2. 总结"]  # 多此一举!
for step in plan.steps:
    execute(step)  # 纯增加延迟和成本

后果: - 延迟增加:规划本身要一次 LLM 调用,单步任务被拖慢 - 成本浪费:为"查天气"这种任务多花一次 API 调用的钱 - 用户体验差:简单问题等 5 秒才返回

正确:先用任务复杂度判断,简单任务直接做,复杂任务才规划。

❌ 反模式 2:规划后不验证(计划脱离实际)

# 坏:生成了计划就直接执行,不检查计划是否合理
plan = generate_plan(task)
# 如果 plan.steps = ["1. 调用不存在的工具", "2. 查询需要权限的数据"]
for step in plan.steps:
    execute(step)  # 第一步就崩了,整个流程失败

后果: - 计划里的步骤可能不可执行(调用了不存在的工具、访问了没权限的数据) - 一步失败导致整个流程中断 - 用户看到"规划得很漂亮"但执行全失败,信任崩塌

正确: - 执行前验证计划(步骤是否可执行、依赖是否满足) - 每步执行有错误处理,失败时跳过或降级,不要让整盘崩 - 可以在计划生成后加一个"计划审查"步骤(类似 Reflection 审视计划)

❌ 反模式 3:规划粒度过粗或过细

# 坏:粒度过粗(计划没分解,等于没规划)
plan.steps = ["1. 完成调研报告"]  # 只有一步,等于没规划

# 坏:粒度过细(每行代码都是一步,等于没抽象)
plan.steps = [
    "1. 打开浏览器",
    "2. 输入 URL",
    "3. 按回车",
    "4. 等待加载",
    "5. 读取内容",
    # ... 50 步 ...
]

后果: - 过粗:失去规划的意义(还是要临场决策) - 过细:规划阶段开销巨大,执行阶段变成机械转发

正确:每步应该是一个有意义的子任务(能独立产出结果),3-6 步为宜。

❌ 反模式 4:反思阶段不给具体批评维度

# 坏:反思 prompt 太模糊
REFLECTION_PROMPT = "请看看这个答案好不好,哪里需要改进?"
# 模型可能回复:"总体不错,可以再详细一点"  # 没用的废话

后果:模型泛泛而谈,反思流于形式,改进版和初版几乎一样。

正确:给出具体的批评维度(完整性/准确性/结构),强制模型聚焦。

❌ 反模式 5:无限反思(过度打磨)

# 坏:不断反思直到"完美"
while not is_perfect(draft):
    critique = reflect(draft)
    draft = revise(draft, critique)
# 永远不会"完美",陷入无限循环

后果:延迟无限增长,成本爆炸,边际收益趋零。

正确固定反思轮数(通常 1 轮足够,最多 2 轮)。第 2 轮后的改进通常不值得额外成本。

常见错误

概念懂了,实际写代码还是会踩坑。这些是初学者最常犯的错误。

错误 症状 解决
response_format 忘了设 json_object 模型输出自由文本 "1. 查定义\n2. 查应用"json.loads() 报错 JSONDecodeError 必须传 response_format={"type": "json_object"},并用 Pydantic 校验
计划步骤写了"研究一下"这种模糊描述 Worker 拿到后不知道具体干什么,输出质量差 在 PLAN_SYSTEM_PROMPT 里约束"每步必须具体、可执行",并给 Few-shot 示例
反思 prompt 没给批评维度 模型回复"总体不错,可以更详细",改进版和初版几乎一样 列出具体维度(完整性/准确性/结构),强制模型聚焦
Reflection 跑了 5 轮还没停 延迟 5 倍,成本 5 倍,改进微乎其微 固定 1-2 轮,用 for _ in range(2) 而非 while True

8. 完整流程图(Plan-and-Execute + Reflection 写调研报告)

用户任务:写一篇 AI Agent 调研报告

Phase 1 (Plan):
  LLM 分解 → {"steps": [
    "1. 检索 AI Agent 定义",
    "2. 检索 AI Agent 应用场景",
    "3. 检索主流 AI Agent 框架",
    "4. 综合撰写报告"
  ]}

Phase 2 (Execute):
  Step 1: search_wiki("AI Agent 定义") → "AI Agent 是能感知..."
  Step 2: search_wiki("AI Agent 应用") → "应用于客服、编程..."
  Step 3: search_wiki("AI Agent 框架") → "LangChain、AutoGPT..."
  (每步结果累积到 results)

Phase 3 (Synthesize):
  LLM 汇总 → draft(初版报告)

Phase 4 (Reflection):
  LLM 审视 draft → critique("缺少发展趋势"、"结构可优化")

Phase 5 (Revise):
  LLM 根据 critique 改进 → final(最终报告)

运行示例

# Python
cd ai-agent/08-planning
python3 python/main.py

# TypeScript
cd ai-agent/08-planning
npx tsx typescript/main.ts

代码会先用真实 API 尝试(占位符密钥会失败),然后自动降级为离线 mock 演示,100% 可靠地展示:

  1. Plan-and-ExecuteOUT:plan: / OUT:execute:step{N}: / OUT:synthesize:):完整的三阶段流程
  2. ReflectionOUT:reflect:draft: / OUT:reflect:critique: / OUT:reflect:revised:):三轮反思改进

兼容性注意

  • .env 是占位符密钥OPENAI_API_KEY=sk-REPLACE-ME)→ 真实 API 调用会 401。 代码捕获错误并降级为离线 mock,依然展示完整的规划逻辑。
  • Plan-and-Execute 的规划阶段用 response_format=json_object——所有 OpenAI 兼容提供商都支持(第02章验证过)。
  • 离线 mock 设计:预设步骤列表 + mock 执行结果 + 预设反思轨迹,不依赖真实 API。

下一步

本章你让「任务助手 Agent」获得了战略眼光——它会先规划全局、再分步执行、还能反思改进。

但这只是"自己跟自己较劲"。真正的复杂任务需要多个 Agent 分工协作——一个负责规划、一个负责执行、一个负责审查。

第10章「多 Agent 编排」会解决这个问题:让多个 Agent 像一个团队一样协作。本章的 Plan-and-Execute 和 Reflection 是多 Agent 协作的基础——理解了"一个 Agent 怎么规划和反思",才能理解"多个 Agent 怎么分工和互审"。

💡 规划是 Agent 的"大脑前额叶":人类的前额叶负责计划、决策、自我控制。Plan-and-Execute + Reflection 给 Agent 装上了一个"数字前额叶"。但要处理真正复杂的任务,还需要"多个大脑"协作——那是第10章的主题。


代码

阅读更多

AI Agent 进阶教程 — 从零到生产(Python + TypeScript)

一句话:从最原始的 API 调用出发,自己从零造一个 mini Agent 框架, 再切换到现代框架——学完后,无论流行什么框架你都能快速上手,知识不过时。 全程 Python + TypeScript 双语言并列,OpenAI 兼容接口可一键切换提供商。 为什么有这个教程 市面上的 Agent 教程大多有两个毛病: "先工具后原理":上来就教你调 LangChain 的 AgentExecutor,一行代码跑起来很爽, 但出 bug 时完全不知道哪里错了。框架一旦过时,你的知识也跟着报废。 玩具示例:清一色 "Hello World Agent",看完不会写真实的 Agent。 本教程反其道而行: 🧭 先原理后工具 先看清原理,再选择工具。 原理是不变的,工具是流动的。

作者:zhangsheng

第01章 · LLM 基础 —— 与模型对话的第一步

目标:理解 LLM API 的核心概念,能用 Python/TypeScript 发出第一个请求并读懂返回值。 这是整个教程的地基——后续 16 章的 Agent 循环、工具调用、记忆系统全都建立在此之上。 TL;DR 30 秒速读:LLM API 就是一个 HTTP 接口,你发 messages 数组,它返回补全文本,每次调用完全独立,模型不会记住任何历史。 如果只记一件事:messages 数组里的 role(system/user/assistant)决定了"谁在说话",system prompt 是你控制模型行为的最强杠杆。 1. LLM API 是什么?

作者:zhangsheng

第02章 Prompt 工程 — 让 LLM 精准听话

本章是「任务助手 Agent」的第一次能力升级:从"随便聊聊"变成"精准执行任务"。 我们会用原生字符串模板(f-string / 模板字符串)手写 4 种核心 Prompt 技术, 不依赖任何第三方抽象库。 TL;DR 30 秒速读:Prompt 工程的四种核心技术是 System Prompt(定身份)、Few-shot(给示例教模式)、Chain-of-Thought(引导逐步推理)、结构化输出(强制 JSON),它们是所有 Agent 指令设计的基础。 如果只记一件事:system prompt 的优先级高于 user message,

作者:zhangsheng

第03章 工具调用(Tool Use / Function Calling)

「任务助手 Agent」获得了"手"——它能调用工具查天气、做计算、搜百科,不再只是空谈。 TL;DR 30 秒速读:LLM 本身不能执行任何代码,所谓"工具调用"是模型输出一段 JSON 指令,由你的客户端解析并执行,再把结果反馈给模型。 如果只记一件事:工具的 description 写得越清晰,模型选择工具越准确;写"处理数据"不如写"查询指定城市的当前天气,返回温度和天气状况"。 本章目标 学完本章,你将理解: 工具调用的本质:LLM 不直接执行工具,而是输出结构化指令(JSON),由客户端执行 JSON Schema

作者:zhangsheng