在这里插入图片描述

为什么你的Agent总在"想"和"做"之间反复横跳?Plan-and-Execute与ReAct的本质差异,决定了你的AI是"战略家"还是"急先锋"——选错范式,Agent性能直接腰斩!


Plan-and-Execute vs ReAct
两种Agent范式的本质区别

一、范式初识
从人类思维说起

二、Plan-and-Execute
先谋后动的战略家

三、ReAct
边想边做的急先锋

四、核心差异对比
四维拆解法

五、实战选型指南
什么场景用什么

六、代码层面的
关键实现差异

七、混合范式
未来的演进方向

人类做事的
两种模式

规划阶段
拆解复杂任务

执行阶段
按计划行事

反思阶段
动态调整

推理与行动
交错进行

观察反馈
即时修正

思维模式

Token消耗

错误恢复

可解释性

适合Plan-and
-Execute的场景

适合ReAct
的场景

状态管理
差异

Prompt设计
差异

ReWOO等
混合方案

目录

  • 一、范式初识:从人类思维说起
  • 二、Plan-and-Execute:先谋后动的战略家
  • 三、ReAct:边想边做的急先锋
  • 四、核心差异对比:四维拆解法
  • 五、实战选型指南:什么场景用什么
  • 六、代码层面的关键实现差异
  • 七、混合范式:未来的演进方向

嗨,大家好呀,我是你的老朋友精通代码大仙。接下来我们一起学习 《大模型应用开发_动手做AI_Agent》,震撼你的学习轨迹!


“磨刀不误砍柴工,但磨太久刀,柴都被别人砍完了。”

这句糙话,道尽了我们程序员做Agent时的纠结。你是不是也这样——刚开始学Agent开发,看到网上各种"智能体"Demo炫酷得不行,自己动手一写,要么Agent想太多不动手,要么动手了才发现路走偏了?

第7章我们进入Agent实战的核心地带:智能调度。而调度的灵魂,就在于你选什么范式(Paradigm)。Plan-and-Execute和ReAct,这两个名字你肯定听过,但它们的本质区别到底是什么?为什么同样的任务,换种范式效果天差地别?

今天这篇,我们就把这层窗户纸捅破。选错范式,你的Agent可能在高并发场景下Token账单爆炸;选对范式,复杂任务也能行云流水。这不是玄学,是工程决策。


一、范式初识:从人类思维说起

点题:两种范式对应人类的两种做事风格

咱们先不搞那些学术定义,聊聊生活。

你周末要搞个大扫除,有两种搞法:

第一种:先拿张纸,把"擦窗户、拖地、整理衣柜、洗床单"列个顺序,估计每项多久,然后按计划一项项做完。这叫Plan-and-Execute

第二种:拿起抹布就开始擦,擦着擦着发现窗户太脏得先喷清洁剂,喷完等的时候顺手把地扫了,扫地发现拖把坏了又下单买新的……边干边调整。这叫ReAct(Reasoning + Acting)。

两种都能完成大扫除,但心态完全不同,适用场景也完全不同

Agent的这两种范式,正是对人类这两种思维模式的建模。

ReAct

思考 Thought

行动 Action

观察 Observation

Plan-and-Execute

规划阶段
分解任务

执行阶段
按步骤执行

可选反思
重新规划

痛点分析:新手最容易犯的"范式混淆症"

我见过太多同学,代码一跑起来就蒙圈:

“我用ReAct做数据分析,让它查三个表做关联,结果它查完第一个表就开始瞎分析,后面两个表完全忘了查……”

这就是典型的范式错配。ReAct的"边想边做"模式,天生不适合需要前置全局信息的任务。它的工作记忆(context window)在反复推理中被消耗,容易"顾头不顾尾"。

另一个极端:

“我用Plan-and-Execute做客服机器人,用户问’你们支持退款吗’,它非要先规划:‘步骤1,理解问题;步骤2,查询政策;步骤3,组织语言……’,用户等了3秒才收到回复。”

简单查询类任务,Plan-and-Execute的"仪式感"反而成了累赘。

解决方案:先理解本质,再谈选型

记住这个口诀:

