1. 从“想”到“做”Agent ReAct模式的核心价值如果你最近在关注AI Agent的开发或者尝试过让大模型去完成一些稍微复杂的任务比如“帮我查一下明天北京的天气然后根据天气推荐一个室内活动并生成一份简单的日程安排”你可能会发现一个尴尬的现象模型要么直接开始“编造”天气信息要么在推荐活动时天马行空完全脱离了查询到的真实数据。它似乎在一个“纯想象”的层面工作缺乏与现实世界比如一个真实的天气API交互并基于反馈调整行动的能力。这正是ReAct模式要解决的核心问题。ReAct即“Reasoning Acting”推理行动它不是某个具体的框架或工具而是一种让AI Agent智能体更可靠、更接近人类解决问题方式的核心范式。简单来说它让Agent学会“三思而后行”先动脑推理Reasoning再动手执行Acting然后根据执行结果再次推理形成一个“思考-行动-观察”的闭环。为什么这种模式如此重要在传统的提示工程或简单链式调用中大模型更像一个“闭卷考试”的考生只能基于已有的知识训练数据进行一次性输出。但对于动态、多步骤、需要与外部工具交互的任务这种模式就力不从心了。ReAct模式将Agent变成了一个“开卷考试”的考生允许它主动查阅资料调用工具、验证信息并动态调整解题思路。我最初接触这个概念是在尝试构建一个自动化数据分析Agent时。当时模型能很好地理解“分析上个月销售数据”这个指令并生成一段看似合理的分析文本。但问题是它分析的数据是它“想象”出来的并非我数据库里的真实数字。这让我意识到没有“行动”能力的“推理”在真实业务场景中几乎是无效的。ReAct模式正是连接AI“大脑”与真实世界“手脚”的关键桥梁。2. ReAct模式的工作原理一个动态的思考循环理解ReAct最好的方式不是看定义而是看它如何运行。我们可以将其拆解为一个可重复的循环单元这个循环由三个核心步骤构成推理Reason、行动Act、观察Observe。2.1 循环拆解Reason, Act, Observe第一步推理Reason这是Agent的“思考”阶段。Agent基于当前的任务目标、已有的历史信息包括之前的观察结果以及自身的知识来决定下一步应该做什么。这个“决定”通常以自然语言的形式输出内容是关于下一步行动的思考过程和具体指令。例如“用户想了解明天的天气。要完成这个任务我需要先调用天气查询API。目前我还没有任何数据所以我的下一步行动是查询北京明天的天气。”关键点在于这个推理过程是可解释的。Agent会“说出”它的思考这让我们能够追踪其决策逻辑这在调试和优化Agent时至关重要。它不仅仅是内部的一个向量计算而是以文本形式呈现的思维链Chain-of-Thought。第二步行动Act根据推理步骤得出的结论Agent会执行一个具体的动作。这个动作通常是调用一个预定义的工具Tool/Function比如执行一段代码python调用一个外部APIget_weather(api_key, city)查询数据库sql_query甚至是在一个模拟环境中移动move_forward行动的输出是一个对工具的调用请求。这个阶段Agent从“思考者”转变为“执行者”。第三步观察Observe行动执行后会有一个结果。这个结果被反馈给Agent成为新的“观察”。例如调用天气API后返回的结果可能是{“city”: “Beijing”, “weather”: “Sunny”, “temp”: “22°C”}。Agent需要接收并理解这个观察结果。观察结果被纳入到Agent的上下文Context中。接下来循环回到第一步“推理”。Agent会基于这个新的观察重新思考“我已经获得了北京的天气是晴天22°C。用户的下一个需求是根据天气推荐活动。晴天适合户外活动所以我下一步应该搜索‘北京晴天户外活动推荐’。”这个Reason - Act - Observe - Reason - ...的循环会一直持续直到Agent推理出任务已经完成例如生成了最终的日程安排并回复给用户或者达到了预设的循环次数上限。2.2 与简单链式调用Chain的本质区别很多人容易把ReAct和LangChain等框架中常见的“Sequential Chain”顺序链混淆。它们看起来都是一步接一步但内核完全不同。简单链式调用Chain流程是预设且线性的。好比一个事先写好的剧本第一步调用工具A第二步将A的结果传给工具B第三步将B的结果总结输出。如果工具A失败了整个链就断了或者会带着错误信息继续执行缺乏应变能力。它没有“思考”环节只是机械地执行预定步骤。ReAct模式流程是动态且由模型实时决定的。好比一个拥有剧本大纲的导演他会根据上一场戏的拍摄效果观察临时决定下一场戏该怎么拍推理然后指挥剧组行动行动。如果查询天气API失败观察到一个错误Agent会推理“API调用失败可能网络问题或城市名错误。我可以重试一次或者换一个备用天气源。” 然后执行新的行动。它的路径是不确定的具备强大的容错和规划能力。用一个更生活的比喻Chain像是按照固定菜谱做菜ReAct则像是经验丰富的大厨会根据灶火的大小、食材的状态随时调整翻炒的手法和调味料的用量。3. 如何实现一个基础的ReAct Agent从零搭建理解了原理我们来看如何动手实现。这里我们不依赖任何重型框架用最直观的Python代码和OpenAI API来演示核心骨架让你看清每一块“骨头”是怎么长的。我们以实现一个“联网搜索Agent”为例。3.1 环境准备与核心组件定义首先你需要准备一个支持函数调用Function Calling的大模型API如OpenAI的GPT-4系列或Claude 3系列。我们以OpenAI为例。pip install openai然后定义我们Agent最核心的两个部分工具集和提示词模板。import openai import json import requests from typing import Dict, Any, List # 假设你的API Key已设置在环境变量中 client openai.OpenAI() # 1. 定义工具集Actions def search_web(query: str) - str: 一个模拟的网页搜索工具。在实际应用中你可以接入Serper API、Google Search API等。 # 这里为了演示我们模拟返回一些固定结果 print(f[行动] 正在搜索: {query}) # 模拟网络请求和结果解析 # 真实情况results call_search_api(query) mock_results f关于{query}的搜索结果晴天适合户外徒步、骑行或公园野餐。北京奥林匹克森林公园是热门选择。 return mock_results def calculate(expression: str) - str: 一个简单的计算器工具。 print(f[行动] 正在计算: {expression}) try: result eval(expression) # 注意生产环境请使用更安全的方式如ast.literal_eval return f计算结果: {result} except Exception as e: return f计算错误: {e} # 将工具函数包装成模型能识别的格式 tools [ { type: function, function: { name: search_web, description: 在互联网上搜索信息回答实时性问题。, parameters: { type: object, properties: { query: {type: string, description: 搜索查询词} }, required: [query] } } }, { type: function, function: { name: calculate, description: 执行数学计算。, parameters: { type: object, properties: { expression: {type: string, description: 数学表达式如 3 5 * 2} }, required: [expression] } } } ]3.2 构建核心ReAct循环引擎接下来是重头戏驱动循环的引擎。这个引擎负责管理对话历史即Reason, Act, Observe的记录并反复调用模型直到任务完成。def run_react_agent(user_query: str, max_steps: int 10) - str: 运行一个简单的ReAct Agent。 :param user_query: 用户初始问题 :param max_steps: 最大循环步数防止无限循环 :return: Agent的最终回答 # 初始化对话历史其中包含系统指令用于设定Agent的角色和行为模式。 messages [ { role: system, content: 你是一个善于思考并能够使用工具的助手。请遵循以下步骤解决问题 1. **思考Reason**分析当前情况、目标和可用工具决定下一步做什么。把你的思考过程用中文写在“思考”后面。 2. **行动Act**如果需要使用工具严格按照JSON格式调用工具格式为{action: 工具名, action_input: {工具参数}}。 3. **观察Observe**工具调用结果会以“观察{结果}”的形式提供给你。 4. 重复1-3步直到你认为可以给出最终答案。 5. 给出最终答案时以“最终答案”开头。 请严格按此格式响应。 }, {role: user, content: user_query} ] step 0 while step max_steps: step 1 print(f\n--- 第 {step} 步 ---) # 调用模型进行“推理”Reason并期望它可能决定“行动”Act response client.chat.completions.create( modelgpt-4-turbo, # 或 gpt-3.5-turbo但4的推理和工具调用能力更强 messagesmessages, toolstools, tool_choiceauto, # 由模型决定是否调用工具 ) response_message response.choices[0].message # 将模型的响应包含推理文本添加到历史中 messages.append(response_message) # 检查模型是否决定调用工具Act tool_calls response_message.tool_calls if tool_calls: # 模型决定行动可能有多个工具调用这里处理第一个 print(f[推理] {response_message.content}) for tool_call in tool_calls: function_name tool_call.function.name function_args json.loads(tool_call.function.arguments) print(f[行动] 调用工具: {function_name}, 参数: {function_args}) # 执行对应的工具函数 available_functions {search_web: search_web, calculate: calculate} function_to_call available_functions[function_name] action_result function_to_call(**function_args) print(f[观察] {action_result}) # 将工具执行结果Observe作为新的消息添加到历史中 messages.append({ role: tool, tool_call_id: tool_call.id, name: function_name, content: action_result, # 观察结果 }) # 本轮结束进入下一轮循环模型将基于观察进行新的推理 else: # 模型没有调用工具直接输出了内容这可能是最终答案或纯推理 print(f[推理/答案] {response_message.content}) if 最终答案 in response_message.content: print(\n--- 任务完成 ---) return response_message.content # 如果不是最终答案继续循环例如模型只做了推理但没行动这在实际中较少见但循环会继续 return 达到最大步数任务未完成。 # 运行示例 if __name__ __main__: final_answer run_react_agent(北京明天天气怎么样如果是晴天推荐一个活动并估算一下如果4个人参加这个活动交通和餐饮大概需要多少预算) print(\n最终输出, final_answer)运行这段代码你会在控制台看到一个清晰的ReAct循环过程。对于上面的复杂问题模型可能会先推理需要天气调用搜索工具模拟观察到“晴天”结果然后推理需要推荐活动再次调用搜索再推理需要计算预算调用计算器工具。每一步的“思考”和“行动”都清晰可见。注意上面的模拟搜索工具返回了固定结果。在真实场景中你需要接入真实的搜索API并处理好可能出现的网络错误、结果解析失败等情况。这就是ReAct模式展现优势的地方当工具返回错误时模型能观察到错误信息并推理出重试或更换策略。3.3 提示词工程的关键细节系统提示词System Prompt是ReAct Agent的“大脑初始化指令”其质量直接决定Agent的表现。除了上面示例中的基础结构有几个关键点需要打磨强化格式遵从必须明确要求模型按“思考”、“行动”、“最终答案”的格式输出。可以使用更严格的描述如“你的输出必须且只能包含以下三个部分之一思考、工具调用、最终答案。”设定思考深度鼓励模型进行深度推理。例如可以加入“在思考步骤请详细分析当前已知信息、待解决问题、可用工具的适用性并解释为什么选择下一步行动。”引入反思机制在提示词中要求模型在得到观察后不仅规划下一步还要评估上一步行动的有效性。例如“观察结果是否直接回答了问题如果没有缺失了什么信息”防止幻觉与循环明确指令“严禁编造工具不提供的信息”、“如果你在连续三步中使用了相同工具且未获得进展应尝试不同策略或给出当前已知的最佳答案。”一个更健壮的提示词开头可以是你是一个严谨的AI助手通过思考、行动、观察的循环解决问题。你的每次响应必须严格遵循以下结构 **思考** [在此详细阐述你的分析过程。基于当前对话历史和观察你现在知道什么你的目标是什么有哪些可用工具你下一步计划做什么以及为什么] **行动** [仅当需要调用工具时填写。必须为严格的JSON格式{action: tool_name, action_input: {param: value}}。如果不需要工具则留空或写“无”。] **最终答案** [当你拥有足够信息可以直接、准确回答用户原始问题时在此给出完整答案。这是循环的终点。] 现在开始处理第一个用户请求。4. 进阶实践处理复杂任务与常见陷阱一个基础的ReAct循环跑通后你会很快遇到更现实的问题任务可能非常复杂需要多个工具协同模型可能会陷入死循环或者工具返回的结果模型无法正确理解。下面分享一些进阶实践和踩坑经验。4.1 多工具协作与状态管理现实任务很少只用一个工具。例如“总结某公司最新财报并分析其股价影响”可能需要1) 搜索工具获取财报新闻2) 爬虫或API工具获取具体财报PDF3) 文档解析工具提取文本4) 总结分析工具可能是另一个LLM调用生成摘要5) 金融数据工具查询历史股价。这时Agent的“推理”步骤就变得至关重要。它需要像一个项目经理决定调用哪个工具、以什么顺序、以及如何将上一个工具的输出转化为下一个工具的输入。在我们的简单引擎中所有历史消息都通过messages列表传递这实际上就是Agent的“工作记忆”。但对于超长对话需要警惕上下文长度限制。解决方案关键信息摘要在每一轮循环后可以添加一个轻量级的“总结”步骤将冗长的观察结果提炼成关键事实再放入上下文。这可以是一个独立的提示词调用要求模型“请用一句话总结刚才观察到的核心信息。”向量数据库存储对于非常长的任务可以将历史“观察”中的重要结果如提取的数据、关键结论存入向量数据库。当模型需要参考时通过检索相关片段而非传入全部历史。子任务分解在初始推理时就要求模型将复杂任务分解为清晰的子任务列表。然后ReAct循环可以围绕一个“子任务栈”进行完成一个弹出下一个使状态更清晰。4.2 循环失控与停滞的应对策略ReAct Agent最常见的两个问题是1)无限循环在两个工具间来回调用无法推进2)提前终止模型过早地给出了不完整的“最终答案”。无限循环的案例 假设工具search_web有时返回“未找到相关信息”。Agent的推理可能是“未找到信息我需要换一个关键词再搜。”于是它再次调用search_web可能又失败陷入“搜索-失败-再搜索”的死循环。应对策略设置最大步数如示例代码中的max_steps这是最后的安全网。在工具层面增加重试和降级逻辑例如search_web工具内部可以尝试3种不同的查询句式如果都失败返回一个结构化的错误信息如{status: error, reason: multiple_attempts_failed, suggestion: 请尝试更具体或不同的关键词。}。这样观察结果更丰富有助于模型推理出新策略。在提示词中引入“循环检测”指令例如“请保持对历史的关注。如果你发现最近三次行动的模式高度相似且未获得新信息你应该停止当前策略尝试一个完全不同的方法或承认无法从此路径获得更多信息。”实现一个简单的循环检测器在引擎代码中维护一个最近N步的行动历史列表。如果检测到完全相同的“推理-行动”对重复出现则中断循环并主动向对话历史中插入一条警告信息“检测到可能循环请重新评估策略。”提前终止的案例 用户问“写一份关于量子计算的行业报告”模型可能搜索了一两篇文章后就推理“已有足够信息”开始生成一份非常肤浅的报告。应对策略在系统提示中明确“完成标准”例如“只有当你能从至少三个独立可靠来源交叉验证核心信息并已涵盖用户问题中的所有关键子主题时才能给出最终答案。”实施“答案质量检查”步骤在模型输出“最终答案”后不立即返回。可以启动一个额外的“审查”步骤用另一个提示词让模型或另一个审查模型评估答案的完整性、准确性。如果不合格则将审查意见作为新的“观察”丢回主循环。4.3 观察结果的解析与标准化工具返回的结果观察五花八门可能是JSON、HTML、纯文本或错误码。模型有时会错误解析这些结果。例如一个返回HTML片段的搜索工具模型可能误将div标签当作答案的一部分。经验之谈工具设计应面向LLM尽可能让工具返回结构清晰、简洁的文本或JSON。例如网页搜索工具不应返回整个HTML而应通过后端解析返回格式如[{title: ..., snippet: ..., url: ...}, ...]的标准化结果。为观察结果添加元数据在将观察插入消息历史时可以包装一下。例如不是直接插入“晴天”而是插入“[来自天气API] 查询成功。天气状况晴天温度22°C。可信度高。”这为模型的推理提供了更多上下文。处理工具错误工具抛出的异常一定要被捕获并以模型能理解的方式格式化后作为观察。例如“观察[工具调用失败] 天气服务暂时不可用HTTP 503。建议稍后重试或使用备用城市数据。”这比一个Python异常堆栈跟踪有用得多。5. 从ReAct模式看AI Agent的发展层次理解了ReAct我们就能更好地定位目前市面上各种各样的Agent框架和概念。在我看来AI Agent的能力可以粗略分为三个层次ReAct是通往更高层次的基石。第一层基础工具调用Function Calling这是大多数LLM API现已支持的能力。模型根据提示词决定是否调用以及调用哪个用户定义的函数。它本质上是单次的“推理-行动”缺少持续的、基于观察的循环。只能完成“一步到位”的简单任务比如“计算一下156的平方根”。第二层规划与执行循环ReAct模式这就是本文讨论的核心。Agent具备了在较长序列中动态规划、执行、调整的能力。它解决了“多步骤”和“依赖外部状态”的任务。目前大多数自称的“Agent框架”如LangChain的AgentExecutor、AutoGPT的早期核心都是在这一层构建的。其上限取决于模型本身的规划推理能力和工具集的丰富程度。第三层长期记忆与技能学习这是当前的前沿探索方向。在这一层Agent不仅能在一次会话中完成循环还能将本次循环中学到的“经验”例如某种查询方式总能得到更好结果存储到长期记忆中并在未来的任务中复用。它甚至能通过分析自己的成功和失败案例自动优化提示词或生成新的工具技能。Harness、Hermes等框架所探讨的“基础设施层”正是为了支撑Agent向这一层演进提供持久化存储、技能库管理、性能监控等能力。ReAct模式是第二层的核心实现范式也是通向第三层的必经之路。它让AI从静态的知识库变成了一个能够主动探索、试错并解决问题的动态智能体。虽然当前的ReAct Agent还远未达到通用人工智能的水平在复杂规划、长期一致性方面仍有局限但它已经为自动化处理大量知识型工作流打开了大门。在我自己的项目中将客服工单分类、信息提取和初步回复的流程改造成ReAct Agent后不仅准确率提升了更重要的是整个流程变得可解释、可调试。当出现错误时我可以回溯完整的“思考-行动”链精准定位是工具的问题、模型推理的偏差还是提示词指令的模糊。这种透明度和可控性是在生产环境中应用AI不可或缺的。最后一个实用的建议当你开始设计自己的Agent时不要一开始就追求全自动和复杂。从一个定义清晰、边界明确的小任务开始精心设计一两个核心工具打磨好系统提示词跑通一个稳定的ReAct循环。这个最小可行产品MVP所带来的理解远比直接套用一个复杂框架要深刻得多。先让Agent可靠地完成一件小事再思考如何让它去做更多。