ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ReAct模式:AI Agent的核心循环,从原理到代码实现

ReAct模式:AI Agent的核心循环,从原理到代码实现 写 Prompt 和建 Agent看起来都是让大模型完成任务但真正的分水岭是模型到底是“想一想就回答”还是“边想边动手验证”。很多刚接触 AI Agent 的开发者都会有这种感觉。看了一堆概念知道 Agent 有规划、记忆、工具调用这些模块但真到自己写一个能查天气、能算数学、能操作数据库的 Agent 时却不知道从哪里下手也不知道“思考”和“执行”到底该怎么串起来。这篇文章要讲的 ReAct 模式就是解决这个问题的核心方法。它用最简单的思路把推理和工具调用交替串联构成了大量现代 AI Agent 的技术原型。读完这篇文章你会搞清楚三件事ReAct 到底是什么它和普通 Prompt、和 CoT 思维链的区别在哪里。为什么说 ReAct 是构建 AI Agent 绕不开的基础模式。怎么用几段代码把 ReAct 跑通以及在生产环境中落地时要注意哪些坑。1. 先搞清楚AI Agent 和普通 ChatBot 到底差在哪在聊 ReAct 之前必须先建立一个判断普通 ChatBot 和真正的 Agent本质上不是同一个东西。普通的 ChatBot工作方式是一问一答。模型收到用户的问题基于训练时学到的知识生成答案。它的能力边界就是“记忆中的知识”。如果问“今天北京天气怎么样”模型可能能背出北京的气候特点但绝对说不出此时此刻的温度因为它的知识有截止时间它也没有实时获取信息的能力。Agent 则不同。Agent 的关键能力是“能动性”。它可以主动调用工具去获取信息、执行操作再根据结果决定下一步做什么。举个例子。用户说“帮我查下今天北京能不能户外跑步”。普通 ChatBot 的做法是“北京今天晴空气质量良适合户外运动。”这个回答大概率是编的它根本没有查过天气。Agent 的做法是思考用户需要北京的实时天气和空气质量我需要调用天气查询工具。行动调用get_weather()工具拿到数据。观察空气质量指数是 45气温 18 度风力 2 级。思考这些条件适合户外运动。回答今天北京适合户外跑步。看到差别了吗Agent 不是凭记忆回答而是通过“思考 → 行动 → 观察结果 → 再思考”的循环逐步逼近正确答案。这个循环就是 ReAct 的核心机制。它的名字已经说明了一切Reasoning Acting推理与行动交替进行。2. 什么是 ReAct一句话版本和三段式解释ReAct 的全称是 Reasoning and Acting这个概念出自 2022 年底的一篇论文《ReAct: Synergizing Reasoning and Acting in Language Models》。这篇论文的核心观点是以前的研究要么让模型只推理CoT要么让模型只行动直接调用工具而 ReAct 把两者结合起来让模型在推理的过程中穿插行动观察环境的反馈再继续推理。用一句话概括 ReAct 的流程就是Thought思考→ Action行动→ Observation观察→ Thought思考→ Action行动→ …… → Answer回答这个循环里每一步的输入是上一步的输出。模型不是在“空想”而是在“实践”。拆开来看ReAct 有三个关键角色Thought模型的推理过程解释“我为什么要这么做”。Action具体的工具调用比如搜了一下、查了一下、算了一下。Observation工具返回的结果是环境给模型的反馈。很多新手会犯一个错误以为 ReAct 就是“调用工具”。这其实只看到了 Action 的部分。ReAct 的灵魂在于 Thought 和 Observation 之间的反馈回路。模型必须能读懂 Observation然后基于 Observation 生成下一步的 Thought而不是机械地执行一串预设好的工具调用。2.1 先打破一个迷思很多 Agent 并不是智能的这里必须泼一盆冷水。网上很多宣传把 Agent 说得神乎其神好像 AI 已经能自主规划、独立完成复杂任务了。但从技术实现上看大部分 Agent 并不是凭空多了“意识”而是套用了类似 ReAct 的循环结构。它的本质是让模型在每一步都做一个小的决策然后根据实际反馈修正方向。这是一种“局部最优”的堆叠但已经足够让模型在很多任务上表现得像“有计划”一样。这也解释了为什么 ReAct 值得学习它不是某个框架里的高级特性而是几乎所有 Agent 框架的底层逻辑。你学会 ReAct就掌握了理解 Agent 世界的钥匙。3. ReAct、CoT、Function Calling 的区别ReAct 概念虽然简单但特别容易和另外两个概念混淆CoT 和 Function Calling。这里用一个表格把它们分清楚。模式全称核心逻辑是否调用外部工具典型应用CoTChain of Thought让模型一步一步推理否数学题、逻辑推理Function Calling函数调用让模型从函数列表中选择要调用的函数并生成参数是但是单次稀疏调用结构化信息抽取、API 调用ReActReasoning Acting推理、行动、观察循环交替是多轮循环AI Agent、复杂任务自动化CoT 解决的是“怎么想”的问题它要求模型在回答前先把推理步骤写出来。比如解一道数学题CoT 会让模型先写“设未知数为 x列出方程”再一步步求解。它不接触外部世界。Function Calling 解决的是“怎么调用工具”的问题。给定一个函数列表模型需要决定调用哪个函数并给出参数。比如定义了一个get_weather(city)函数模型看到用户说“北京天气”就会输出一个结构化的请求告诉系统调用这个函数参数是“北京”。但 Function Calling 通常只执行一轮调用完就结束了。ReAct 解决的是“怎么把思考和调用串起来”的问题。它把 CoT 的推理能力和 Function Calling 的工具能力统一到一个循环里。模型不是调用一次工具就完事而是可以根据观察结果反复调用直到得出结论。举一个更具体的例子。假设用户问“北京和上海今天的温差是多少”CoT 的方式模型基于记忆中的气候数据给出一个估算比如“北京平均气温 10 度上海平均气温 18 度温差 8 度”。这个答案看起来合理但不是实时准确的。Function Calling 的方式模型决定调用get_weather(北京)拿到结果后再调用get_weather(上海)但这里需要写代码判断要不要第二次调用模型本身没有这个循环控制能力。通常需要在应用层写逻辑来处理多步调用否则就只调用一次就结束了。ReAct 的方式模型先 Thought“我需要查北京的天气”Action 调用工具Observation 拿到“北京气温 10 度”再 Thought“我还需要查上海的天气”Action 调用工具Observation 拿到“上海气温 18 度”最后 Answer“温差 8 度”。这里的关键区别有两个。一是循环ReAct 天然支持多轮循环二是决策能力ReAct 中模型可以根据观察结果动态决定下一步而不是提前设计好固定流程。4. 用生活场景理解 ReAct比如做一道菜技术概念如果只停留在定义上很容易读过就忘。我习惯用生活场景来类比这里用“做一道菜”来理解 ReAct。假设你要做一个红烧肉但没有完整菜谱只有一个模糊的目标。你的行动过程是这样的Thought要做红烧肉需要先准备材料。Action打开冰箱查看有什么材料。Observation有五花肉、姜、葱但没有冰糖。Thought没有冰糖可以用白糖代替。现在需要处理五花肉。Action把五花肉切块焯水。Observation肉表面变了颜色浮沫出来了。Thought看来焯水到位了可以开始炒糖色。Action下锅炒肉。Observation肉块已经上色但锅有点干。Thought需要加水没过肉转小火慢炖。Action加水调小火定时 40 分钟。Observation40 分钟后打开锅盖汤汁浓稠尝了一下味道偏淡。Thought需要加盐再加一点生抽提鲜。Action调味收汁出锅。Observation成品色香味俱全。Answer红烧肉完成。这个过程中每一步都是基于上一步的结果来决策的。你没有一次性规划好全部 20 个步骤而是“想一步、做一步、看一步”。如果锅干了你不会按原计划继续炖而是会调整加水量。这就是 ReAct 的思想只不过它把这个过程放到了大模型上。这个类比能解释为什么 ReAct 比单纯的 CoT 强大CoT 相当于“闭着眼睛想象做菜的过程”ReAct 则是“看着食材和锅里的状态动态调整每一步”。5. 为什么 ReAct 是 AI Agent 的主流范式从 2023 年开始AI Agent 赛道爆发各种框架层出不穷。LangChain、LangGraph、AutoGPT、BabyAGI、MetaGPT、Spring AI 这些框架底层逻辑虽然各有不同但最基础的“思考-行动-观察”循环几乎都有着 ReAct 的影子。ReAct 能成为主流范式有几个关键原因。第一它足够简单。不需要复杂的状态机不需要定义海量的状态流转规则只需要让模型在每一轮交替生成 Thought、Action、Observation 三段文本就能实现一个基本的 Agent。思路清晰工程实现成本低。第二它足够通用。不管是查数据库、调 API、读文件、执行代码还是多轮对话中需要查找上下文信息几乎任何需要模型“动手”的场景都可以用 ReAct 来组织。第三它符合大模型的训练方式。大模型本来就擅长文本生成ReAct 把推理过程、工具调用过程都变成了文本生成过程。模型的强项被充分利用了。当然ReAct 也有明显的局限。最典型的就是效率问题。因为每走一步都要调用一次大模型而大模型推理的延迟以秒计如果需要 10 步才能完成一个任务用户就要等 10 轮。这就是为什么现在很多框架开始研究如何让 Agent“少想几步”比如 Reflexion、Plan-and-Solve 这些变体本质上都是在 ReAct 的骨架上做优化。6. 动手实践用代码实现一个最小 ReAct Agent理论讲完接下来是这篇文章最值得收藏的部分用代码把 ReAct 跑通。考虑到很多读者可能还没有 OpenAI 的 API Key我们用两个方案来演示。第一个方案用 OpenAI API 的 Function Calling 能力模拟 ReAct第二个方案用纯 Python 加上一个简单的 LLM 接口做演示。如果你有本地部署的模型也可以替换成对应 API。6.1 前置准备本文不绑定具体版本以 OpenAl 的接口为例需要准备Python 3.9 环境。openaiPython 包安装方式pip install openai。一个可用的 LLM API KeyOpenAI 或兼容接口均可模型需要支持工具调用建议使用 gpt-4o 或更新的模型。网络能访问你使用的 API 服务。版本说明如果你使用的是其他家的模型API 路径和参数会略有不同但 ReAct 的循环逻辑是通用的。6.2 示例实现一个能查询天气和计算加减法的 ReAct Agent我们先定义两个虚拟工具一个查询天气一个做四则运算。然后让模型在 ReAct 循环中自主决定调用哪个工具。# 文件路径react_mini.py import json from openai import OpenAI # 初始化客户端API Key 也可以从环境变量读取 client OpenAI() # 1. 定义两个工具函数 def get_weather(city: str) - str: 模拟查询天气的工具 weather_data { 北京: 晴气温 10 度空气质量良, 上海: 小雨气温 18 度空气质量优, 广州: 多云气温 25 度空气质量良, } return weather_data.get(city, f没有 {city} 的天气数据) def calculator(expression: str) - str: 模拟计算器工具注意这里只用 eval 做演示生产环境不要这么写 try: result eval(expression) return str(result) except Exception as e: return f计算错误: {e} # 3. 工具描述列表供模型读取 tools [ { type: function, function: { name: get_weather, description: 查询指定城市的实时天气和空气质量, parameters: { type: object, properties: { city: { type: string, description: 城市名称例如北京, } }, required: [city], }, }, }, { type: function, function: { name: calculator, description: 计算数学表达式例如 3 5 * 2, parameters: { type: object, properties: { expression: { type: string, description: 数学表达式, } }, required: [expression], }, }, }, ] # 4. 执行工具调用的分发函数 def execute_tool_call(name: str, arguments: str) - str: 根据工具名称和参数调用对应的工具函数 args json.loads(arguments) if name get_weather: return get_weather(args[city]) elif name calculator: return calculator(args[expression]) else: return f未知工具: {name} # 5. ReAct 循环主逻辑 def react_agent(user_query: str, max_steps: int 5) - str: messages [{role: user, content: user_query}] for step in range(max_steps): print(f--- Step {step 1} ---) response client.chat.completions.create( modelgpt-4o, # 以实际可用模型为准 messagesmessages, toolstools, ) choice response.choices[0] message choice.message # 判断模型是否返回了工具调用请求 if message.tool_calls: # 把模型的输出追加到消息历史中 messages.append({ role: assistant, content: message.content, tool_calls: [ { id: tc.id, type: function, function: { name: tc.function.name, arguments: tc.function.arguments, }, } for tc in message.tool_calls ], }) # 依次执行每个工具调用 for tc in message.tool_calls: tool_name tc.function.name tool_arguments tc.function.arguments print(f Action: {tool_name}({tool_arguments})) tool_result execute_tool_call(tool_name, tool_arguments) print(f Observation: {tool_result}) # 将工具执行结果作为新消息返回给模型 messages.append({ role: tool, tool_call_id: tc.id, content: tool_result, }) else: # 模型没有调用工具说明已经可以直接回答 print(f Answer: {message.content}) return message.content return 已达到最大步数任务可能未完成。 if __name__ __main__: # 测试 1查询城市天气 print(react_agent(北京和上海的天气分别怎么样)) # 测试 2计算表达式 print(react_agent(计算 (23 17) * 2 的结果)) # 测试 3天气加计算 print(react_agent(北京上海温差是多少度))这里有几个关键的工程细节值得展开讲。第一messages列表是 ReAct 循环的记忆载体。每一轮的 assistant 消息和 tool 消息都会被追加进去保证模型在第 5 步还能记得第 3 步观察到了什么。如果没有这个历史消息模型就会“失忆”无法完成多轮推理。第二判断循环是否退出的条件是模型有没有返回tool_calls。当模型认为不需要再调用任何工具时它会直接返回最终答案文本。此时就可以把答案返回给用户了。第三max_steps是循环的安全上限。如果模型反复调用工具却无法得出结论或者工具调用链过长就强制退出。这是防止 Agent 无限循环的关键保护机制。6.3 如果要自己写核心循环而不依赖 OpenAI 的 Function Calling有些场景下你用的模型不支持 Function Calling或者你想完全掌控 ReAct 的 Prompt 格式。这时可以退回到最原始的 ReAct 实现方式让模型直接生成带格式的文本。思路是设计一个 Prompt要求模型按固定格式输出 Thought、Action、Action Input然后由代码解析这段文本执行工具再把 Observation 拼接回 Prompt。# 文件路径react_prompt_template.py import re REACT_PROMPT_TEMPLATE 你是一个智能助手需要根据用户的问题按照以下循环进行操作 Thought: 你需要决定下一步做什么直接输出你的思考。 Action: 你可以选择以下工具 - get_weather[城市名]: 获取指定城市的天气 - calculator[数学表达式]: 计算表达式的值 Action Input: 执行工具所需的输入。 当观察到结果后如果问题已经被解决直接输出 Answer: 最终答案 请开始。 Question: {question} def parse_react_response(text: str): 解析模型输出的 Thought 和 Action thought_match re.search(rThought: (.), text) action_match re.search(rAction: (.?)\[(.?)\], text) if thought_match: print(f Thought: {thought_match.group(1)}) if action_match: return action_match.group(1), action_match.group(2) return None, None这个纯 Prompt 的做法在 2023 年很常见现在一些轻量级框架里仍然能看到类似的实现。它的好处是不依赖模型的工具调用特性任何文本对话模型都能跑坏处是对格式解析的稳定性要求高模型偶尔会输出不规范内容导致解析失败。6.4 运行和验证运行代码前先确认环境变量里已经配置了OPENAI_API_KEY。export OPENAI_API_KEY你的_key python react_mini.py预期输出大致如下--- Step 1 --- Action: get_weather({city: 北京}) Observation: 晴气温 10 度空气质量良 --- Step 2 --- Action: get_weather({city: 上海}) Observation: 小雨气温 18 度空气质量优 Answer: 北京晴、气温 10 度空气质量良上海小雨、气温 18 度空气质量优。两地温差 8 度。如果运行失败第一件事是看报错内容。常见情况是 API Key 配置错误、网络不通、模型名写错。这些错误信息通常很直白顺着改就行。判断成功的关键标准是模型的输出里确实出现了多个 Action且每个 Action 之间隔着一次 Observation。这说明模型是根据环境反馈在动态决策而不是一次性生成了一堆操作。7. 生产环境中的 ReActLangGraph 和 Spring AI 的选择刚才的示例是最小实现适合理解原理。放到真实项目中直接用裸代码搭建 ReAct 会面临几个问题没有状态持久化、没有并发控制、没有人工干预机制、没有流程可视化。这就解释了为什么 LangGraph、Spring AI 这类框架会流行。它们把 ReAct 循环变成了标准化的框架组件。以 LangGraph 为例它用图结构来管理 Agent 的流程。你可以把“思考”和“行动”定义成两个节点节点之间的边就是 ReAct 循环。LangGraph 还支持 checkpoint 机制可以把 Agent 的每一步执行状态保存下来任务中断后可以从上次的 Observation 继续。这对长时间运行的任务很关键比如一个爬取数据的 Agent 跑了 3 个小时中间网络断了如果支持 checkpoint重新启动后就能续上。以 Spring AI 为例它给 Java 生态提供了 Agent 能力。过去 Java 后端想集成 AI Agent代码又长又绕Spring AI 把工具调用、对话记忆、模型配置都做了统一抽象Java 开发者可以像写 Spring Web 应用一样写 Agent。框架没有绝对的好坏关键是场景如果你在做技术验证、学习原理裸代码 ReAct 就够了代码量不到 100 行。如果你在做生产应用且任务流程相对固定建议用成熟框架至少能帮你省下状态管理和错误恢复的精力。如果你需要工作流可视化、人工审核环节、多 Agent 协同必须选图结构的编排框架比如 LangGraph。8. 常见误区和排查方法ReAct 看起来简单实际开发中踩坑的人不在少数。我总结了几类高频问题。现象可能原因排查方式解决方案Agent 死循环不停调用同一工具工具返回的结果没有帮助模型收敛模型可能陷入了固定模式打印每次的 Thought观察模型为什么反复调用增加最大步数限制优化工具返回的文本让反馈更明确Agent 没有调用工具直接回答工具描述不清晰模型没有理解工具适用场景模型本身不支持工具调用检查 tools 参数是否传入检查工具 description 是否具体优化工具描述写明“什么时候用这个工具”换用支持工具调用的模型工具返回结果太长超出上下文观察结果包含了大量无关内容检查工具返回内容设置截断逻辑对工具返回做摘要或截断只保留关键字段多轮调用后历史消息过大每次循环都追加 assistant 和 tool 消息检查 messages 列表长度使用长上下文模型定期对历史做摘要压缩模型无法正确解析工具参数参数 schema 定义不严谨检查 parameters 里的 required 和 type增加参数校验给 description 写清格式要求这里特别提醒一点工具描述的质量直接决定 Agent 的效果。很多开发者把工具描述写得非常潦草比如“天气查询”结果模型根本不知道应该什么时候用这个工具。正确做法是写清楚使用场景和参数含义比如“当用户询问某个城市的天气、温度、空气质量时使用。城市必须是中文名称例如北京”。9. 生产环境落地的几条工程建议如果你准备把 ReAct 模式应用到真实业务下面这几个建议值得收藏。第一永远给 Agent 设置预算上限。预算可以是步数上限也可以是 token 上限还可以是成本上限。没有预算控制的 Agent可能在一次任务中消耗大量的调用次数和费用。建议在生产环境里同时设置步数上限和费用告警。第二工具是 Agent 的边界也是安全边界。Agent 能做什么完全由你提供的工具决定。不要给 Agent 开放执行任意代码、删除数据、修改生产配置的权限除非有严格的安全审批流程。工具函数里一定要做参数校验和权限校验Agent 传进来的参数不能直接信。参考上面示例里的eval我特意标注了“生产环境不要这么写”就是这个原因。第三日志和可观测性非常重要。每轮 Thought、Action、Observation 都应该记录到日志系统。Agent 出问题时如果没有完整的执行日志排查会非常痛苦。建议使用结构化日志把工具名称、调用参数、返回结果、耗时都记录下来。第四要有“人在回路”的兜底机制。有些任务影响重大比如发送邮件、下单、删除数据。这类操作应该设计成 Agent 先输出意图由人工确认后再执行。ReAct 本身不包含人工确认环节需要在业务代码中单独实现。第五不要盲目追求长链条推理。ReAct 每多走一步都会增加延迟和费用。如果一个问题两步就能解决就不要让模型绕三条路。你可以在任务设计时把复杂任务拆解成多个小 Agent而不是一个 Agent 干到底。10. 总结与下一步实践建议这篇文章从概念、原理、代码到工程实践把 ReAct 讲了一遍。现在回头看ReAct 的核心其实就是一个简单的循环思考、行动、观察再思考。但就是这个看似简单的循环构成了 AI Agent 的一个稳定范式。不要被“Agent”这个词吓到。你写完那个 100 行的最小 ReAct 示例之后就已经具备了一个 Agent 雏形。接下来可以往几个方向继续深入尝试让 ReAct 调用真实 API比如真实的天气 API、数据库查询 API。研究 ReAct 的变体比如 Reflexion带反思机制的 ReAct、Plan-and-Solve先规划再执行理解它们在解决什么问题。尝试把一个成熟框架跑起来比如 LangGraph 的 ReAct 示例感受框架带来的工程化收益。给自己设计一个综合任务比如“查公司某员工上周的加班时长并生成报表”让 Agent 调用多个真实业务工具完成。代码可以收藏备用但更重要的是动手把循环逻辑跑一遍。只有亲眼看到模型在 Thought、Action、Observation 之间来回切换才真正理解 Agent 是怎么“思考”和“行动”的。
返回列表