复杂任务看全局,用Plan;简单任务要响应,用ReAct;不确定时,先ReAct探路,复杂子任务再Plan。

具体判断标准:

  • 任务步骤是否可预先确定?是→Plan,否→ReAct
  • 执行过程中是否需要频繁外部反馈?是→ReAct,否→Plan
  • 延迟敏感还是质量敏感?延迟敏感→ReAct,质量敏感→Plan

小结

范式不是"哪个更好",是"哪个更合适"。理解人类思维的两种模式,是选对范式的第一步。


二、Plan-and-Execute:先谋后动的战略家

点题:三层架构,环环相扣

Plan-and-Execute,直译"规划-执行",但它的完整形态其实是Plan-Execute-Reflect三层循环。

用户输入

Planner
规划器

计划
Plan

Executor
执行器

执行结果

需要
重新规划?

最终结果

Planner(规划器):大模型的"慢思考"模式,把复杂任务拆解为可执行的子任务序列。这里的输出是结构化的计划,不是自然语言。

Executor(执行器):可以是另一个大模型(专门做执行),也可以是确定性代码(调用API、查数据库)。关键是按计划执行,不擅自发挥

Re-Planner(重规划器):当执行结果与预期不符时,触发重新规划。这是容错机制,也是Plan-and-Execute能处理动态环境的关键。

痛点分析:规划阶段的"过度自信"与"规划失效"

新手写Planner,最容易踩两个坑。

坑一:过度规划

# 错误的Planner输出示例
plan = """
1. 理解用户意图
2. 分析情感倾向
3. 识别关键实体
4. 查询知识库
5. 生成候选回复1
6. 生成候选回复2
7. 评估回复质量
8. 选择最优回复
9. 格式化输出
"""

一个"查天气"的请求,规划了9步。Planner把简单问题复杂化,执行器跟着受罪,Token疯狂燃烧。

坑二:计划与执行脱节

# Planner生成的计划
plan = ["搜索2024年Python教程", "筛选评分>4.5的", "按发布时间排序"]

# 但Executor的可用工具只有
tools = ["web_search", "calculator"]  # 没有筛选和排序工具!

计划里假设的能力,执行器根本没有。结果执行到一半报错,或者执行器"自由发挥"偏离计划。

解决方案:让规划"可执行、可验证、可回滚"

可执行:Planner必须知道Executor有什么工具。用**工具描述(Tool Description)**约束规划输出。

# 正确的做法:Planner接收工具列表
available_tools = [
    {"name": "web_search", "description": "搜索网络信息", "params": {"query": "string"}},
    {"name": "read_file", "description": "读取本地文件", "params": {"path": "string"}}
]

# Planner输出必须匹配工具名和参数
plan = [
    {"step": 1, "tool": "web_search", "params": {"query": "2024年Python教程"}},
    {"step": 2, "tool": "read_file", "params": {"path": "/tmp/search_result.txt"}}
]

可验证:每个子任务要有明确的完成标准(Done Criteria)。

plan = [
    {
        "step": 1, 
        "task": "获取北京天气",
        "tool": "weather_api",
        "params": {"city": "北京"},
        "done_criteria": "返回结果包含temperature和condition字段"
    }
]

可回滚:执行失败时,能定位到具体步骤,选择重试、跳过或重新规划。

class PlanExecutor:
    def execute(self, plan):
        for i, step in enumerate(plan):
            try:
                result = self.execute_step(step)
                self.checkpoint.save(i, result)  # 保存检查点
            except Exception as e:
                # 三种策略
                return self.retry(step)      # 重试
                # return self.skip(step)     # 跳过
                # return self.replan(plan, i, e)  # 从第i步重新规划

小结

Plan-and-Execute的精髓是**“谋定而后动,动则有据,败则可返”**。规划器是大脑,执行器是手脚,检查点是安全绳。


三、ReAct:边想边做的急先锋

点题:Thought-Action-Observation循环

ReAct的论文标题很直白:《ReAct: Synergizing Reasoning and Acting in Language Models》。推理与行动的协同,是它的核心。

用户输入

Thought
我需要搜索...

Action
调用搜索工具

Observation
搜索结果:...

任务完成?

Final Answer

