行业资讯
📅 2026/8/28 6:48:53
ReAct模式解析:大模型如何通过思考与行动协同完成复杂任务
大模型本身是“回答问题”的高手但在“完成复杂任务”这件事上却经常捉襟见肘。你问它“北京今天适合带伞吗”它能给出一个听起来合理但可能是编造的回答你让它帮你对比三份方案并给出结论它往往只在文字层面打转既不会主动去查最新数据也不会发现自己的推理已经偏离事实。真实世界里的任务哪是只靠一次问答就能完成的这正是 ReAct 出现的契机。ReAct 不是某个前端框架而是一套让大模型像人一样“先思考、再行动、看结果、再思考”的工作模式。它的名字来自 Reasoning Acting直译过来就是“推理与行动协同”。自 2022 年底论文《ReAct: Synergizing Reasoning and Acting in Language Models》发表以来这个概念已经成了 AI Agent 领域最基础、也最常被引用的控制流范式之一——今天绝大多数 Agent 应用底层都能看到 ReAct 的影子。这篇文章的目标很简单用最短时间帮你建立对 ReAct 的正确认知然后给出一份可以直接复制运行的最小代码。读完你会明白三件事ReAct 和 React 框架到底有什么关系它和 CoT、RAG、Plan-and-Execute 这些概念的区别在哪里以及不依赖重量级框架怎么用朴素代码手写一个最小可用的 ReAct Agent。如果你想快速判断这个模式适不适合自己的项目读第 1 到第 4 节就够了想动手实践直接跳到第 5、6 节。1. 先澄清ReAct 不是 React 框架很多第一次接触 ReAct 的开发者都会把它和前端框架 React 搞混。这不能怪大家因为从拼写上看ReAct 和 React 只差了一个字母的大小写。你去搜索引擎里搜“ReAct 入门”前面几页大概率是 JSX、组件生命周期、虚拟 DOM 这类前端教程。中文语境下这两者的名字几乎无法区分给学习 AI Agent 的人造成了不小的困扰。从词源上就能看出本质区别。React 是 Facebook 开发的 UI 库因为“构建响应式界面”而得名ReAct 则来自学术论文它的初衷是让语言模型在推理Reasoning和行动Acting之间来回切换从而解决单纯问答回答不了的问题。一个是前端渲染方案一个是 Agent 控制流模式根本不在同一个技术层次上。如果你在面试中被问到 Agent 相关内容提到的 ReAct 通常都是论文里的这个模式而不是前端框架。建议无论如何都先确认对方指的是哪一个避免鸡同鸭讲。React 前端框架解决的是“界面怎么渲染”ReAct 解决的是“智能体要怎么一步步完成任务”两者唯一的共同点就是名字看起来很像。2. 为什么需要 ReAct传统提示词方法的三个瓶颈在 ReAct 提出之前用大模型做任务有三种常见思路但它们各自都有明显短板。第一种是普通的少样本提示Few-shot Prompting。给模型几个示例让它照着格式回答。这种方式适合单轮问答但模型一旦遇到没见过的问题很容易一本正经地给出错误答案。第二种是思维链Chain-of-ThoughtCoT让模型先把推理过程写出来再给出最终结论。这一步能提升数学题、逻辑题的正确率但本质上仍然是“闭卷答题”——模型从头到尾都在自己脑补没有外部信息进来也没有纠错机制。第三种是先写一段计划再让模型按计划执行看起来更接近真实工作但计划一旦出错模型往往没有能力在执行中途修正。这三条路线共同的问题是大模型只有“思考”这一根支柱没有“行动”。它不会主动查资料不会调用工具也不会因为中途发现信息错误而停下来调整。而 ReAct 的突破点恰恰在这里它把“思考”和“行动”放到同一个循环里模型可以一边推理一边选择调用外部工具拿到工具返回的真实结果后再继续推理。打个比方CoT 是闭卷考试时在草稿纸上打草稿ReAct 是允许你开卷考试并且可以随时翻书、查资料、做完一步再回头改草稿。后者显然更接近人类解决复杂问题的过程也正是真实业务对 Agent 的期待。3. ReAct 核心机制Thought / Action / Observation 循环ReAct 的整个循环可以浓缩成一句话遇到任务时按照“思考 → 行动 → 观察 → 再思考”的顺序推进直到得到最终答案。这个流程可以表示为Thought → Action → Observation → Thought → Action → Observation → …… → Final Answer其中三个关键组件分别承担不同职责。Thought思考是模型对当前状态的分析作用是决定下一步应该做什么。这一步不需要特别复杂但必须让后续动作有依据。Action行动是模型选择要调用的工具比如“搜索天气”“查询数据库”“执行某个内部 API”。模型输出的 Action 通常需要附上一个 Action Input也就是给工具的输入参数。Observation观察是工具执行后返回的结果模型拿到这个外部反馈之后再进入下一轮 Thought。如此反复直到模型判断信息已经足够才输出 Final Answer 结束循环。拿最开始的问题举例。用户问“北京今天适合带伞吗”一个 ReAct Agent 的完整过程可能是这样第一轮模型先推理用户想知道北京天气情况我还没有数据所以先查天气。于是输出Thought: 我需要先查询北京的天气信息再判断是否需要带伞。 Action: search Action Input: 北京天气工具返回Observation: 北京今天晴最高气温25摄氏度最低气温12摄氏度。模型看到这个结果再进入第二轮今天是晴天没有降雨所以不需要带伞如果白天在户外时间长可以提醒注意防晒。于是输出Thought: 查询结果显示北京今天是晴天没有降雨迹象。 Final Answer: 北京今天晴不需要带伞户外时间长的话建议注意防晒。整个过程中模型每轮只推进一小步而且每一步的推理都留下了文字轨迹。这就是 ReAct 的重要价值它不止让智能体“会做事”还让做事过程“可解释、可审计、可排查”。一旦最终结果出错你能顺着推理轨迹找到是哪一步决策失误这是黑盒式单轮问答做不到的。还有一个容易被忽略的优势因为行动能带来外部事实模型可以在不确定时主动选择“检索”而不是“编造”这在降低幻觉上会比纯粹脑补稳定不少。当然它不能根治幻觉——如果工具本身返回了错误信息模型依然可能基于错误继续推理这一点后面在工程建议里会再提到。4. 与主流方案对比CoT、RAG、Plan-and-Execute 到底什么关系很多初学者会把 ReAct 和另外几个高频概念搞混这里用一张表直接对比。方法核心动作是否可以调用工具是否在执行中调整典型场景CoT仅推理否否数学题、逻辑推理题RAG先检索再生成仅一次检索否知识库问答、文档问答ReAct推理 行动循环可以多轮调用是多步任务、实时信息查询、工具编排Plan-and-Execute先规划再执行可以是通常否步骤明确、可预先拆解的任务CoT 最接近“想一想再回答”适合不需要外部信息的纯推理任务。RAG 则多了一个“查资料”的动作但通常只查一次查到结果后直接生成答案不会因为答案不完整而再次检索。ReAct 更像是 RAG 的加强版——它把“检索”变成一个可复用的行动并且允许模型在一轮检索之后发现问题再决定是否进行第二轮。Plan-and-Execute 和 ReAct 的差别更微妙。前者先让模型生成一个完整计划再按顺序执行执行过程中一般不重新规划ReAct 则是边执行边看结果每走一步都重新评估下一步。打个比方Plan-and-Execute 是出发前规划好所有路线的自驾游ReAct 是边走边查地图、遇到封路随时掉头的自由行。对天气变化、信息不确定的场景ReAct 的容错能力明显更强但对步骤非常明确、几乎没有意外的任务Plan-and-Execute 的成本通常更低。现实项目中ReAct 和 RAG 不是只能二选一。一个常见的组合是把“检索知识库”包装成 ReAct 的一个工具这样模型可以根据用户问题的复杂度决定要不要检索、检索几次、还需要检索什么。你可以把它理解成 RAG 是插在 Agent 手里的一个工具包而 ReAct 是使用工具包的决策流程。5. 环境准备你需要哪些依赖和 API Key这一节我们准备写代码。为了让示例足够“朴素”不依赖 LangChain 或 LlamaIndex 这类重量级 Agent 框架直接用 OpenAI 兼容接口手写循环。这样做的好处是你能看到 ReAct 的全部骨骼而不是被框架封装的黑盒。环境准备清单如下Python 3.9 或更高版本。安装 OpenAI Python SDK。终端执行pip install openai一个支持 OpenAI 兼容接口的大模型 API Key并配置环境变量。export OPENAI_API_KEY你的APIKey如果你的模型服务商提供了自定义接口地址也可以额外配置 base_url。比如某些国内云厂商提供 OpenAI 兼容接口可以把服务地址设置成对应的 base_url。没有 OpenAI 官方账号完全不影响学习关键是这个 API 必须支持 Chat Completions 格式。需要提醒的是如果你手头暂时没有可用的 API Key依然可以先把代码写好用一个假的 Key 跑一遍观察模型输出位置的报错流程。真正的 ReAct 循环逻辑与具体 Key 无关后续换成可用 Key 即可。另外由于 ReAct 每次循环都要调用一次大模型接口务必注意 API 服务的计费规则建议先在测试环境用最小模型跑通。6. ReAct 最小实现用 OpenAI 兼容 API 手写 Agent 循环下面这份代码是本文的核心示例。它没有使用任何 Agent 框架而是自己维护一段对话消息列表按照 ReAct 协议循环调用大模型。# 文件路径react_demo.py import os import re from openai import OpenAI client OpenAI( api_keyos.environ.get(OPENAI_API_KEY, sk-你的APIKey), base_urlos.environ.get(OPENAI_BASE_URL, https://api.openai.com/v1), ) SYSTEM_PROMPT 你是一个通过“思考-行动-观察”循环来完成任务的 AI 助手。 你必须严格遵守以下协议 1. 每一轮先输出 Thought: 用一句话描述你当前的推理。 2. 如果你需要调用工具继续输出 Action 和 Action Input。 3. 拿到工具返回的 Observation 结果后继续进入下一轮推理。 4. 当你确信已经得到最终答案直接输出 Final Answer: 最终答案。 你可以使用的工具如下 - search(query): 返回一段与 query 相关的本地信息。 - calculator(expression): 计算一个数学表达式并返回结果。 输出示例 Thought: 我需要先查出目标城市的天气数据。 Action: search Action Input: 北京天气 Observation: 北京今天晴25摄氏度。 Thought: 我现在得到了足够信息可以给出答案。 Final Answer: 北京今天晴25摄氏度。 TOOL_TABLE { 北京天气: 北京今天晴最高气温25摄氏度最低气温12摄氏度无降雨。, 上海天气: 上海今天多云最高气温23摄氏度最低气温16摄氏度午后可能有阵雨。, } def search(query: str) - str: 教学演示工具返回本地字典中的数据。 if query in TOOL_TABLE: return TOOL_TABLE[query] return f没有找到与‘{query}’相关的数据请尝试更精确的关键词。 def calculator(expression: str) - str: 教学演示工具计算数学表达式。生产环境请勿直接使用 eval。 try: result eval(expression, {__builtins__: {}}, {}) return str(result) except Exception as e: return f计算出错: {e} def run_agent(user_input: str, max_steps: int 6) - str: messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_input}, ] for step in range(max_steps): response client.chat.completions.create( modelgpt-4o-mini, messagesmessages, temperature0, ) answer response.choices[0].message.content print(f Step {step 1} ) print(answer) print() if Final Answer: in answer: return answer.split(Final Answer:, 1)[1].strip() action_match re.search(rAction:\s*(\w), answer) input_match re.search(rAction Input:\s*(.), answer) if action_match and input_match: action action_match.group(1) action_input input_match.group(1).strip() if action search: observation search(action_input) elif action calculator: observation calculator(action_input) else: observation f未知工具: {action} messages.append({role: assistant, content: answer}) messages.append({role: user, content: fObservation: {observation}}) else: messages.append({role: assistant, content: answer}) messages.append({role: user, content: 请严格按协议输出 Thought/Action/Action Input/Final Answer。}) return 达到最大步数未得到最终答案。 if __name__ __main__: result run_agent(北京今天天气怎么样如果带伞合适吗) print(最终答案:, result)这段代码的关键逻辑可以拆成四步看。第一步维护 messages 列表。这是 ReAct 循环的数据基础每轮都会把模型输出和工具观察结果追加进去让模型在下一次调用时能看到完整的执行历史。第二步调用大模型。用 chat.completions.create 让模型按照 system prompt 给定的 ReAct 协议输出文本。temperature 设为 0是为了减少随机性让模型的工具选择更稳定。第三步解析模型输出。用正则匹配 Action 和 Action Input然后根据动作名分发到具体工具。这里用了 search 和 calculator 两个工具search 返回的是本地字典里的固定信息实际项目中可以换成搜索引擎接口或内部知识库 API。第四步把 Observation 放回上下文。这是 ReAct 循环最核心的一步——模型输出的内容作为 assistant 消息工具返回的结果作为一条新的 user 消息。下一轮模型调用时就能基于 Observation 继续推理。需要特别提醒的是示例中的 calculator 使用了 eval这在生产环境是非常危险的存在任意代码执行风险。这里纯粹为了演示循环协议生产环境请使用专门的计算库或沙箱执行。search 工具也只是一个教学演示不要直接搬到线上。另外示例代码默认使用 gpt-4o-mini 模型。你可以根据自己实际可用的模型服务改成其他模型名称比如某些云厂商提供的 qwen-plus、deepseek-chat 等只要 base_url 对应兼容接口即可。这部分配置写成环境变量方便在不同服务之间切换。7. 运行结果与效果验证运行方式很简单终端执行python react_demo.py正常情况下你会看到类似下面的输出实际内容取决于模型生成不一定逐字一致 Step 1 Thought: 用户想知道北京的天气情况以及是否需要带伞。我需要先查询北京的天气信息。 Action: search Action Input: 北京天气 Step 2 Thought: 查询结果显示北京今天是晴天没有降雨所以不需要带伞。 Final Answer: 北京今天晴不需要带伞户外时间长的话建议注意防晒。 最终答案: 北京今天晴不需要带伞户外时间长的话建议注意防晒。判断循环是否成功的标准有三个。第一模型没有一次给出最终答案而是先选择了工具第二工具返回的 Observation 确实被展示在了助手输出里并且后续模型推理引用了这个结果第三循环在有限步内终止输出 Final Answer而且没有报错。如果模型只输出了 Thought 而没有 Action或者出现了“达到最大步数”的提示排除时先打印每轮 messages 内容重点看模型是否按照 system prompt 里的协议输出。很多情况下这是提示词描述不够清楚导致的而不是代码逻辑问题。可以适当在 SYSTEM_PROMPT 里增加一个完整的 few-shot 示例模型通常会立刻改观。8. 常见问题与排查思路ReAct 循环本身不复杂但在实际运行中会遇到各种意外。这里整理了一份排查清单遇到问题时按表定位。问题现象可能原因排查方式解决方案模型只输出 Thought没有 Action提示词工具描述不明确或模型想直接回答打印模型完整输出检查是否提前出现 Final Answer在 SYSTEM_PROMPT 中补充工具说明和示例必要时增加 few-shot在同一个 Action 上反复循环工具返回没有新信息或 Observation 没有正确拼入上下文打印每轮 messages 内容精简工具返回内容设置 max_steps 上限检查解析规则上下文越来越长、成本上升ReAct 每轮都会追加消息多次调用 token 消耗叠加观察 API 调用计费和 token 用量降低 max_steps工具返回长文本时先做摘要模型输出格式混乱正则解析失败模型没有严格遵守协议打开日志记录完整输出改用 function calling 或结构化输出减少文本解析脆弱性调接口报 401 错误API Key 无效或没有权限检查环境变量和控制台配置重新生成 Key配置最小权限使用其他模型服务接口报连接错误base_url 配置不正确检查 base_url 是否与模型服务商文档一致换成可用的兼容服务地址或云厂商接口本地小模型生成结果不稳定模型指令跟随能力较弱换大模型或调整采样参数降低 temperature或换用指令遵循能力更强的模型这里重点展开两个高频问题。第一模型输出格式不稳定。这是文本协议方案最主要的痛点。即使 SYSTEM_PROMPT 写得再详细模型依然可能偶尔不按格式输出。工程上更稳妥的解法是用 OpenAI 的 function calling 或结构化输出让模型直接返回一个结构化的工具调用对象而不是让文本解析器去猜。但理解 ReAct 思想时文本协议是最好懂的入门方式因为它把循环机制完整暴露了出来。第二长短上下文与成本控制。ReAct 每轮循环都要携带全部历史消息包括所有 Thought、Action、Observation。随着步数增加上下文会快速膨胀不仅影响速度也影响成本。建议在实际项目中给工具返回设置长度上限或者在 Observation 进入上下文之前做一次摘要压缩。另一个思路是限制 Agent 只处理必要信息不要把一大段原始文档塞回上下文。9. 工程化建议与使用边界理解了 ReAct 的循环原理之后接下来要回答一个更实际的问题真实项目里要不要上 ReAct、以及怎么上。先给判断标准。如果你的任务满足以下条件ReAct 是一个比较合适的选择任务需要多步推理任务需要访问实时或外部数据执行过程中存在不确定性需要根据中间结果调整过程需要可审计。典型场景包括个人知识库问答助手、营销素材自动整理、客服工单跟进、需要跨多个内部系统查询数据的 Agent。反过来如果任务只是单轮知识问答或者对响应延迟非常敏感ReAct 反而不是好选择。因为 ReAct 每走一步都要调用一次大模型多轮循环意味着延迟和成本的多倍放大。一个简单的“今天是几号”问题如果非要套 ReAct纯属杀鸡用牛刀。很多工具包括搜索引擎、数据库查询、代码执行器单独就能解决一轮任务不需要让模型反复决策。工程上落地时有几个经验值得参考。工具描述要写得足够具体模型是通过工具描述来判断该调用哪个工具的描述模糊会直接导致选错工具。比如一个检索公司内部文档的工具描述最好写成“输入员工姓名返回其所在部门和入职时间”而不是一句含糊的“查询员工信息”。循环要有边界max_steps 和超时时间必须设置否则模型可能在某个奇怪的问题上无限循环。日志要记录每一步的 Thought、Action、Observation排错和审计都依赖这些中间信息。安全边界是最不能忽略的部分。Agent 能调用的工具必须白名单化不要让 Agent 自由选择任意系统命令。涉及数据库写操作、生产环境变更、支付、删除等高风险操作时必须设置人工审批环节不能让 Agent 直接执行。API Key 要遵循最小权限原则只授予 Agent 完成任务所需的必要权限。所有涉及生产环境的改动都应该先在测试环境验证再逐步放量。关于框架的选择我的建议是入门阶段先手写一遍最小循环理解协议之后再去使用 LangChain、LangGraph、LlamaIndex 这类工具。框架可以帮你省去消息拼装和工具分发的重复劳动但也可能把循环机制封装成黑盒遇到问题时不方便排查。用 LangGraph 这类图编排框架时尤其要理解节点的执行顺序和状态传递否则写出来的 Agent 依然难以维护。需要提醒的是框架版本迭代很快Agent API 在不同版本之间差异很大使用前一定要核对当前文档。10. 总结ReAct 到底改变了什么ReAct 看起来结构简单但它对 LLM 应用范式的影响非常深远。在 ReAct 之前大模型和外部世界的交互是割裂的要么模型独立思考回答要么通过 RAG 查一次资料直接生成。ReAct 把“思考”和“行动”组合成了一个紧密的循环让模型可以在执行过程中不断吸收新信息、修正判断最终给出来自推理与实证共同支撑的答案。真正值得记住的点是ReAct 解决的核心问题不是“调用一个工具”而是“如何让一个语言模型在不迷失方向的情况下完成多步任务”。Thought 负责方向Action 负责触碰外部世界Observation 负责把外部世界的结果反馈回模型。三者交替出现才构成了 Agent 的基本工作单元。理解了这一点再看各种 Agent 框架你会发现自己已经能看懂它们的骨架了。建议的下一步是把本文的示例代码跑通然后把 search 换成真实的数据源比如天气 API、数据库查询、内部系统接口。观察它在真实数据下的表现再评估延迟和成本。等你对循环机制足够熟悉后再去研究 LangGraph、多智能体协作、Agent 自我改进等进阶方向。AI Agent 领域发展很快但 ReAct 作为最基础的控制流范式之一值得你花一个下午彻底搞懂。收藏这篇文章动手跑一遍比看十篇概念科普都管用。