news 2026/10/3 15:16:24

ReAct模式解析:让大模型从“会聊天”到“能干活”的智能体核心循环

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ReAct模式解析:让大模型从“会聊天”到“能干活”的智能体核心循环

1. 先搞明白:ReAct 为什么能"想一步走一步"

我第一次给客户演示 AI Agent 时,对方问了一句特别扎心的话:"你这 Agent 跟 ChatGPT 有什么区别?不都是聊天吗?"当时我做的 Demo 确实就是个聊天框,模型再聪明,它也只会"说"不会"做"。

后来我把 ReAct 模式接进去,同一个 Demo 立刻就不一样了:用户说"帮我查一下杭州明天天气,顺便看看有没有合适的航班",Agent 会自己先拆解问题,然后调天气 API、查航班接口,拿到结果再继续推理,最后把结论汇总成一段完整答复。整个过程不是一次生成答案,而是一轮一轮地"想一步、做一步、看一步"。

先提醒一句,这里的 ReAct 是 Reasoning + Acting 的缩写,跟前端圈那个 React.js 完全不是一回事,别搞混。ReAct 最早是 2022 年底由 Princeton 和 Google 的研究者在论文ReAct: Synergizing Reasoning and Acting in Language Models里提出的。它要解决的根本问题很简单:大语言模型本身只擅长"想",不擅长"做"。模型没有途径去查数据库、调接口、操作文件,而现实世界的任务恰恰需要这些外部动作。ReAct 的做法,是让模型在推理过程中主动声明"我现在需要做什么动作",然后由外部系统真正执行这个动作,把结果再喂回给模型,让它继续推理。

如果你是刚接触 AI Agent 的开发者,ReAct 是你绕不开的第一个执行范式。它同时也是现在大量 Agent 框架(LangChain、LangGraph、AutoGPT 等)底层默认采用的核心模式。它不依赖特定 SDK,也不是某个框架的专利,本质上就是一套"思考-行动-观察"交替进行的循环结构,用任何模型、任何编程语言都能实现。

1.1 从"只会聊"到"能干活",差的不是模型而是循环

做个思想实验。你让一个对话模型完成这个任务:"告诉我北京和上海今天的实时温差是多少。"模型如果没见过实时天气数据,它只能靠训练时的记忆瞎编,因为它根本没有途径获取今天的天气。传统的 prompt engineering 解决不了这个问题,因为瓶颈不在提示词,而在"获取外部信息"这个动作本身。

ReAct 的思路,是把"获取天气"这个动作显式地交给模型去决策。模型可以输出一个结构化指令,比如调用get_weather(city="北京"),然后外部代码真正去请求天气 API,把返回结果作为"观察"(Observation)再交给模型。模型看到北京气温 25 度、上海 28 度,就能自然算出温差 3 度。注意:这里的"计算温差"是模型推理能力的一部分,但"拿到两个城市的实时温度"这个前提,是靠行动循环补上的。

所以 ReAct 解决的问题可以概括成一句话:让模型借助外部工具,把"推理"和"行动"交替串联起来,最终收敛到一个可验证的答案。它给 LLM 补上了"可操作性"这层能力。模型不再只是回答问题,而是能调度工具、能感知结果、能根据结果调整下一步。这也是为什么同样一个模型,套上 ReAct 循环之后,能从一个"聊天机器人"变成真正"下地干活"的智能体。

1.2 "推理"和"行动"交替发生的底层逻辑

ReAct 的底层逻辑可以画成一个循环:模型根据当前已知信息,推理出下一步该做什么(Thought),然后声明一个具体动作(Action),外部系统执行动作后返回观察结果(Observation),模型再基于新的 Observation 重复这个过程,直到它认为信息足够、可以直接输出最终答案为止。

这个循环听起来简单,但它对应着人类解决复杂问题的基本方式。你接到"帮我规划周末出行"这个任务时,不会一次性把整套方案背出来,而是先想"出行得知道天气吧",然后去查天气(行动);看到下雨再想"那得带伞,再看看室内备选方案"(推理),接着查博物馆开放时间……每一步的"想"都建立在上一步"做"的结果之上。ReAct 就是把这个过程显式地建模成模型可以执行的循环。

这里有个关键区别:ReAct 不是让模型一次性输出"完整思考过程+所有动作",而是要求它每轮只输出当前这一步的推理和动作,执行完拿到结果后再继续。这种"逐步推进"设计有实际的工程意义——它让每一步都建立在真实、最新的观察结果上,而不是模型凭空的预测。在我实测过的多步工具调用任务里,ReAct 的准确率明显高于"让模型一次性规划完再执行"的做法,主要原因就是它每一步都有外部结果兜底。

