从ReAct到ReWOO与Plan-and-Execute:新一代AI Agent架构解析与实战

📅 2026/8/14 3:27:32
从ReAct到ReWOO与Plan-and-Execute:新一代AI Agent架构解析与实战
1. 从“无脑循环”到“有脑规划”为什么我们需要新的Agent架构如果你最近在折腾AI Agent大概率遇到过这种场景你让Agent去查一下“今天上海的天气然后根据天气推荐一个适合的室内活动最后把推荐理由和活动详情整理成一份邮件草稿”。听起来是个很自然的任务链对吧但交给一个基于传统ReAct或类似框架的Agent去执行结果往往让人血压升高。它可能会陷入一种“无脑循环”先调用工具查天气拿到结果后开始“思考”下一步然后调用工具搜索“室内活动”再“思考”再调用工具写邮件……每一步都伴随着一次完整的LLM调用生成一段包含“Thought”、“Action”、“Observation”的文本。整个过程冗长、缓慢且成本高昂更关键的是LLM在每一步的“思考”都是孤立的缺乏对全局任务的整体把握和规划。这种“走一步看一步”的模式我称之为“无脑循环”。它暴露了早期Agent架构的核心痛点将任务分解、工具调用和结果整合的决策权完全交给了LLM在单次交互中的即时反应。LLM就像一个没有短期记忆的超级执行者每次只处理当前最紧迫的一步对后续步骤没有预见性导致效率低下并且在复杂、多步骤的任务中容易迷失方向或产生逻辑谬误。正是在这样的背景下ReWOO (Reasoning Without Observation)和Plan-and-Execute这两种架构思想应运而生。它们的目标高度一致将“规划”与“执行”分离让LLM专注于它最擅长的抽象推理和规划而将具体、重复的工具调用交给更高效、可靠的执行模块。这不仅仅是技术上的优化更是一种思维范式的转变——从让LLM“实时反应”转向让它“运筹帷幄”。简单来说传统Agent是“边想边做”容易手忙脚乱而ReWOO和Plan-and-Execute是“先想好再做”谋定而后动。接下来我们就深入这两种架构的骨髓看看它们是如何具体实现这一理念各自又有哪些独特的优势和适用场景。2. ReWOO架构拆解将推理与观察解耦ReWOO全称Reasoning Without Observation直译为“无观察推理”。这个名字非常精准地概括了它的核心思想让LLM在进行任务规划和推理时暂时“屏蔽”掉具体工具调用的细节和返回的庞杂数据即“Observation”。2.1 核心工作流程三步走策略一个标准的ReWOO Agent工作流程可以清晰地分为三个阶段第一阶段规划生成在这个阶段LLM接收用户查询并基于其内置的知识和对可用工具的理解生成一个完整的规划。这个规划不是一个简单的待办列表而是一个结构化的、包含抽象推理步骤的蓝图。关键点在于LLM在生成这个规划时并不实际调用任何工具它只是在“纸上谈兵”基于对工具功能的假设来规划步骤。例如对于“查询天气并推荐活动”的任务LLM可能生成如下规划规划 1. 子任务获取上海市今日的天气情况特别是温度和降水概率。 - 所需工具天气查询工具。 - 预期输出一个包含温度、天气状况、降水概率的结构化数据。 2. 子任务基于上述天气数据如雨天、高温等推理出适合的室内活动类别如博物馆、咖啡馆、健身房、电影院。 - 所需工具无纯推理。 - 预期输出一个活动类别列表及选择理由。 3. 子任务针对推荐的某个活动类别如博物馆获取具体的地点推荐和详情。 - 所需工具本地生活信息查询工具。 - 预期输出一个包含博物馆名称、地址、开放时间、特色的列表。 4. 子任务整合以上所有信息生成一封友好的邮件草稿。 - 所需工具无内容生成。 - 预期输出格式规范的邮件正文。第二阶段规划执行这是一个完全自动化的阶段。一个独立的执行器模块会接管这个规划。执行器逐条读取规划中的子任务当子任务标明需要工具时它就调用对应的工具API并将返回的原始结果Observation存储起来。对于不需要工具的纯推理子任务执行器可能会暂时跳过或者用一个占位符标记。这个阶段高效且直接没有LLM的反复介入。第三阶段答案合成所有子任务执行完毕后执行器收集了所有的中间结果。此时LLM再次被调用。这次它将接收到最初的用户查询、最初生成的完整规划、以及每个规划步骤对应的实际执行结果。LLM的职责是充当“总编辑”基于这些材料合成出最终、连贯、符合用户要求的答案。注意ReWOO并非完全不让LLM看到观察结果而是将观察结果的介入时机推迟到了最后一步。这避免了LLM在推理过程中被冗长、可能含有噪音的工具返回结果所干扰使其能保持清晰、高层次的规划思路。2.2 优势与价值为什么它能“告别无脑循环”大幅降低Token消耗与延迟这是最直观的好处。传统ReAct模式每一步都需要LLM生成包含Thought、Action的文本并等待Observation返回后再进行下一步交互次数多总Token消耗巨大。ReWOO将LLM调用减少到两次规划 合成中间大量的工具调用是高效的API请求整体延迟和成本显著下降。在我的实测中对于一个包含4-5个工具调用的复杂任务ReWOO的响应速度能提升40%-60%API调用成本降低超过50%。提升规划的连贯性与全局性因为LLM在规划时拥有完整的上下文整个任务描述并且不受中途具体数据干扰它更容易生成逻辑自洽、步骤衔接顺畅的全局规划。它能够预先考虑到步骤之间的依赖关系例如步骤3需要步骤1的结果作为输入从而生成更合理的计划。增强结果的可控性与可解释性生成的“规划”是一个清晰的中介产物。我们可以审查这个规划看LLM是否理解了任务步骤分解是否合理。这为调试和优化Agent提供了极大的便利。如果最终结果不对我们可以定位是规划阶段就出了问题还是某个工具返回结果有误抑或是合成阶段的理解偏差。2.3 局限性与挑战没有银弹ReWOO同样面临挑战对规划能力要求极高整个架构的基石是第一步生成的规划。如果LLM生成的规划本身有漏洞、逻辑错误或对工具能力有误解那么后续执行再完美最终结果也必然是错的。这高度依赖于LLM本身的规划与推理能力。难以处理动态变化规划是静态的。如果在执行过程中外部环境发生了规划时未预料到的变化例如某个关键API临时不可用或返回的数据格式与预期严重不符ReWOO缺乏动态调整规划的能力。整个流程会僵化地执行完既定步骤可能导致合成阶段得到毫无意义或错误的结果。纯推理步骤的“空转”规划中可能存在“纯推理”步骤。在执行阶段这些步骤没有实际执行内容可能导致上下文对齐上的小问题需要在合成阶段由LLM很好地弥补。3. Plan-and-Execute架构探秘更具弹性的任务管理Plan-and-Execute 是一种比ReWOO更概括、也更灵活的架构范式。它的核心同样是“规划”与“执行”分离但它在两者之间引入了一个更明确的“执行代理”角色并且为动态调整留下了更多空间。LangChain的Plan-and-Execute实现是这一思想的典型代表。3.1 核心组件与协作模式该架构通常包含两个核心Agent规划者一个LLM负责根据用户输入创建初始的任务执行计划。这个计划通常是一个任务列表每个任务有清晰的描述。执行者另一个LLM也可以是同一个但角色分离负责具体执行规划中的单个任务。执行者拥有调用工具的权限它接收“规划者”分配的具体任务思考如何完成它调用工具并返回该任务的结果。此外还有一个关键的协调器通常是逻辑代码也可能由LLM驱动负责管理整个流程将规划分解为任务将任务分配给执行者收集执行结果并根据情况决定是继续执行下一个任务还是需要将当前结果反馈给规划者进行规划修正。3.2 动态工作流与反思机制这是Plan-and-Execute超越静态ReWOO的关键。其工作流是一个动态循环初始规划规划者Agent生成第一个版本的计划。任务执行协调器取出第一个任务交给执行者Agent去完成。结果评估与迭代如果任务成功完成协调器将结果记录下来并继续下一个任务。如果任务失败、或执行结果与预期严重偏离协调器可以将这个“意外情况”连同当前所有已收集的结果一起反馈给规划者。规划修正规划者根据当前遇到的实际问题重新评估并修改后续的规划。这可能包括跳过某个任务、增加新任务、改变任务顺序等。继续执行协调器基于修正后的新规划继续驱动执行者工作。这个过程可以循环直到所有任务完成或达到终止条件。3.3 与ReWOO的对比灵活性 vs. 效率我们可以通过一个表格来清晰对比两者特性ReWOOPlan-and-Execute核心思想解耦推理与观察一次性生成静态规划。分离规划与执行角色支持动态调整。规划性质静态蓝图。生成后不再改变。动态路线图。可根据执行反馈进行修正。LLM调用模式两次主要调用规划 最终合成。多次调用初始规划 N次执行 可能的M次规划修正。执行单元自动化的执行器非LLM。专门的执行者AgentLLM。灵活性较低。难以应对规划外情况。较高。具备“反思”和“重规划”能力。效率与成本高。LLM调用次数最少效率高。中。LLM交互更多成本相对较高但容错性好。适用场景任务定义清晰、步骤稳定、工具可靠的环境。如数据查询流水线、格式固定的报告生成。任务有一定不确定性、环境可能变化、需要试错的场景。如复杂问题排查、创意性内容的多轮生成。可解释性强。有清晰的规划文档。中。有规划历史但动态调整过程可能复杂。实操心得选择哪种架构首先取决于你对任务确定性的判断。如果你构建的是一个处理标准化流程的客服机器人或数据分析助手ReWOO的高效和简洁是巨大优势。但如果你在做一个需要探索性解决问题、与不完美API打交道的研发助手Plan-and-Execute的鲁棒性可能更重要。不要追求架构的“先进”而要追求架构与问题域的“匹配”。4. 实战构建一个简易的Plan-and-Execute Agent理论说得再多不如动手试一下。我们使用LangChain来快速构建一个具备规划-执行能力的Agent完成一个稍复杂的任务“查询北京明天下午的天气如果下雨就推荐一个适合雨天的电影并介绍其主要情节如果不下雨就推荐一个户外公园并介绍其特色。”我们将使用OpenAI的模型作为LLM并假设我们有可用的天气查询和电影/公园信息查询工具这里用模拟函数代替。4.1 环境准备与工具定义首先安装必要库并定义几个简单的模拟工具。# 安装依赖 (假设已安装langchain和openai) # pip install langchain langchain-openai import os from langchain_openai import ChatOpenAI from langchain.agents import Tool, AgentExecutor from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain.agents.format_scratchpad import format_log_to_str from langchain.agents.output_parsers import ReActSingleInputOutputParser from typing import Dict, Any # 设置你的OpenAI API Key os.environ[OPENAI_API_KEY] your-api-key-here # 初始化LLM llm ChatOpenAI(modelgpt-4-turbo-preview, temperature0) # 模拟工具1天气查询 def get_weather(location: str, date: str) - str: 模拟天气查询。为了演示我们固定返回‘下雨’ print(f[工具调用] 查询{location}在{date}的天气) # 模拟数据 weather_data { 北京: { 明天: {下午: 小雨温度15-18℃降水概率80%} } } return weather_data.get(location, {}).get(date, {}).get(下午, 天气信息暂缺) # 模拟工具2电影推荐 def recommend_movie(weather_condition: str) - str: 根据天气推荐电影 print(f[工具调用] 根据天气‘{weather_condition}’推荐电影) recommendations { 下雨: 《肖申克的救赎》 - 一部关于希望与救赎的经典影片雨天窝在家里观看再合适不过。, 晴天: 《阳光小美女》 - 一部充满温情的家庭公路片适合晴朗的好心情。 } return recommendations.get(weather_condition, 暂无推荐) # 模拟工具3公园推荐 def recommend_park(weather_condition: str) - str: 根据天气推荐公园 print(f[工具调用] 根据天气‘{weather_condition}’推荐公园) recommendations { 晴天: 奥林匹克森林公园 - 面积广阔植被茂盛适合骑行、跑步和野餐。, 下雨: 室内植物园 - 在玻璃房内欣赏雨景和各类植物别有一番风味。 } return recommendations.get(weather_condition, 暂无推荐) # 将函数封装成LangChain Tool对象 tools [ Tool( nameGetWeather, funclambda loc, dt: get_weather(loc, dt), description查询指定城市在指定日期的天气情况。输入参数location城市名 date日期如‘明天’。 ), Tool( nameRecommendMovie, funclambda w: recommend_movie(w), description根据天气状况推荐一部电影并简要介绍。输入参数weather_condition天气描述如‘下雨’。 ), Tool( nameRecommendPark, funclambda w: recommend_park(w), description根据天气状况推荐一个公园并简要介绍。输入参数weather_condition天气描述如‘晴天’。 ), ]4.2 构建规划者Agent规划者需要理解任务并分解出步骤。我们为其设计一个简单的提示词。from langchain.schema import SystemMessage, HumanMessage, AIMessage planner_prompt ChatPromptTemplate.from_messages([ SystemMessage(content你是一个任务规划专家。请将用户的复杂请求分解成一系列清晰的、可顺序执行的具体步骤。每个步骤应该明确说明要做什么以及需要用到哪个工具如果有的话。请以‘步骤1... 步骤2...’的列表格式输出。), HumanMessage(content{input}) ]) planner_chain planner_prompt | llm # 测试规划者 user_query 查询北京明天下午的天气如果下雨就推荐一个适合雨天的电影并介绍其主要情节如果不下雨就推荐一个户外公园并介绍其特色。 plan planner_chain.invoke({input: user_query}) print( 生成的规划 ) print(plan.content) print(\n)运行后你可能会得到类似这样的规划步骤1使用GetWeather工具查询北京明天下午的天气情况。 步骤2根据步骤1获取的天气结果判断是“下雨”还是“不下雨”。 步骤3如果天气是“下雨”则使用RecommendMovie工具根据“下雨”这个条件推荐一部电影并获取其介绍。 步骤4如果天气是“不下雨”则使用RecommendPark工具根据“晴天”或类似条件推荐一个公园并获取其介绍。 步骤5整合天气信息和推荐内容形成最终的回答。4.3 构建执行者Agent执行者负责完成单个具体步骤。我们使用一个标准的ReAct风格的Agent。from langchain.agents import create_react_agent # 创建执行者Agent executor_agent create_react_agent(llm, tools, allow_dangerous_deserializationTrue) # 注意实际生产环境需处理序列化警告 agent_executor AgentExecutor(agentexecutor_agent, toolstools, verboseTrue, handle_parsing_errorsTrue)4.4 实现协调器逻辑协调器是粘合剂它解析规划按顺序将步骤交给执行者并处理条件逻辑。def coordinate_execution(plan_text: str, original_query: str) - str: 协调器函数解析并执行规划 steps [] # 简单解析规划文本按行分割并过滤空行和编号 for line in plan_text.split(\n): line line.strip() if line.startswith(步骤) or line.startswith(Step): # 提取步骤描述 step_desc line.split(, 1)[-1].strip() steps.append(step_desc) print(f解析出{len(steps)}个步骤: {steps}) context {} # 用于存储步骤间的共享信息如天气结果 final_answer_parts [] for i, step in enumerate(steps, 1): print(f\n 正在执行步骤 {i}: {step}) # 根据步骤描述决定如何执行 # 这是一个简化的逻辑实际项目可能需要更复杂的NLU来理解步骤意图 if GetWeather in step: # 构造给执行者的问题 task_for_agent f请执行{step}。用户原始问题是{original_query} result agent_executor.invoke({input: task_for_agent}) weather_result result[output] context[weather] weather_result final_answer_parts.append(f天气查询结果{weather_result}) elif 判断 in step or 根据 in step and 天气 in step: # 这是一个逻辑判断步骤由协调器自己处理 weather context.get(weather, ) if 下雨 in weather: context[should_recommend_movie] True print(判断结果下雨将推荐电影。) final_answer_parts.append(根据天气今天下午预计有雨。) else: context[should_recommend_movie] False print(判断结果不下雨将推荐公园。) final_answer_parts.append(根据天气今天下午天气晴好。) elif RecommendMovie in step and context.get(should_recommend_movie): task_for_agent f请执行{step}。已知天气情况是{context.get(weather)} result agent_executor.invoke({input: task_for_agent}) final_answer_parts.append(f电影推荐{result[output]}) elif RecommendPark in step and not context.get(should_recommend_movie, True): task_for_agent f请执行{step}。已知天气情况是{context.get(weather)} result agent_executor.invoke({input: task_for_agent}) final_answer_parts.append(f公园推荐{result[output]}) elif 整合 in step or 形成最终回答 in step: # 最终合成步骤可以调用LLM这里简单拼接 print(所有步骤执行完毕进行最终整合。) # 在实际应用中这里可以再调用一次LLM来润色最终答案 # final_answer llm.invoke(f基于以下信息回答用户问题‘{original_query}’\n{chr(10).join(final_answer_parts)}) # return final_answer.content else: print(f步骤‘{step}’无法识别或条件不满足跳过。) # 简单返回拼接的结果 return \n.join(final_answer_parts) # 运行整个流程 final_result coordinate_execution(plan.content, user_query) print(\n 最终结果 ) print(final_result)4.5 运行结果与解析运行上述代码你会在控制台看到类似以下的输出 生成的规划 步骤1使用GetWeather工具查询北京明天下午的天气情况。 步骤2根据步骤1获取的天气结果判断是“下雨”还是“不下雨”。 步骤3如果天气是“下雨”则使用RecommendMovie工具根据“下雨”这个条件推荐一部电影并获取其介绍。 步骤4如果天气是“不下雨”则使用RecommendPark工具根据“晴天”或类似条件推荐一个公园并获取其介绍。 步骤5整合天气信息和推荐内容形成最终的回答。 解析出5个步骤: [使用GetWeather工具查询北京明天下午的天气情况。, 根据步骤1获取的天气结果判断是“下雨”还是“不下雨”。, 如果天气是“下雨”则使用RecommendMovie工具根据“下雨”这个条件推荐一部电影并获取其介绍。, 如果天气是“不下雨”则使用RecommendPark工具根据“晴天”或类似条件推荐一个公园并获取其介绍。, 整合天气信息和推荐内容形成最终的回答。] 正在执行步骤 1: 使用GetWeather工具查询北京明天下午的天气情况。 [工具调用] 查询北京在明天的天气 ... (Agent执行日志) ... 输出小雨温度15-18℃降水概率80% 正在执行步骤 2: 根据步骤1获取的天气结果判断是“下雨”还是“不下雨”。 判断结果下雨将推荐电影。 正在执行步骤 3: 如果天气是“下雨”则使用RecommendMovie工具根据“下雨”这个条件推荐一部电影并获取其介绍。 [工具调用] 根据天气‘下雨’推荐电影 ... (Agent执行日志) ... 输出《肖申克的救赎》 - 一部关于希望与救赎的经典影片雨天窝在家里观看再合适不过。 正在执行步骤 4: 如果天气是“不下雨”则使用RecommendPark工具根据“晴天”或类似条件推荐一个公园并获取其介绍。 步骤‘如果天气是“不下雨”则使用RecommendPark工具根据“晴天”或类似条件推荐一个公园并获取其介绍。’无法识别或条件不满足跳过。 正在执行步骤 5: 整合天气信息和推荐内容形成最终的回答。 所有步骤执行完毕进行最终整合。 最终结果 天气查询结果小雨温度15-18℃降水概率80% 根据天气今天下午预计有雨。 电影推荐《肖申克的救赎》 - 一部关于希望与救赎的经典影片雨天窝在家里观看再合适不过。代码解析与踩坑点规划解析是脆弱的我们用了简单的字符串匹配来解析规划步骤。在实际生产中这极其不可靠。规划者LLM的输出格式必须被严格约束例如要求输出JSON或者使用更强大的解析器如Pydantic输出解析。协调器逻辑硬编码示例中的coordinate_execution函数包含了大量的if...elif...逻辑来判断步骤意图。这只是一个演示。真正的Plan-and-Execute框架如LangGraph会提供更优雅的状态机和流程控制方式让LLM来参与决策“下一步该执行哪个节点”。执行者的效率我们让执行者Agent去处理每一个步骤包括简单的工具调用。对于GetWeather这种简单工具直接调用函数比通过一个完整的ReAct Agent更高效。在实际架构中协调器应该能区分“简单工具调用”和“需要复杂推理的任务”并分派给不同的执行单元。错误处理与重规划本例完全没有实现错误处理和动态重规划。如果GetWeather工具调用失败整个流程就会崩溃。一个健壮的实现需要在协调器中加入异常捕获并可能触发规划修正流程。5. 架构演进与选型思考ReWOO、Plan-and-Execute及其他了解了这两种架构后我们需要将其放在更广阔的Agent架构演进图中来看。当前主流的Agent设计模式大致可以看作一个光谱一端是高度耦合的“反应式”Agent如经典的ReAct。LLM同时负责思考、决策和生成工具调用指令每一步都紧密耦合。优点是简单直接适合快速原型验证缺点就是开头提到的“无脑循环”问题效率低、成本高、长程依赖弱。另一端是完全解耦的“规划优先”AgentReWOO是其中的典型代表。它极端地将规划与执行分离追求极致的效率。适合流程固定、确定性高的任务。Plan-and-Execute处于中间地带它分离了角色但保持了规划与执行之间的动态沟通渠道。它用更高的复杂性和一定的性能开销换取了应对不确定性的能力。LangChain的Plan-and-Execute和AutoGPT的早期思想都属于这一类。更前沿的架构如LangGraph可以看作是Plan-and-Execute范式的强化和通用化。它用图Graph来显式地定义Agent的工作流节点可以是LLM调用、工具调用或条件判断边定义了控制流。这带来了前所未有的灵活性和可控性你可以构建带循环、分支、并行处理的工作流真正实现复杂的、有状态的Agent应用。LangGraph的StateGraph概念使得“规划修正”和“上下文传递”变得非常自然。那么如何为自己的项目选型我的经验是问自己三个问题任务的确定性与复杂性如何高确定性线性流程比如“从数据库A取数加工后写入数据库B然后发邮件通知”。这类任务步骤清晰几乎不会出错。首选ReWOO它的高效是巨大优势。你甚至可以不用通用框架自己写一个简单的“规划生成脚本执行”的管道。中低确定性有条件分支比如“分析这个错误日志尝试几种常见修复方案如果不行就搜集更多系统信息”。任务路径可能因执行结果而改变。Plan-and-Execute或基于LangGraph的定制工作流是更好的选择。高复杂性探索性任务比如“研究这个新开源项目为我写一份评估报告并对比它和另一个项目的优劣”。这需要大量的信息搜集、整理、推理和合成路径高度不确定。基于LangGraph构建的、具备强大反思和重规划能力的Agent几乎是唯一选择。开发与维护成本考量如何ReWOO概念简单自己实现一个基础版本不难调试也相对直观看规划对不对即可。Plan-and-Execute需要管理多个Agent的协作和状态复杂度更高。LangGraph功能强大但学习曲线最陡峭需要你以“图”的思维来设计应用。对于简单任务可能是杀鸡用牛刀。性能与成本的边界在哪里如果应用频率极高每次调用都省下几十万Token长期来看ReWOO节省的成本非常可观。如果任务本身失败成本高比如错误的操作会导致生产事故那么Plan-and-Execute或LangGraph提供的容错和重试机制就值得投入。在我最近的一个项目中我们为内部数据分析平台构建了一个“自助数据提取与可视化Agent”。最初我们用了ReAct用户抱怨速度慢。后来我们切换到ReWOO架构将常见的几十种数据查询和图表生成请求模板化让LLM生成一个包含参数和图表类型的“规划”然后由后端服务直接执行。响应时间从10-20秒缩短到2-3秒成本下降了70%。这就是架构选型带来的直接收益。最后记住一点没有最好的架构只有最合适的架构。很多时候一个混合模式可能更有效对于任务中确定性高的部分如数据查询采用ReWOO式的静态规划执行对于需要创意或决策的部分如报告结论撰写采用更灵活的、LLM深度参与的方式。理解这些架构模式的本质才能灵活地组合它们设计出真正强大、高效的AI Agent。