这个循环的关键在于:每一步都基于最新信息做决策。没有预设的计划,只有"当下最优"的选择。

典型的ReAct轨迹长这样:

Question: 2024年诺贝尔物理学奖得主是谁?他们因什么获奖?

Thought 1: 我需要搜索2024年诺贝尔物理学奖的最新信息。
Action 1: Search["2024年诺贝尔物理学奖得主"]
Observation 1: 2024年诺贝尔物理学奖授予John J. Hopfield和Geoffrey E. Hinton...

Thought 2: 我已经知道得主名字,但需要确认他们的具体贡献。
Action 2: Search["Hopfield Hinton 诺贝尔物理学奖 获奖原因"]
Observation 2: 他们因"基础性发现和发明,使使用人工神经网络实现机器学习成为可能"而获奖。

Thought 3: 我现在有足够信息回答用户了。
Final Answer: 2024年诺贝尔物理学奖授予...

痛点分析:陷入"思考漩涡"与"行动瘫痪"

ReAct的灵活性是双刃剑,新手常遇到两种故障模式。

故障一:过度思考(Over-thinking)

Thought 1: 用户问的是北京天气,我需要确认"北京"是指中国首都还是其他同名地点。
Thought 2: 如果是中国首都,我需要调用天气API;如果是其他地点,可能需要澄清。
Thought 3: 但考虑到用户用中文提问,大概率指中国首都。
Thought 4: 不过为了准确,我应该先确认地点...
Thought 5: 但确认会增加交互轮次,影响体验...
...(无限循环)

模型在"想"上消耗大量Token,迟迟不行动。这在GPT-4等强推理模型上尤其明显——它们太"谨慎"了。

故障二:短视行动(Short-sighted Acting)

Thought 1: 用户要比较iPhone 15和华为Mate 60。
Action 1: Search["iPhone 15 参数"]
Observation 1: [iPhone 15详细参数...]

Thought 2: 我已经拿到iPhone 15信息,可以回复用户了。
Final Answer: iPhone 15的主要参数是...

完全忘了还要查华为Mate 60!ReAct没有全局计划,容易"见树不见林"。

解决方案:用结构约束和记忆增强驯服ReAct

结构约束:强制输出格式

REACT_PROMPT = """你必须按以下格式响应:

Thought: [你的推理过程,必须包含:1)当前已知信息 2)还缺什么信息 3)下一步行动理由]
Action: [工具名,必须是以下之一: Search, Calculator, GetWeather]
Action Input: [工具参数]

如果信息已足够:
Final Answer: [直接回答用户]

规则:
- Thought必须分析"是否已收集全部必要信息"
- 比较类问题必须收集全部比较对象的信息后才能回答
- 禁止在信息不全时输出Final Answer
"""

记忆增强:维护关键状态

class ReActAgent:
    def __init__(self):
        self.collected_info = {}  # 已收集的信息
        self.pending_goals = []   # 待完成的目标
        
    def run(self, query):
        # 初始化目标
        self.pending_goals = self.extract_goals(query)  # ["查iPhone15", "查Mate60", "比较"]
        
        while self.pending_goals:
            thought = self.llm.generate_thought(
                query=query,
                collected=self.collected_info,
                pending=self.pending_goals
            )
            action, action_input = self.parse_action(thought)
            obs = self.execute(action, action_input)
            
            # 更新状态和检查目标完成
            self.collected_info.update(self.extract_info(obs))
            self.pending_goals = self.update_goals(self.pending_goals, self.collected_info)
            
        return self.synthesize_answer(self.collected_info)

截断保护:防止无限循环

def run_with_limits(self, query, max_steps=10, max_tokens=4000):
    for step in range(max_steps):
        if self.total_tokens > max_tokens:
            return self.fallback_response("思考过长,请简化问题")
        # ...正常执行

小结

ReAct是"敏捷开发"思维:快速迭代,小步快跑。但需要结构约束防止发散,记忆机制弥补短视,熔断机制防止失控


四、核心差异对比:四维拆解法

点题:从四个维度看透本质