2. 拆开 ReAct 的核心循环:Thought / Action / Observation 三段式

把 ReAct 循环的每一轮拆开看,其实就是三个角色在接力:模型负责 Thought 和 Action,外部系统负责执行并产生 Observation,然后 Observation 又成为下一轮模型输入的上下文。

2.1 一次循环里的三个关键角色

在 ReAct 的 Prompt 中,通常会要求模型按固定格式输出三段内容:

  • Thought(思考):模型解释它为什么决定这么做,这一步的推理过程。
  • Action(行动):模型声明要调用的工具名称和参数,比如search(query="北京到上海机票")。
  • Observation(观察):外部系统在模型输出 Action 后执行得到的结果,会作为下一轮输入的一部分再喂回给模型。

这里要特别强调:Thought 和 Action 是模型生成的,Observation 不是模型生成的,而是外部工具返回的真实结果。很多初学者第一次看 ReAct 执行日志时,会把 Observation 当成模型的输出,实际上它来自工具层。把这个角色搞混,调试 Agent 时会走很多弯路。

一次完整的循环大概长这样:

模型输出 -> Thought: 用户需要查询北京的天气,我需要先调用天气接口。 Action: get_weather(city="北京") 外部系统执行 -> get_weather 返回 {"temperature": 25, "condition": "晴"} 拼接下一轮 -> Observation: 北京今天 25 度,晴天

模型看到 Observation 后,再决定是继续行动(比如还需要查上海天气),还是直接回答用户。这就是"想一步做一步"的最小闭环。整个 Agent 就是把这个循环重复若干次,直到收敛。

2.2 一个可以直接抄的 Prompt 模板

ReAct 的实现不依赖任何框架,核心是两件事:Prompt 设计 + 输出解析。我贴一个最简版本的 system prompt 模板,可以直接拿去改:

你是一个能够调用外部工具的智能助手。请严格按照以下格式输出: Thought: 你对当前任务的分析和你决定采取行动的原因。 Action: 你要调用的工具,格式为 工具名(参数1=值1, 参数2=值2)。 (当且仅当需要调用工具时输出 Action) Observation: 这是工具返回的结果,你需要基于它继续推理。 (Observation 不需要你生成,它由系统提供) 当你已经获得足够信息,不再需要调用工具时,请输出: Thought: 我已获得足够信息。 Answer: 你对用户问题的最终回答。

这是最经典的 ReAct 格式。实际使用时,我强烈建议在 Prompt 里附上完整的工具清单和每个工具的详细说明(参数类型、返回值含义、使用示例),因为模型对"有哪些工具、参数怎么填"的全部感知都来自这里。在我的项目里,工具说明写得好不好,直接决定了模型调用工具的准确率能差出两到三成。别嫌 Prompt 长,这里多花的心思一定会在效果上赚回来。

3. 完整走一遍真实执行链路:让 Agent 查机票并给出建议

光看模板不够直观。我拿一个自己在项目里反复跑过的场景——"查机票价格并给建议"——把完整链路走一遍。假设 Agent 有两个工具:search_flights(origin, destination, date)和search_hotels(city, check_in, check_out)。

3.1 第一轮:模型生成 Thought 和 Action

用户提问:"帮我看看下周五从北京到上海的机票,顺便看看有没有便宜的酒店。"

模型收到问题后,第一轮输出可能是:

Thought: 用户需要北京到上海的机票和上海酒店信息。我需要先查询机票。 Action: search_flights(origin="北京", destination="上海", date="2026-05-15")

这里有个非常容易被忽略的细节:用户说的"下周五"是个相对时间,模型需要自己推算具体日期。如果模型不知道当前日期,它在这一步就会卡住或者编造日期。所以实际工程中,通常要把当前日期作为系统信息注入到 Prompt 里,让模型知道"今天是什么日子"。我最早做 Agent 时没注意这个,结果模型一本正经地查了一个过去日期的航班,返回"无结果"——排查了半天才发现是日期推算的问题。

外部系统(你的代码)解析出 Action 后,调用真实的航班查询接口,返回结果:

Observation: 北京->上海 下周五共有 3 个航班: 国航 CA1501 08:00-10:15 950元 东航 MU5108 10:30-12:40 780元 吉祥 HO1252 19:00-21:10 680元

3.2 中间轮:工具返回 Observation,模型继续推理

模型拿到 Observation,继续生成下一轮:

Thought: 有三趟航班可选,价格从 680 到 950 元。 用户还问了便宜的酒店,我需要继续查询上海酒店。 Action: search_hotels(city="上海", check_in="2026-05-15", check_out="2026-05-16")

