1. 从“指令执行”到“自主思考”为什么ReAct是智能体的分水岭最近和几个做AI应用的朋友聊天发现一个挺有意思的现象大家手头都有了大模型都能调API但做出来的东西差距却很大。有的产品你问一句它答一句像个高级版的搜索引擎而有的产品你给一个模糊的目标它却能自己规划、执行、纠错最后把事儿给你办成了。这背后的关键往往不在于模型本身有多强而在于你给这个“智能体”装了一个什么样的“大脑”。这个“大脑”的核心运作模式就是ReAct。你可能在各种技术博客里见过这个词但很多人对它的理解还停留在“思考-行动”这个简单的字面意思上。今天我想和你深入聊聊为什么ReAct三步循环Reasoning, Acting, Observing是让Agent智能体真正“能自己干活”的灵魂而不仅仅是又一个花哨的学术概念。简单来说ReAct解决了一个根本性问题如何让一个基于大语言模型的程序从被动的“答题机器”转变为主动的“问题解决者”。传统的提示工程更像是我们手把手地教模型“第一步做什么第二步做什么”模型只是照章办事。一旦遇到计划外的情况它就懵了。而ReAct赋予了智能体一种动态的、与环境交互的认知能力。它让智能体学会在行动前“想一想”Reasoning根据思考结果“动动手”Acting然后“看一看”结果如何Observing再基于观察进行下一轮的思考。这个循环模拟了人类解决问题最朴素也最有效的方式。举个例子你让一个传统提示的模型“帮我查一下北京明天飞上海的航班并预订最便宜的那一班”。模型可能会直接调用搜索工具返回一堆航班列表然后就结束了。它不会去判断这些航班是否真的符合“最便宜”的条件可能包含了昂贵的高铁票也不会去检查是否有特价舱位更不会在预订失败时尝试其他方案。而一个基于ReAct框架构建的智能体则会先推理“用户要订最便宜的航班我需要先获取所有航班信息然后按价格排序筛选出真正的机票最后尝试预订。如果第一次预订失败可能是座位已售罄我需要尝试价格次优的航班。” 然后它才会执行一系列动作并在每一步观察结果动态调整策略。所以当我们说“Agent能自己干活”本质上是在说它具备了在开放环境中通过试错和学习逐步逼近并完成复杂目标的能力。ReAct就是这个能力的实现框架。接下来我会拆解这个三步循环的每一个环节看看它们具体是如何工作的以及在实际构建中我们需要注意哪些“坑”。2. ReAct循环拆解Reasoning推理—— 智能体的“内语音”推理环节是ReAct循环的起点也是智能体区别于简单工具调用的核心。你可以把它理解为智能体的“内心独白”或“思维链”。这个环节的目标是基于当前的任务目标、已有的历史信息包括之前的观察和行动以及可用的工具规划出下一步最应该做什么。这个“规划”不是天马行空的幻想而是一个结构化的决策过程。在技术实现上我们通常通过精心设计的提示词Prompt引导大模型生成一段包含以下关键元素的文本任务状态评估总结当前已经知道什么完成了什么距离最终目标还有多远。下一步行动分析分析为了推进任务现在有哪些可行的选项。例如是继续搜索更具体的信息还是可以开始执行某个操作工具选择与参数确定如果决定行动具体调用哪个工具如search_web,execute_code,send_email以及调用时需要传入什么参数。预期与风险判断对即将采取的行动可能产生的结果有一个初步预判并识别潜在风险比如这个API可能失败那个网站可能没有所需信息。一个高质量的推理输出看起来应该是这样的思考用户想了解特斯拉最新车型的续航里程。我已经通过之前的搜索知道目前的最新车型是Model 3焕新版。但我还没有获取到官方的、具体的续航数据。我手头有网络搜索工具和特斯拉官网查询工具。为了获得最权威的数据我应该优先使用官网查询工具。如果官网工具返回失败或信息不全我再启用通用网络搜索作为备选。接下来我将调用query_tesla_website工具参数为model: “Model 3 Refresh”, info_type: “range”。这里的关键在于推理的输出必须严格遵循预设的格式并且要“自我说服”。它不能只是一个干巴巴的指令“调用A工具”而必须清晰地展示出从现状分析到决策的完整逻辑链条。这样做有两个巨大好处可调试性当智能体行为异常时我们可以直接查看它的“思考过程”快速定位是推理逻辑有误还是工具调用出了问题。这比黑盒调试要高效得多。稳定性提升强制模型进行结构化思考能有效减少其“幻觉”和随机性输出让智能体的行为更加可控、可预测。在实际编码中推理环节通常是一个独立的函数或模块它接收当前的对话历史、任务状态和可用工具列表然后构造提示词调用大模型并解析其返回的文本提取出结构化的“思考”内容和下一步的“行动意图”。注意推理的深度和广度需要根据任务复杂度进行权衡。对于简单任务过度的推理反而会降低效率并增加Token消耗。我们的设计原则是让智能体思考“足够”下一步行动的信息而非思考整个任务的完整剧本。3. ReAct循环拆解Acting行动—— 从思考到实践的桥梁推理环节产生了“做什么”的意图行动环节就是负责“动手做”。这是智能体与外部世界或内部系统交互的唯一途径。行动通常体现为调用一个预定义的工具Tool或技能Skill。工具的本质是一个个封装好的函数它们可以获取信息如search_web(query),get_weather(city),read_file(path)。执行操作如send_email(to, subject, body),execute_python_code(code),click_button(element_id)。进行计算或决策如calculate(expression),compare_prices(item_a, item_b)。行动环节的技术实现相对直接解析推理环节输出的“行动意图”找到对应的工具函数传入参数然后执行它。但这里藏着几个容易踩坑的细节3.1 工具的描述与匹配大模型本身并不知道你有什么工具。你需要为每个工具提供一个清晰、准确的自然语言描述。例如对于search_web工具描述可能是“在互联网上搜索相关信息并返回最相关的几条摘要。参数query字符串搜索关键词。” 在推理时这些描述会被拼接到提示词中模型才能知道“哦我有个工具叫search_web可以用来找东西”。工具描述的清晰度直接决定了模型能否正确选择和使用它。3.2 参数的验证与安全模型生成的参数是文本直接传给工具函数可能引发错误或安全风险。例如模型可能生成execute_python_code(“import os; os.system(‘rm -rf /’)”)。 因此行动层必须包含参数验证和清洗逻辑类型检查确保字符串参数不会传给需要数字的接口。格式校验对于日期、URL等有固定格式的参数进行校验。安全过滤对于执行代码、访问文件系统、操作数据库等高风险工具必须进行严格的白名单限制或沙箱隔离。绝对不能让模型生成的代码拥有不受限制的系统权限。3.3 行动的原子性与容错一个好的工具设计应该是“原子化”的即一个工具只完成一件明确、独立的事情。避免设计功能过于复杂、参数繁多的“巨无霸”工具。原子化工具有利于模型的正确调用和错误定位。 同时行动函数内部必须有完善的异常处理Try-Catch机制。网络超时、API限流、资源不存在……这些错误必须被捕获并转化为一个结构化的错误信息传递给下一个环节——观察Observing。一个简单的失败信息如{“status”: “error”, “message”: “Network timeout when calling search API.”}远比一个未处理的异常崩溃更有用。4. ReAct循环拆解Observing观察—— 智能体的“感官”与反馈观察环节是最容易被轻视但实则至关重要的部分。它负责接收行动执行后的结果无论是成功的数据还是失败的异常并将其转化为一段可供下一轮推理使用的“观察”文本。观察的输出不是简单的数据转发而是信息的提炼与格式化。它的目标是将原始的、可能很冗长或杂乱的工具返回结果加工成一段简洁、信息量集中、对后续推理有帮助的描述。4.1 处理成功结果假设搜索工具返回了一个包含10条结果的JSON数组。直接把这个JSON扔给模型去推理会浪费大量Token且干扰重点。观察环节应该做摘要原始结果[{“title”: “A”, “snippet”: “…”}, {“title”: “B”, “snippet”: “…”}, …]提炼后的观察“根据网络搜索关于‘特斯拉Model 3续航’的信息较多。其中第一条提到‘国标续航为606公里’第二条提到‘实际续航受温度影响较大’。未直接找到官网标称的最新准确数据。”看到区别了吗提炼后的观察指出了关键信息606公里指出了不足非官网数据并给出了现状判断未找到目标这直接引导下一轮推理思考“是否需要换用官网工具进行精确查询”。4.2 处理失败与异常当行动失败时观察环节传递的信息更是决定了智能体能否“迷途知返”。糟糕的观察“工具调用失败。”信息量为零模型不知道接下来该怎么办良好的观察“调用query_tesla_website失败返回错误码404提示‘页面未找到’。可能车型名称不准确或官网API路径已变更。”后一种观察明确告诉了模型1) 行动失败了2) 失败原因是4043) 提供了可能的原因推测。这会让模型在下一轮推理中考虑“是不是我用的车型名称不对让我再确认一下准确的车型命名或者尝试用‘Model 3’而不是‘Model 3焕新版’去查询。”4.3 观察的历史管理ReAct是一个循环所以智能体拥有“记忆”。观察的结果连同对应的推理和行动需要被有序地记录到对话或任务历史中。这个历史上下文是每一轮推理时最重要的输入之一。 管理好这个历史上下文涉及两个权衡长度限制大模型有上下文窗口限制。不能无限制地堆积历史。需要设计策略例如只保留最近N轮循环或对早期历史进行摘要压缩。信息完整性不能为了压缩而丢失关键决策节点。例如之前某次尝试失败的原因对于避免后续重蹈覆辙至关重要必须保留。观察环节实际上是为智能体装上了“感官”和“短期记忆”让它能感知环境反馈并基于反馈学习调整。一个对反馈不敏感的智能体就像蒙着眼睛在迷宫里乱撞。5. 构建ReAct智能体的实战架构与核心代码模式理解了理论我们来看看如何用代码把它搭起来。这里我给出一个高度精简但核心逻辑完整的Python示例它不依赖任何特定的Agent框架如LangChain以便你能看清本质。import json # 假设我们有一个大模型调用函数这里用伪代码表示 def call_llm(prompt): # 调用OpenAI GPT、Claude、国产大模型等 # 返回模型生成的文本 pass # 1. 定义工具 tools { “search_web”: { “function”: lambda query: f”模拟搜索‘{query}’的结果相关文章A相关文章B” # 实际这里调用真实API “description”: “在互联网上搜索相关信息。参数query搜索关键词” }, “get_current_time”: { “function”: lambda: “北京时间2023-10-27 15:30:00”, “description”: “获取当前的日期和时间。无需参数。” } } # 2. ReAct核心循环函数 def react_agent(initial_task, max_steps10): history [] # 记录每一步的推理、行动、观察 current_state f“任务{initial_task}” for step in range(max_steps): # 第一步推理 (Reasoning) reasoning_prompt f“”” 你是一个智能助手。你的目标是{initial_task} 当前已知情况{current_state} 你可以使用的工具{json.dumps([{k: v[‘description’]} for k, v in tools.items()], ensure_asciiFalse)} 请根据以上信息思考下一步应该做什么。你的回答必须严格遵循以下JSON格式 {{ “thought”: “你的详细思考过程分析现状和下一步计划” “action”: “工具名称如果无需行动则为 null”, “action_input”: “工具的输入参数如果无需行动则为 null” }} “”” response call_llm(reasoning_prompt) try: decision json.loads(response) # 解析模型输出的JSON thought decision.get(“thought”, “”) action_name decision.get(“action”) action_input decision.get(“action_input”) except json.JSONDecodeError: # 模型没有返回合法JSON记录为观察错误 observation “模型回复格式错误无法解析。” history.append({“step”: step, “error”: observation}) current_state f“\n步骤{step}错误{observation}” continue history.append({“step”: step, “thought”: thought, “action”: action_name, “action_input”: action_input}) # 检查任务是否在思考中已完成例如模型可能直接给出答案 if action_name is None or action_name “null”: print(f“任务可能在步骤{step}完成。最终思考{thought}”) break # 第二步行动 (Acting) if action_name not in tools: observation f“错误未知工具 ‘{action_name}’。可用工具{list(tools.keys())}” else: try: # 执行工具调用 tool_func tools[action_name][“function”] # 这里可以加入参数清洗和验证 result tool_func(action_input) if action_input else tool_func() observation f“调用工具‘{action_name}’成功。结果{result}” except Exception as e: observation f“调用工具‘{action_name}’时发生异常{str(e)}” # 第三步观察 (Observing) - 这里我们简单地将结果格式化 history[-1][“observation”] observation # 记录到历史 current_state f“\n步骤{step}{thought}\n行动{action_name}({action_input})\n观察{observation}” # 简单判断如果观察结果中包含了明显的问题答案可以提前结束 if “答案” in observation or “result is” in observation.lower(): # 根据你的任务定义结束条件 print(f“任务在步骤{step}完成。”) break return history # 3. 运行智能体 task “查一下今天的日期然后搜索‘什么是ReAct框架’” trace react_agent(task) print(“执行轨迹”) for step in trace: print(json.dumps(step, indent2, ensure_asciiFalse))这个示例包含了ReAct的核心骨架工具注册将功能函数和描述封装起来。提示工程构造引导模型进行结构化推理输出JSON的提示词。循环引擎依次执行推理解析JSON、行动安全调用工具、观察记录结果三步。状态管理将每一步的结果累积到current_state中作为下一轮推理的上下文。在实际项目中你需要在此基础上增强更强大的提示词、更鲁棒的JSON解析使用模型的功能调用特性更好、更复杂的工具集、历史窗口管理、以及更智能的任务终止判断逻辑。6. 超越基础循环高级模式与实战调优心得基本的ReAct循环能解决很多问题但对于复杂、长周期的任务我们还需要更高级的模式和优化技巧。6.1 分层规划与子任务分解对于“策划一场线上发布会”这样的宏大任务智能体很难一步规划到位。这时需要引入规划器Planner。规划器在ReAct循环之上工作先将大任务分解为一系列有序的子任务如“确定主题”、“邀请嘉宾”、“准备物料”、“技术测试”每个子任务再由一个独立的ReAct智能体或同一个智能体的不同阶段去执行。这模仿了人类项目经理的工作方式。6.2 反思Reflection机制这是ReAct的一个强大扩展。在每轮循环或关键节点后让智能体进行一次“复盘”反思提示“回顾你到目前为止的所有行动和观察。你的策略有效吗有没有走弯路基于现有信息是否有更优的下一步计划” 这能让智能体从错误中学习及时调整策略避免在死胡同里浪费步数。例如如果连续三次搜索都找不到想要的信息反思可能会让它意识到“关键词需要调整”或“应该换一个数据源”。6.3 工具学习的模糊匹配模型有时会生成与工具名称不完全匹配的行动指令。比如工具叫search_web模型可能生成action: “search_the_internet”。严格的字符串匹配会导致失败。我们可以引入模糊匹配或嵌入向量相似度计算让系统能理解“search_the_internet”和“search_web”指的是同一个工具从而提高智能体的鲁棒性。6.4 控制循环成本与超时ReAct循环依赖多次调用大模型成本Token消耗和耗时是必须考虑的因素。设置最大步数max_steps防止智能体陷入无限循环。关键信息摘要在将历史观察加入上下文时不要简单拼接所有原始文本而是用一个小模型或规则对过往观察进行摘要只保留关键决策点和最新信息。并行行动探索对于某些可以并行尝试的行动例如同时查询多个信息源可以在单轮推理中规划多个行动然后并行执行再合并观察提升效率。我的几点实战心得提示词是方向盘推理环节的提示词质量决定了智能体的“思考质量”。花最多的时间去迭代和优化你的核心提示词模板。让它明确任务边界、输出格式和思考风格。工具设计是基石工具要尽可能原子化、可靠、有清晰的错误返回。一个经常崩溃或返回混乱数据的工具会迅速导致整个智能体失控。观察要提炼不要转储永远不要直接把API返回的原始JSON或HTML丢给模型。一定要有一个“观察处理器”来提炼信息。这是提升智能体效率最有效的手段之一。从简单任务开始验证不要一开始就让人工智能去处理“优化公司财报”这种任务。从“查天气并判断是否需要带伞”开始确保基础循环跑通再逐步增加复杂度。日志记录至关重要完整记录每一步的推理、行动、观察。这是你调试和优化智能体的唯一依据。可视化这些日志你能清晰地看到智能体的“决策轨迹”。ReAct不是一个僵化的公式而是一个强大的设计范式。它为我们提供了一种将大语言模型的推理能力与外部工具的执行能力有机结合的方法。理解并掌握这个三步循环你就掌握了构建真正能够自主完成复杂任务的智能体的钥匙。剩下的就是根据你的具体业务场景去设计工具、打磨提示词、优化循环逻辑让你的智能体从“听话”变得“能干”。