维度 Plan-and-Execute ReAct
思维模式 瀑布式:先全局规划,再分步执行 敏捷式:边执行边调整
Token消耗 规划阶段集中消耗,执行阶段可控 持续消耗,随步数线性增长
错误恢复 检查点回滚,重新规划 下一步即时修正,无全局回滚
可解释性 计划即文档,执行轨迹清晰 需追溯思考链,理解成本较高
50% 25% 15% 10% "Token消耗分布对比(典型10步任务)" Plan-and-Execute: 规划30% [30] Plan-and-Execute: 执行20% [20] Plan-and-Execute: 预留50% [50] ReAct: 思考链累积 [100]

痛点分析:只看表面差异,忽视深层trade-off

很多同学的对比停留在"一个有计划一个没计划",导致选型时只看任务复杂度,忽略了其他关键因素。

真实案例:客服系统的选型灾难

某团队做智能客服,选了Plan-and-Execute,理由是"用户问题可能涉及多轮查询,需要规划"。结果上线后发现:

  • 80%的查询是单轮问答(“你们营业时间?”“怎么退款?”)
  • Plan的 overhead 让平均响应延迟从1.5秒变成4秒
  • 用户投诉"机器人反应慢"

他们只看了"最复杂的情况",没看分布

另一个案例:数据分析Agent用ReAct,用户问"对比我司和竞品过去三年的营收、利润、增长率"。

  • ReAct查完2021年营收就开始分析,忘了还有2022、2023
  • 或者查完营收忘了查利润
  • 三轮对话后context window爆炸,早期信息丢失

他们只看了"灵活性",没看信息完整性需求

解决方案:建立多维决策矩阵

def select_paradigm(task_profile):
    """
    任务画像决定范式选择
    """
    score_plan = 0
    score_react = 0
    
    # 维度1:步骤可预测性
    if task_profile["steps_predictable"]:
        score_plan += 2
    else:
        score_react += 2
    
    # 维度2:信息依赖模式
    if task_profile["needs_global_info"]:  # 需要全局信息才能决策
        score_plan += 2
    if task_profile["feedback_driven"]:    # 强依赖外部反馈
        score_react += 2
    
    # 维度3:延迟敏感度
    if task_profile["latency_sensitive"]:
        score_react += 1  # ReAct可以早返回,但注意是"可以"不是"一定"
    
    # 维度4:容错成本
    if task_profile["error_cost_high"]:    # 错误代价高(如医疗、金融)
        score_plan += 2  # Plan的可回滚更有价值
    
    # 维度5:任务长度
    if task_profile["expected_steps"] > 10:
        score_plan += 1  # ReAct长链条容易失控
    
    return "Plan-and-Execute" if score_plan >= score_react else "ReAct"

实际决策时,还要考虑混合策略

# 外层用ReAct快速响应,复杂子任务用Plan-and-Execute
def hybrid_agent(user_query):
    # 第一步:ReAct判断意图和复杂度
    intent_analysis = react_classifier.classify(user_query)
    
    if intent_analysis["complexity"] == "simple":
        return react_agent.answer(user_query)  # 简单查询,ReAct直接答
    
    elif intent_analysis["complexity"] == "complex":
        # 复杂任务,提取子任务用Plan-and-Execute
        subtasks = intent_analysis["subtasks"]
        return plan_execute_agent.run(subtasks)
    
    else:
        # 不确定,先ReAct探路,必要时切换
        return adaptive_agent.run(user_query)

小结

没有银弹。Plan-and-Execute是"重型武器",ReAct是"瑞士军刀"。理解trade-off,才能因时制宜。


五、实战选型指南:什么场景用什么

点题:五类典型场景的范式匹配

场景类型 推荐范式 关键原因
数据分析报告生成 Plan-and-Execute 多数据源关联,需要全局schema理解
实时客服对话 ReAct 低延迟,对话流不可预测
代码生成与调试 混合:Plan定框架,ReAct填细节 架构可规划,实现细节需迭代
科学研究辅助 Plan-and-Execute 假设-验证的严谨性要求
游戏NPC行为 ReAct 环境动态变化,需要即时反应

痛点分析:教条主义选型

反模式一:盲目追新

“ReAct是论文新提出的,肯定更先进,都用ReAct!”