外部系统再次执行,返回:

Observation: 上海人民广场附近经济型酒店:如家 350元/晚、汉庭 320元/晚。 静安寺附近:亚朵 650元/晚。

注意看,模型每一轮的 Thought 都依赖上一轮的 Observation。如果没有这个循环,模型在第一轮就一次性输出"我估计机票几百块、酒店五六百",那全是编的。ReAct 的强项恰恰在于:每一步结论都有工具结果作支撑,这也是它能用来做真实业务决策的根本原因。

3.3 终止轮:没有 Action,只有 Final Answer

模型看到酒店信息后,觉得自己掌握的信息足够回答用户问题了,于是输出:

Thought: 我已有航班和酒店信息,可以给出建议。 Answer: 从北京到上海下周五有三趟航班,最便宜的是吉祥 HO1252(680元), 但起飞时间是晚上 19:00。如果希望白天到达,东航 MU5108 是 780 元。 酒店方面,汉庭 320 元/晚性价比最高。

到这里,一轮任务就完整收敛了。这个例子看着简单,但任务复杂度上去之后——比如需要对比多个渠道、需要根据中间结果动态改变查询条件、需要在多个结果之间做权衡——ReAct 逐步推理的优势会非常明显。我见过不少团队花大力气把 Agent 的"思考"写死在代码里,结果每次需求一变就要改代码,而 ReAct 的灵活性恰恰省掉了这部分维护成本。

4. ReAct 的边界:它和 Chain、Plan-and-Execute 到底差在哪

ReAct 不是唯一的 Agent 执行模式。实际工程里我经常被问到:"什么时候用 ReAct,什么时候用 Chain,什么时候用 Plan-and-Execute?"这三个模式我都在生产环境跑过,给你梳理一下它们的本质区别和取舍逻辑。

4.1 三类模式的运行方式对比

Chain(链式调用)最简单:把任务拆成固定几个步骤,按顺序执行,每步输入来自上一步输出。比如"先总结用户评论,再翻译成英文,再提取要点",就是一个固定链。它的特点是步骤是预设的、不可变的,模型没有"决定下一步做什么"的自由度。优点是可预测、好调试,缺点是任务一变化就失效。

ReAct 的特点是模型每轮自主决定下一步动作,没有预设步骤数,循环直到模型认为可以回答为止。它比 Chain 灵活得多,但也带来了不可预测性——你不知道它会调用多少次工具,也不知道它会不会跑偏、会不会陷入死循环。

Plan-and-Execute(规划后执行)是两段式:模型先一次性制定完整计划(Plan),然后按计划逐步执行。比如先规划"第一步查机票、第二步查酒店、第三步比较价格",然后一步步执行。它和 ReAct 最大的区别在于:ReAct 是"边想边做",Plan-and-Execute 是"先想后做"。

用表格对比更直观:

模式决策方式灵活度适用场景主要风险
Chain预设固定步骤低流程稳定、不允许跳步的任务步骤一变就失效
ReAct每轮自主决策高多步工具调用、结果影响后续决策可能循环过多或跑偏
Plan-and-Execute先整体规划再逐步执行中任务结构清晰、步骤间相互独立计划可能与实际情况脱节

4.2 什么时候别用 ReAct

ReAct 虽然灵活,但某些场景下它真不是最优解。比如你做的 Agent 必须严格遵循动作序列(先鉴权、再查库、再脱敏、再返回),这种时候固定 Chain 更可靠,因为你根本不想让模型"自由发挥"去跳过鉴权步骤——那会直接变成安全事故。

另外,当任务是一锤子买卖、不需要外部工具时(比如纯文本改写、翻译),ReAct 的循环完全是多余的,直接单次生成更快、更省 token。我见过不少团队把 ReAct 用在所有场景上,结果延迟高企、成本翻倍,其实很多简单任务用普通 prompt 就能搞定。

还有一种情况是"先规划"更合适:当任务步骤非常多、且单次工具调用成本很高(比如外部 API 收费、耗时很久),Plan-and-Execute 可以先让模型制定一份完整计划,执行时每一步都可以审视"计划是否仍然合理",必要时候再重新规划。它跟 ReAct 没有绝对的优劣之分,核心是看你更看重"灵活性"还是"可控性"。我的经验是:步骤少于 5 步、环境变化快的任务用 ReAct;步骤多、执行成本高、需要稳定流程的任务用 Plan-and-Execute 或者 Chain。

5. 工程落地时的四个关键决策点

理论讲完,说点工程落地一定会碰到的问题。我在好几个项目里从零实现过 ReAct 循环,下面这几个决策点几乎每个项目都会碰到,而且踩坑成本都不低。

