【从0搭建AI智能体·8】ReAct vs Plan-and-Execute:两种 Agent 编排模式对比
【从0搭建AI智能体·8】ReAct vs Plan-and-Execute:两种 Agent 编排模式对比
📚 本文是《从 0 搭建你的 AI 智能体》专栏第 8 篇。
上一篇:《给 Agent 接搜索:实时联网问答》 | 专栏目录:点此查看全部标签建议:
AI AgentReActPlan-and-Execute智能体编排大模型LLM
📌 前言:从「一问一答」到「自己想办法」
到目前为止,我们的 Agent 还停留在「反应式」——你问一句,它调个工具,答一句。但真正的智能体,要能应对复杂的多步骤任务:
「帮我查一下北京和上海这周末的天气,如果都是晴天,就推荐两个城市各自适合的户外活动,并对比哪个更适合带孩子。」
这个任务需要:查两次天气 → 判断天气 → 分别推荐活动 → 综合对比。模型不能一步到位,它得自己规划步骤、逐步执行、根据中间结果调整。
怎么让模型学会「规划」?业界有两大主流编排模式:ReAct(边想边做)和 Plan-and-Execute(先规划再执行)。它们是构建复杂 Agent 的两种基本「思维骨架」。这一篇把它们讲透、对比清楚、各给一份可跑代码,让你知道什么任务该用哪种。
💡 本文适合谁:已掌握 Function Calling、想让 Agent 处理复杂多步任务的开发者。阅读约 14 分钟。
目录
- 两种模式的核心区别(一图看懂)
- 模式一:ReAct(推理-行动循环)
- ReAct 代码实现
- 模式二:Plan-and-Execute(先规划后执行)
- Plan-and-Execute 代码实现
- 关键对比:什么任务用哪个
- 两者结合:Plan + ReAct 混合模式
- 常见坑与工程建议
- FAQ
- 总结
① 两种模式的核心区别(一图看懂)
一句话概括:ReAct 是「走一步看一步」,Plan-and-Execute 是「先画好地图再上路」。
【ReAct:边想边做】
想 → 做 → 看结果 → 再想 → 再做 → 看结果 → ... → 答
(每一步都根据上一步的结果,临时决定下一步)
【Plan-and-Execute:先规划后执行】
┌─ 规划:一次性列出所有步骤 [1,2,3,4]
└─ 执行:按计划逐步做 1→2→3→4 → 汇总答
(先想清楚全部步骤,再依次执行)
- ReAct 灵活,能随机应变,但可能「绕路」、步数不可控;
- Plan-and-Execute 有全局规划、步骤清晰,但计划一旦定死,遇到意外不好调整。
理解了这个根本区别,下面的代码和选型就都好懂了。
② 模式一:ReAct(推理-行动循环)
ReAct = Reasoning(推理)+ Acting(行动)。它把「思考」和「行动」交织成一个循环:
Thought(想):我需要先知道北京的天气
Action(做):调用 get_weather("北京")
Observation(看):北京晴,25℃
Thought(想):北京是晴天,再查上海
Action(做):调用 get_weather("上海")
Observation(看):上海多云
Thought(想):信息够了,可以回答了
Answer:……
其实你在第 3 篇写的 run_agent_loop(带 max_steps 的工具调用循环),就是 ReAct 的最简实现——每轮让模型看着已有结果决定下一步,直到它给出最终答案。现代大模型的 Function Calling 已经内建了这套「推理-行动」能力,你不用手写 Thought/Action 的解析。
③ ReAct 代码实现
基于 Function Calling 的 ReAct 循环(复用第 3 篇的骨架):
先准备好工具(沿用第 3 篇的天气 + 计算工具,这里给出完整可跑的精简版):
import os, json
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
client = OpenAI(api_key=os.getenv("AGENT_KEY"), base_url=os.getenv("API_BASE_URL"))
MODEL = "standard-agent-v1"
def get_weather(city):
return {"北京": "晴,30℃", "上海": "多云,26℃", "广州": "雷阵雨,31℃"}.get(city, f"暂无{city}数据")
def calculate(expr):
import ast, operator
ops = {ast.Add: operator.add, ast.Sub: operator.sub,
ast.Mult: operator.mul, ast.Div: operator.truediv, ast.USub: operator.neg}
def ev(n):
if isinstance(n, ast.Constant): return n.value
if isinstance(n, ast.BinOp): return ops[type(n.op)](ev(n.left), ev(n.right))
if isinstance(n, ast.UnaryOp): return ops[type(n.op)](ev(n.operand))
raise ValueError("不支持")
try: return str(ev(ast.parse(expr, mode="eval").body))
except Exception as e: return f"计算错误:{e}"
FUNCS = {"get_weather": get_weather, "calculate": calculate}
TOOLS = [
{"type": "function", "function": {"name": "get_weather",
"description": "查城市天气,用户问天气/气温时用",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}},
{"type": "function", "function": {"name": "calculate",
"description": "算数学表达式,需精确计算时用",
"parameters": {"type": "object", "properties": {"expr": {"type": "string"}}, "required": ["expr"]}}},
]
有了工具,ReAct 循环就是让模型「走一步看一步」:
def react_agent(question, max_steps=8):
messages = [
{"role": "system", "content":
"你是一个会逐步推理的助手。面对复杂问题,先思考需要哪些信息,"
"逐个调用工具获取,再综合得出答案。一步步来,不要急于下结论。"},
{"role": "user", "content": question},
]
for step in range(max_steps):
msg = client.chat.completions.create(
model=MODEL, messages=messages, tools=TOOLS, tool_choice="auto"
).choices[0].message
if not msg.tool_calls: # 模型认为信息够了,给出最终答案
return msg.content
messages.append(msg)
for tc in msg.tool_calls: # 执行本轮要求的工具(可能多个)
args = json.loads(tc.function.arguments)
result = FUNCS[tc.function.name](**args)
print(f"[Step {step+1}] {tc.function.name}({args}) -> {result}")
messages.append({"role": "tool", "tool_call_id": tc.id, "content": str(result)})
return "已达最大步数,任务可能过于复杂。"
特点:整个过程模型「走一步看一步」,每次都基于最新的 Observation 决定下一步。灵活,适合步骤数不确定、需要随机应变的任务。
⚠️ ReAct 的软肋:容易「绕路」或「钻牛角尖」。模型可能反复调用无用的工具、或在错误方向上越走越远。
max_steps是必须的刹车。
④ 模式二:Plan-and-Execute(先规划后执行)
Plan-and-Execute 把任务分成两个明确阶段:
- Plan(规划):让模型先把任务拆解成一个有序的步骤清单,不执行;
- Execute(执行):按清单逐步执行,每步可调工具,最后汇总。
好比出差前先列好行程表(订票→酒店→会议→返程),再照着一项项办,而不是走一步想一步。
用户任务
│
▼
[规划阶段] 模型输出:
1. 查北京天气
2. 查上海天气
3. 判断是否都晴天
4. 分别推荐活动并对比
│
▼
[执行阶段] 按 1→2→3→4 逐步做,每步结果传给下一步
│
▼
汇总所有步骤结果 → 最终答案
⑤ Plan-and-Execute 代码实现
分两段:先让模型规划出 JSON 步骤清单,再逐步执行。
def make_plan(question):
"""规划阶段:让模型输出步骤清单"""
resp = client.chat.completions.create(
model=MODEL, temperature=0.2,
messages=[
{"role": "system", "content":
"你是任务规划器。把用户任务拆解成有序的执行步骤,"
"只输出 JSON 数组,每个元素是一个步骤的描述字符串,不要其他文字。"
'例如:["查询北京天气", "查询上海天气", "对比并推荐"]'},
{"role": "user", "content": question},
],
)
content = resp.choices[0].message.content.strip()
# 容错:剥掉可能的代码块标记
content = content.replace("```json", "").replace("```", "").strip()
return json.loads(content)
def execute_plan(question, steps):
"""执行阶段:带着计划和已完成结果,逐步执行"""
done = [] # 累积已完成步骤的结果
for i, step in enumerate(steps):
context = "\n".join(f"步骤{j+1}({s}): {r}" for j, (s, r) in enumerate(done))
msg = client.chat.completions.create(
model=MODEL, tools=TOOLS, tool_choice="auto",
messages=[
{"role": "system", "content":
f"你在执行一个多步骤任务。总任务:{question}\n"
f"已完成步骤及结果:\n{context or '(无)'}\n"
f"现在只做当前这一步,可调用工具。"},
{"role": "user", "content": f"执行步骤:{step}"},
],
).choices[0].message
# 若这步要调工具,执行后再让模型给出该步结论
if msg.tool_calls:
tool_msgs = []
for tc in msg.tool_calls:
args = json.loads(tc.function.arguments)
result = FUNCS[tc.function.name](**args)
tool_msgs.append({"role": "tool", "tool_call_id": tc.id, "content": str(result)})
follow = client.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": f"步骤:{step}"}, msg, *tool_msgs],
).choices[0].message.content
done.append((step, follow))
else:
done.append((step, msg.content))
print(f"[执行完成 步骤{i+1}] {step}")
# 汇总所有步骤,生成最终答案
summary_ctx = "\n".join(f"{s}: {r}" for s, r in done)
final = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content": "根据各步骤结果,给用户一个完整、连贯的最终回答。"},
{"role": "user", "content": f"任务:{question}\n各步骤结果:\n{summary_ctx}"},
],
)
return final.choices[0].message.content
def plan_execute_agent(question):
steps = make_plan(question)
print("规划的步骤:", steps)
return execute_plan(question, steps)
特点:先有全局规划,执行路径清晰、可控、可展示给用户(进度条式体验),适合步骤明确的复杂任务。
5.1 两种模式跑同一个任务,看差异
用同一个复杂问题分别跑两种模式(配好第 ⑤ 节开头的工具和 .env 即可直接运行):
if __name__ == "__main__":
q = "北京和上海哪个更热?把更热那个城市的气温数字乘以 2 是多少?"
print("===== ReAct 模式 =====")
print(react_agent(q))
print("\n===== Plan-and-Execute 模式 =====")
print(plan_execute_agent(q))
ReAct 的运行轨迹(走一步看一步,过程"涌现"):
===== ReAct 模式 =====
[Step 1] get_weather({'city': '北京'}) -> 晴,30℃
[Step 2] get_weather({'city': '上海'}) -> 多云,26℃
[Step 3] calculate({'expr': '30*2'}) -> 60
北京更热(30℃ vs 26℃),30 乘以 2 等于 60。
Plan-and-Execute 的运行轨迹(先出计划,再按表执行):
===== Plan-and-Execute 模式 =====
规划的步骤: ['查询北京天气', '查询上海天气', '比较气温得出更热的城市', '将更热城市的气温乘以2']
[执行完成 步骤1] 查询北京天气
[执行完成 步骤2] 查询上海天气
[执行完成 步骤3] 比较气温得出更热的城市
[执行完成 步骤4] 将更热城市的气温乘以2
北京更热,气温 30℃,乘以 2 得 60。
对比很直观:ReAct 的步骤是"边想边冒出来"的,Plan 是"一开始就列全了"。后者能提前把"规划的步骤"展示给用户当进度条,前者更省一次规划调用。
⑥ 关键对比:什么任务用哪个
| 维度 | ReAct(边想边做) | Plan-and-Execute(先规划后执行) |
|---|---|---|
| 思路 | 走一步看一步 | 先画地图再上路 |
| 灵活性 | 高,能随机应变 | 低,计划定死后不易调整 |
| 可控性 | 步数不定,可能绕路 | 步骤清晰,路径可控 |
| 可展示 | 过程较乱 | 能展示「计划+进度」,体验好 |
| Token 成本 | 简单任务省 | 多一次规划调用,略贵 |
| 适合场景 | 探索性、步骤不定、需应变 | 步骤明确、流程化、要展示进度 |
| 典型例子 | 「查资料回答一个开放问题」 | 「按固定流程生成一份报告」 |
一句话选型:
- 任务开放、步骤数说不准 → ReAct;
- 任务能提前拆成清晰步骤、想给用户看进度 → Plan-and-Execute。
🎯 现实中,简单任务用 ReAct 就够了(现代模型的 Function Calling 天然就是 ReAct)。只有当任务复杂到「必须先想清楚全局」时,Plan-and-Execute 的规划优势才划算。别为了用而用。
⑦ 两者结合:Plan + ReAct 混合模式
生产级 Agent 常把两者结合:用 Plan 定大方向,每一步内部用 ReAct 灵活执行,兼顾全局规划和局部应变。
[Plan] 拆出大步骤:1.调研 2.分析 3.撰写
│
├─ 步骤1「调研」内部:ReAct 循环(搜多次、随机应变)
├─ 步骤2「分析」内部:ReAct 循环
└─ 步骤3「撰写」内部:ReAct 循环
│
▼
汇总
更进一步还有 Re-planning(重规划):执行中发现计划不对,回到规划阶段调整。这就是 LangGraph 等框架里「带状态、可回退」的 Agent 图的雏形——本质还是这两种模式的组合与增强。
💡 别被框架名词唬住:LangGraph、AutoGPT、BabyAGI 这些花哨的 Agent 框架,内核都逃不出「ReAct 循环 + Plan 规划 + 状态管理」这几样。理解了本篇,你再看它们就是「换了层包装」。
⑧ 常见坑与工程建议
| 坑 | 说明 | 建议 |
|---|---|---|
| 无限循环 | ReAct 反复调工具不收敛 | 必设 max_steps;超限优雅退出 |
| 规划解析失败 | Plan 输出的 JSON 不合法 | 剥代码块标记 + try/except,失败降级为 ReAct |
| 步骤间信息断层 | 执行时没把前序结果传给后续 | 累积 done 结果并注入上下文(见第⑤节) |
| 成本失控 | 多步骤 = 多次模型调用 | 记录每步 Token,设总预算上限(接第10篇可观测性) |
| 计划过度拆解 | 简单任务被拆成一堆碎步骤 | 规划 Prompt 里限制「最多 N 步」 |
🎯 一条实用工程建议:给 Plan 阶段加个「最多拆 5 步」的约束,能避免模型把「1+1」都拆成三步。规划要「够用就好」,不是越细越好。
⑨ FAQ
Q1:现代模型 Function Calling 已经能多步调用了,还需要手写 ReAct 吗?
基础多步调用不用手写(模型内建)。但当你需要掌控每一步(加校验、记录、人工介入)时,手写循环能给你控制权。
Q2:Plan-and-Execute 的计划一定要执行完吗?
不一定。可以加「提前终止」:某步发现已能回答,就跳过后续。也可加「重规划」应对意外。
Q3:两种模式对模型能力要求高吗?
Plan-and-Execute 对模型的「规划能力」要求更高,弱一点的模型可能拆解得不合理。ReAct 对单步判断要求高。用能力强的模型效果都更好。
Q4:怎么给用户展示 Agent 的执行过程?
Plan-and-Execute 天然适合:先展示计划,再逐步更新进度(配合第4篇流式输出,体验很好)。
Q5:任务失败了怎么办?
加重试和降级:某步失败重试 N 次;仍失败则如实告知用户「第 X 步未完成」,而非假装成功。
⑩ 总结
这一篇讲了让 Agent 处理复杂任务的两种「思维骨架」:
| 模式 | 一句话 | 何时用 |
|---|---|---|
| ReAct | 走一步看一步 | 开放、步骤不定、需应变 |
| Plan-and-Execute | 先规划后执行 | 步骤明确、要展示进度 |
| 混合 | Plan 定方向,ReAct 管执行 | 复杂生产任务 |
核心认知:Agent 的「智能」,很大程度来自编排——怎么把「思考」和「行动」组织起来。 模型是引擎,编排是变速箱,两者配合才能跑好复杂路况。
下一篇我们把编排推向多个 Agent:多智能体协作——让几个各有专长的 Agent 分工合作,完成单个 Agent 搞不定的大任务。
🔜 下一篇预告:《用 LangGraph / 自研搭多智能体协作》——多个 Agent 分工合作,1+1>2。
👍 如果本文帮到你,点赞 / 收藏 / 关注,追更不迷路。
更多推荐



所有评论(0)