ReAct论文是2022年的,Plan-and-Execute的思想在2023年的LLMCompiler、2024年的各种Agent框架中持续演进。范式没有代差,只有适配度差异

反模式二:一刀切

“我们系统统一用Plan-and-Execute,规范好管理。”

结果简单查询也要等规划,用户体验崩盘。或者"全用ReAct",复杂报表生成经常漏步骤。

解决方案:场景化决策流程图

接到任务

步骤是否
可预先确定?

是否需要
严格顺序执行?

ReAct

错误代价
是否高昂?

ReAct或
轻量Plan

Plan-and-Execute
+ 严格验证

Plan-and-Execute
标准版

延迟要求
是否苛刻?

具体场景深度解析

场景1:SQL生成(推荐Plan-and-Execute)

# 错误做法:ReAct直接生成
# 容易漏JOIN条件,或者WHERE子句不全

# 正确做法:Plan-and-Execute
plan = [
    {"step": "schema_linking", "task": "识别相关表和字段"},
    {"step": "condition_extraction", "task": "提取所有过滤条件"},
    {"step": "sql_draft", "task": "生成草稿SQL"},
    {"step": "validation", "task": "检查语法和语义"},
    {"step": "execution", "task": "执行并返回结果"}
]
# 每一步可验证,出错可定位

场景2:工具调用链(推荐ReAct)

# 场景:根据用户描述自动选择并组合工具
# "帮我找北京明天适合户外活动的地点,要人少"

# ReAct的灵活优势
Thought 1: 需要天气信息和地点推荐,先查天气
Action 1: GetWeather(city="北京", date="明天")
Observation 1: 晴天,25度,适合户外

Thought 2: 天气好,现在找人少的地方。需要调用地图API
Action 2: SearchPlaces(city="北京", type="户外", crowd_level="low")
...
# 工具组合不可预先确定,ReAct更合适

场景3:长文档生成(推荐混合范式)

# 先生成大纲(Plan),再逐节写作(ReAct迭代)

# Phase 1: Plan
outline = planner.generate_outline(
    topic="2024年AI行业发展报告",
    sections=["技术突破", "商业应用", "政策监管", "未来趋势"]
)

# Phase 2: 每节用ReAct写作,但受大纲约束
for section in outline.sections:
    content = react_writer.write(
        section=section,
        constraints=f"必须覆盖大纲要点: {section.key_points}",
        max_iterations=5
    )
    # 验证是否覆盖要点,不足则补充

小结

选型不是一次决策,是持续调优。从场景出发,用数据验证,比教条更有价值。


六、代码层面的关键实现差异

点题:状态管理与Prompt设计的分水岭

Plan-and-Execute和ReAct在代码实现上,最核心的差异体现在状态管理Prompt设计两个层面。

痛点分析:把两种范式写成一种代码

我见过最离谱的代码,是用ReAct的循环结构硬套Plan-and-Execute:

# 反模式:假Plan真ReAct
def fake_plan_agent(query):
    # 号称是Plan-and-Execute,实际没有分离规划和执行
    plan = llm.generate(f"请规划如何回答: {query}")  # 生成文本计划,不结构化
    
    for step in plan.split("\n"):  # 按行解析,脆弱
        thought = f"现在执行: {step}"
        action = llm.generate(f"{thought}\n请决定行动")  # 又变成ReAct式的即时决策
        # ...混乱不堪

这种代码既没拿到Plan的全局性,又丢了ReAct的灵活性,两头不靠。

解决方案:两种范式的标准实现模板

Plan-and-Execute标准模板

from dataclasses import dataclass
from typing import List, Dict, Any, Callable
from enum import Enum

class StepStatus(Enum):
    PENDING = "pending"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"

@dataclass
class PlanStep:
    id: str
    description: str
    tool: str
    parameters: Dict[str, Any]
    dependencies: List[str]  # 依赖的其他步骤ID
    status: StepStatus = StepStatus.PENDING
    result: Any = None