5.1 工具怎么定义,模型才不容易出错

工具定义直接决定模型能不能正确调用。我建议每个工具的说明至少包含五件事:

  • 工具名称:简短、语义明确,比如get_weather,别用do_thing_1这种。
  • 参数说明:每个参数的名称、类型、取值范围、是否必填。
  • 返回值说明:返回的 JSON 结构或文本格式,模型需要知道结果长什么样才能正确解析。
  • 使用示例:至少一个完整的调用例子,模型会模仿示例的格式。
  • 使用场景提示:什么时候该用这个工具、什么时候不该用。

实测下来,工具说明里给示例的效果远好于只给字段说明。大模型的 few-shot 能力很强,你给它一两个正确调用示例,它就会照着例子输出格式。工具数量超过五六个以后,我建议把工具说明按业务域分组,或者给每个工具加一句"当用户提到 XXX 时使用本工具"的场景提示,能明显减少模型选错工具的概率。

5.2 最大循环轮数设置多少合适

ReAct 循环如果没有限制,模型可能陷入无限循环——不停调用工具、不停看观察结果、就是不出最终答案。所以工程上必须设置最大轮数(max_iterations)。我的经验值是:常规任务设 5 到 8 轮,复杂任务放宽到 10 到 15 轮,超过 15 轮还收敛不了,基本可以判定是 Prompt 或工具定义有问题,不是轮数不够。

轮数触顶之后的处理方式也很有讲究。最简单的做法是强制要求模型基于已有观察输出 Answer;更好的做法是给模型一个兜底提示:"你已用完所有尝试次数,请基于已有信息尽可能回答,如果信息不足以回答,请明确告诉用户缺什么。"这个兜底逻辑能显著降低 Agent 最后"硬编一个答案"的幻觉率。我见过很多 Agent 在轮数耗尽后为了完成任务开始瞎编数据,加一句话提示就能规避大半。

5.3 上下文窗口不够了怎么办

ReAct 的每次循环都会把 Thought、Action、Observation 追加到历史里,多轮之后 Prompt 会变得非常长。上下文窗口有限,Observation 太详细时(比如搜索接口返回 100 条结果),几轮下来窗口就炸了。

我的做法是两级策略。第一级:在把 Observation 喂给模型前先做裁剪,只保留关键字段、限制返回条数、超长内容截断。第二级:对历史对话做摘要压缩,把早期轮次的 Thought/Observation 压缩成一行摘要,只保留最近几轮的完整内容。这个"早期摘要 + 最近完整"的组合策略,在我实际项目里支撑过 20 轮以上的复杂任务,上下文窗口始终没爆过。记住一个原则:模型只需要看到做决策所需的最小信息集,而不是所有原始数据。

5.4 解析模型输出时,格式不稳定的处理

ReAct 依赖结构化输出(Thought/Action/Observation),但模型输出格式天然不稳定。你要求它输出Action: search("北京"),它可能给你加一段解释,或者把 Action 写进 JSON 里,或者用 Markdown 代码块包起来。解析失败是 ReAct 实现中最常见的 bug 来源,没有之一。

我的经验是三层防护。第一层:在 Prompt 里明确要求"只输出规定格式,不要输出任何解释性文字",并同时给正例和反例。第二层:用正则做宽松匹配,比如从整段输出里提取Action:标记之后的内容,修剪掉多余字符。第三层:解析失败时不要直接报错,而是把"你的输出格式不符合要求,请重新按格式输出"作为 Observation 喂回给模型,给它一次纠错机会。这个"格式纠错循环"在我的实测里能把解析成功率从 80% 拉到 95% 以上,是所有调优手段里性价比最高的一个。

6. 实测中踩过的坑和调优心得

最后分享几个我在真实项目里踩过、且有代表性的大坑。这些基本都是文档里不会写的东西,属于"不自己跑一遍根本不知道"的经验。

6.1 模型"幻觉式调用"工具

模型有时会在信息不足时硬编一个工具调用,比如虚构一个不存在的 ID、或者把查询参数乱填。我遇到最典型的一次:模型在 Action 里调用了一个根本不存在的工具名称,完全是根据我工具清单里的描述"编"出来的。排查下来发现,是因为那个工具说明写得太模糊,模型误以为还存在另一个变体工具。

解决方法是加一道"工具校验层":外部系统解析出 Action 后,先检查工具名是否在白名单里、参数类型是否合法,不合规就返回一个固定 Observation:"工具不存在,可用工具为:XXX",让模型自己纠正。千万不要让不合规的调用直接抛异常崩掉整个循环,那样整个 Agent 会话就废了。这个校验层成本很低,但对稳定性的提升非常明显。