class PlanAndExecuteAgent:
    def __init__(self, tools: Dict[str, Callable], llm):
        self.tools = tools
        self.llm = llm
        self.plan_history: List[List[PlanStep]] = []  # 支持重规划的历史
        
    def planner(self, query: str, available_tools: List[str], 
                previous_failure: str = None) -> List[PlanStep]:
        """生成结构化计划,非自然语言"""
        
        prompt = f"""你是一个任务规划专家。将用户请求拆解为可执行步骤。

可用工具: {available_tools}
用户请求: {query}
{f'之前计划失败原因: {previous_failure}' if previous_failure else ''}

输出必须是JSON格式:
{{
    "steps": [
        {{
            "id": "step_1",
            "description": "步骤描述",
            "tool": "工具名(必须在可用工具列表中)",
            "parameters": {{"参数名": "参数值或占位符"}},
            "dependencies": []  # 依赖的步骤ID
        }}
    ]
}}

规则:
1. 工具名必须严格匹配可用工具列表
2. 有依赖关系的步骤,dependencies必须填写
3. 参数值如果是从前序步骤获取,用{{step_id.result.field}}标记
"""
        response = self.llm.generate(prompt)
        plan_data = json.loads(response)
        return [PlanStep(**step) for step in plan_data["steps"]]
    
    def executor(self, step: PlanStep, context: Dict[str, Any]) -> Any:
        """执行单步,纯执行无决策"""
        
        # 解析参数(处理依赖引用)
        resolved_params = self._resolve_params(step.parameters, context)
        
        # 调用工具
        tool = self.tools[step.tool]
        result = tool(**resolved_params)
        
        return result
    
    def run(self, query: str) -> Any:
        # 生成初始计划
        plan = self.planner(query, list(self.tools.keys()))
        self.plan_history.append(plan)
        
        # 拓扑排序,按依赖执行
        execution_order = self._topological_sort(plan)
        
        context = {}  # 步骤结果上下文
        
        for step_id in execution_order:
            step = next(s for s in plan if s.id == step_id)
            step.status = StepStatus.RUNNING
            
            try:
                result = self.executor(step, context)
                step.result = result
                step.status = StepStatus.COMPLETED
                context[step.id] = result
                
            except Exception as e:
                step.status = StepStatus.FAILED
                
                # 决策:重试、跳过、还是重新规划?
                recovery_action = self._decide_recovery(step, str(e), plan)
                
                if recovery_action == "replan":
                    # 重新规划,从当前失败步骤开始
                    new_plan = self.planner(
                        query, 
                        list(self.tools.keys()),
                        previous_failure=f"步骤{step.id}失败: {str(e)}"
                    )
                    # 合并已完成步骤和新计划
                    plan = self._merge_plans(plan, new_plan, step_id)
                    execution_order = self._topological_sort(plan)
                    
                elif recovery_action == "skip":
                    step.status = StepStatus.COMPLETED  # 标记为完成但结果为空
                    context[step.id] = None
                    
                else:  # retry
                    # 简单重试,实际可加入指数退避
                    result = self.executor(step, context)
                    step.result = result
                    step.status = StepStatus.COMPLETED
                    context[step.id] = result
        
        # 综合所有结果生成最终答案
        return self._synthesize(plan, context)

ReAct标准模板

from dataclasses import dataclass
from typing import Optional, Tuple

@dataclass
class ReActStep:
    thought: str
    action: Optional[str]
    action_input: Optional[str]
    observation: Optional[str]
    is_final: bool = False

class ReActAgent:
    def __init__(self, tools: Dict[str, Callable], llm, 
                 max_iterations: int = 10):
        self.tools = tools
        self.llm = llm
        self.max_iterations = max_iterations
        
        # ReAct的Prompt模板,严格约束格式
        self.prompt_template = """回答用户问题,通过思考-行动-观察的循环。

可用工具:
{tool_descriptions}

历史轨迹:
{trajectory}

规则:
1. 必须按格式输出,Thought和Action必须成对出现(Final Answer除外)
2. Thought必须分析当前已知信息和下一步计划
3. 不要重复已经执行过的行动
4. 如果信息足够,立即输出Final Answer

开始:
Question: {question}
"""
        
    def parse_output(self, text: str) -> Tuple[str, Optional[str], Optional[str]]:
        """严格解析Thought/Action/Observation"""
        
        thought = ""
        action = None
        action_input = None
        
        # 提取Thought
        if "Thought:" in text:
            thought = text.split("Thought:")[1].split("Action:")[0].strip()
        
        # 提取Action
        if "Action:" in text and "Final Answer:" not in text:
            action_part = text.split("Action:")[1].split("Action Input:")[0].strip()
            action = action_part
            
            # 提取Action Input
            if "Action Input:" in text:
                action_input = text.split("Action Input:")[1].split("Observation:")[0].strip()
        
        # 检查是否结束
        if "Final Answer:" in text:
            return text.split("Final Answer:")[1].strip(), None, None, True
            
        return thought, action, action_input, False
    
    def run(self, question: str) -> str:
        trajectory = []  # 完整历史
        iterations = 0
        
        while iterations < self.max_iterations:
            # 构建当前Prompt
            tool_descriptions = "\n".join([
                f"{name}: {func.__doc__}" 
                for name, func in self.tools.items()
            ])
            
            trajectory_text = "\n".join([
                f"Thought: {s.thought}\nAction: {s.action}\n"
                f"Action Input: {s.action_input}\nObservation: {s.observation}"
                for s in trajectory
            ])
            
            prompt = self.prompt_template.format(
                tool_descriptions=tool_descriptions,
                trajectory=trajectory_text,
                question=question
            )
            
            # 生成下一步
            response = self.llm.generate(prompt)
            
            thought, action, action_input, is_final = self.parse_output(response)
            
            if is_final:
                return thought  # Final Answer的内容
            
            # 执行行动
            if action and action in self.tools:
                try:
                    result = self.tools[action](action_input)
                    observation = str(result)
                except Exception as e:
                    observation = f"错误: {str(e)}"
            else:
                observation = f"错误: 未知工具 '{action}',可用工具: {list(self.tools.keys())}"
            
            # 记录步骤
            trajectory.append(ReActStep(
                thought=thought,
                action=action,
                action_input=action_input,
                observation=observation
            ))
            
            iterations += 1
            
            # 检查是否陷入循环(重复相同的thought/action)
            if self._is_looping(trajectory):
                return "抱歉,我似乎在循环思考,请简化问题或提供更多上下文。"
        
        return "达到最大迭代次数,未能完成回答。"
    
    def _is_looping(self, trajectory: List[ReActStep], window: int = 3) -> bool:
        """检测是否陷入循环"""
        if len(trajectory) < window * 2:
            return False
        
        recent = trajectory[-window:]
        previous = trajectory[-window*2:-window]
        
        # 比较thought和action的相似度
        recent_sig = [(s.thought[:50], s.action) for s in recent]
        previous_sig = [(s.thought[:50], s.action) for s in previous]
        
        return recent_sig == previous_sig

关键差异总结

方面 Plan-and-Execute ReAct
核心数据结构 计划图(DAG) 线性轨迹(List)
LLM调用模式 规划时一次/多次,执行时可能零次 每步都调用LLM
状态持久化 计划版本、执行检查点 轨迹历史
并发可能 无依赖步骤可并行 本质串行
调试难度 计划可见,易定位 需追溯思考链

小结

代码层面的差异,根源于思维模型的差异。Plan-and-Execute需要图结构处理依赖,ReAct需要严格的格式约束防止发散。


七、混合范式:未来的演进方向

点题:ReWOO、LLMCompiler等新一代方案

2023-2024年,学术界和工业界都在探索"取两者之长"的混合范式。

ReWOO(Reasoning WithOut Observation):把ReAct的"观察"成本降下来。先规划所有工具调用,然后批量执行,减少LLM调用次数。

LLMCompiler:明确分离规划、任务调度和执行。规划生成DAG,调度器优化并行,执行器高效运行。

Hierarchical Agents:高层用Plan做战略,低层用ReAct做战术。

简单

中等

复杂

用户请求

意图识别
ReAct快速分类

复杂度判断

轻量ReAct
直接响应

ReWOO
预规划+批量执行

分层规划
LLMCompiler

战略层Plan
分解里程碑

战术层ReAct
每个里程碑执行

是否触发
异常分支?

局部Replan
不影响主计划

继续执行