6.2 Observation 内容过载

工具返回大段 JSON 时,如果把原始 JSON 直接作为 Observation 喂给模型,既浪费 token,又会干扰模型推理——模型在里头翻半天找不到关键信息,还容易找错重点。

我的做法是在工具层做一次"结果摘要":把原始返回 JSON 转成一句话或结构化要点,只保留模型做决策所需的信息。比如查询结果有 50 条,就取前 5 条,并注明"共 50 条结果"。模型不需要看全部原始数据,它只需要拿到能支撑下一步决策的最小信息集。养成这个习惯之后,整个 Agent 的稳定性和 token 成本都会有肉眼可见的改善。

6.3 冷启动、超时与并发场景的建议

如果你打算把 ReAct Agent 做成线上服务,有几个容易被忽略的工程点。一是冷启动:首次请求时模型要加载系统 Prompt 和工具清单,如果多轮循环串行执行,延迟会很高。建议把工具清单和系统 Prompt 缓存起来,或者用支持 prompt caching 的模型服务,首轮响应能快不少。二是并发控制:ReAct 循环通常会调用外部 API,多路并发时要对下游接口做限流。我遇到过不止一次 Agent 本身没挂、下游 API 先被打爆的情况。三是超时设置:每轮循环都要有独立的超时控制,避免某个工具调用卡死,导致整个 Agent 会话长时间挂起。

最后说一点模型选型的体会。ReAct 模式对模型的推理能力要求不低,小参数模型经常出现 Action 格式错误、Thought 逻辑跳脱的问题。如果你受限于部署条件只能用较小的模型,建议把任务拆得更细,或者改用 Plan-and-Execute 模式减少循环深度。如果项目预算允许,用推理能力强的模型跑 ReAct,整体体验会好一个档次——尤其是复杂任务,模型一弱,整个循环就变成在错误边缘反复试探,调试成本反而更高。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 15:15:46

Python环境安装与配置全指南:从发行版选型到虚拟环境实战

装Python这事儿,听起来简单,实际上坑比想象中多。热搜词里那一串报错—— defaulting to user installation because normal site-packages is not writeable 、 attempting uninstall: protobuf found existing installation: protobuf 5.29.6 、 …

作者头像 李华
网站建设 2026/10/3 15:13:45

dsh-waker 插件实战:事件驱动唤醒 AI 员工,从配置到踩坑

1. 从"一个人干三个人的活"说起:dsh-waker 到底想解决什么 如果你最近在折腾 dsh 这套工具链,大概率会刷到 dsh-waker 这个名字。第一次看到"唤醒专属你的 AI 员工"这句描述,我其实是有点警惕的——这两年打着"AI…

作者头像 李华
网站建设 2026/10/3 15:12:48

Python实现KMeans聚类算法:源码解析与数据集实战指南

简介:这份资源面向机器学习初学者与数据挖掘实践者,提供一套可直接运行的KMeans聚类算法Python实现方案,帮助读者理解从数据预处理、核心算法执行到结果可视化的完整聚类分析流程。压缩包共246个文件,约35.02MB,其中14…

作者头像 李华
网站建设 2026/10/3 15:11:28

稀疏贝叶斯DOA估计:从谱峰搜索到稀疏回归的工程实践解析

简介:面向无线通信、雷达与音频信号处理研究者的 Matlab 智能算法资源包,聚焦方向到达角(DOA)估计问题,覆盖经典 DOA、稀疏贝叶斯 DOA、投影追踪、聚类分析等方向。无需大型实验平台,在 Matlab 中即可完成从…

作者头像 李华
网站建设 2026/10/3 15:11:25

DDSRF双解耦控制原理与工程实践:正负序分离核心技术解析

1. 项目概述:为什么DDSRF双解耦是正负序分离的“定海神针” 你有没有遇到过这样的情况:光伏电站并网测试时,电网突然出现不对称短路,逆变器输出电流波形瞬间畸变,保护动作跳闸,可后台录波数据里根本看不出问…

作者头像 李华
网站建设 2026/10/3 15:11:21

Nginx应用与运维——Nginx HTTP模块详解(访问控制功能模块)

Nginx HTTP模块详解2、访问控制功能模块2.1、访问镜像模块2.1.1、访问镜像指令——mirror2.1.2、镜像请求体指令——mirror_request_body2.2、referer请求头控制模块2.3、连接校验模块2.4、源IP访问控制模块2.5、基本认证模块2.6、认证转发模块2.7、用户cookie模块2.8、并发连接…

作者头像 李华