痛点分析:过早优化与架构复杂化

“听说LLMCompiler很先进,我们也上!”

结果团队花了两个月实现DAG调度、并行执行、依赖解析,却发现90%的任务是单步骤查询。复杂的架构反而降低了可维护性。

解决方案:渐进式演进路径

# 阶段1:从纯ReAct开始,快速验证
agent = ReActAgent(tools, llm)

# 阶段2:识别瓶颈,对复杂任务引入轻量Plan
def stage2_agent(query):
    if is_complex(query):
        # 简单规划:提取关键子目标
        subgoals = llm.extract_subgoals(query)
        return execute_with_checkpoints(subgoals)
    else:
        return react_agent.run(query)

# 阶段3:全功能Plan-and-Execute,支持重规划
agent = PlanAndExecuteAgent(tools, llm, enable_replan=True)

# 阶段4:混合调度,动态选择策略
agent = AdaptiveAgent([
    ("simple", ReActAgent(tools, llm)),
    ("complex", PlanAndExecuteAgent(tools, llm)),
    ("batch", ReWOOAgent(tools, llm))
])

小结

范式演进是需求驱动的,不是技术驱动。先跑起来,再测瓶颈,最后优化,永远是最佳路径。


写在最后

聊到这里,Plan-and-Execute和ReAct的本质区别,你应该有清晰的体感了。

Plan-and-Execute是战略思维:先看清全局,再步步为营,错了能回头。它适合那些"输不起"的场景——医疗诊断、金融决策、复杂数据分析。代价是前置的思考成本,和可能的过度规划。

ReAct是战术思维:快速响应,边打边学,灵活应变。它适合那些"等不起"的场景——实时对话、探索性任务、工具组合不确定。代价是可能短视,可能陷入循环,可能Token失控。

但最厉害的Agent开发者,不是"会选范式"的人,是能根据任务动态组合范式的人。简单查询用ReAct秒回,复杂分析用Plan-and-Execute保质量,批量任务用ReWOO省成本——这才是"智能调度"的真谛。

做Agent和做人有点像。有时候你需要停下来想清楚再动,有时候你需要先动起来再找路。关键是,你要知道自己在哪种模式,以及为什么

编程之路不易,但每一步踩过的坑都算数。保持好奇,持续实验,你也能做出让人眼前一亮的AI Agent。下次见!


关注私信备注:“资料代找获取”,全网计算机学习资料代找:例如:
《课程:2026 年多模态大模型实战训练营》
《课程:AI 大模型工程师系统课程 (22 章完整版 持续更新)》
《课程:AI 大模型系统实战课第四期 (2026 年开课 持续更新)》
《课程:2026 年 AGI 大模型系统课 23 期》
《课程:2026 年 AGI 大模型系统课 21 期》
《课程:AI 大模型实战课 8 期 (2026 年 2 月最新完结版)》
《课程:AI 大模型系统实战课三期》
《课程:AI 大模型系统课程 (2026 年 2 月开课 持续更新)》
《课程:AI 大模型全阶课程 (2025 年 12 月开课 2026 年 6 月结课)》
《课程:AI 大模型工程师全阶课程 (2025 年 10 月开课 2026 年 4 月结课)》
《课程:2026 年最新大模型 Agent 开发系统课 (持续更新)》
《课程:LLM 多模态视觉大模型系统课》
《课程:大模型 AI 应用开发企业级项目实战课 (2026 年 1 月开课)》
《课程:大模型智能体线上速成班 V2.0》
《课程:Java+AI 大模型智能应用开发全阶课》
《课程:Python+AI 大模型实战视频教程》
《书籍:软件工程 3.0: 大模型驱动的研发新范式.pdf》
《课程:人工智能大模型系统课 (2026 年 1 月底完结版)》
《课程:AI 大模型零基础到商业实战全栈课第五期》
《课程:Vue3.5+Electron + 大模型跨平台 AI 桌面聊天应用实战 (2025)》
《课程:AI 大模型实战训练营 从入门到实战轻松上手》
《课程:2026 年 AI 大模型 RAG 与 Agent 智能体项目实战开发课》
《课程:大模型训练营配套补充资料》

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