ReAct:让语言模型在推理与行动之间来回切换

把显式推理链和外部行动结合起来,让模型不只“想”,还会在需要时查资料、调工具、再继续推理,成为 Agent 设计的经典起点。

年份与会议

2022 · ICLR

作者

Shunyu Yao、Jeffrey Zhao、Dian Yu、Nan Du、et al.

主题

Agent

阅读时长

约 3 分钟

收录时间

2022/10/07

标签

原文链接

https://arxiv.org/abs/2210.03629

为什么 ReAct 会成为 Agent 设计的经典起点

在 ReAct 之前,很多 Prompt 方法大致可以分成两类:

  • 一类强调“先思考”,例如 Chain-of-Thought
  • 一类强调“先调用外部环境”,例如检索、搜索、API 或工具系统

问题在于,真实任务往往既需要推理,也需要行动。比如:

  • 回答事实问题时需要先查资料
  • 做复杂规划时需要边想边验证
  • 多步任务里往往需要先观察环境,再修正下一步思路

ReAct 的关键贡献,就是把这两件事统一成一条交替链路:

模型不是先想完再做,也不是只做不想,而是在 Thought 和 Action 之间反复切换。

这件事对后来的 Agent、Tool Use、工作流编排影响非常深。很多今天看起来很自然的“思考 -> 调工具 -> 观察 -> 再思考”范式,本质上都可以追溯到 ReAct。

背景:为什么单纯 CoT 或单纯检索都不够

单独看 CoT,它解决的是“复杂问题如何显式展开中间步骤”。但如果模型推理过程依赖外部知识,CoT 会遇到一个局限:

  • 它可能写出非常自洽的推理链
  • 但这条链未必建立在真实证据上

而单纯检索或工具调用也有另一种问题:

  • 模型能拿到外部信息
  • 但不一定知道何时调用、如何使用、何时停止

ReAct 的作者看到的正是这个缝隙:很多任务不是“多想一点”就能解决,也不是“多查一点”就能解决,而是需要两者不断互相修正。

核心方法:Thought、Action、Observation 的交替循环

ReAct 的结构可以概括成一个非常经典的循环:

  1. Thought:模型先写当前思路
  2. Action:根据当前思路决定下一步行动
  3. Observation:读取环境或工具返回结果
  4. 再回到 Thought,继续更新推理

这比普通 CoT 多出来的关键在于:

  • 中间步骤不再只是文字推理
  • 模型可以通过行动真正获取新信息

你可以把 ReAct 看成一种“带环境反馈的 CoT”。它的真正创新不是单个组件,而是把思考和行动编织成同一条轨迹。

如果把这条轨迹画出来,ReAct 为什么会成为 Agent 原型协议就会非常明显:

ReAct 的 Thought-Action-Observation 循环图 用户任务进入后,模型先形成当前 Thought,再执行 Action,读取环境 Observation,并在循环中继续更新计划,直到得到最终答案或结束任务。 ReAct 的最小 Agent 循环
  <g>
    <rect x="44" y="122" width="150" height="72" rx="18" fill="#e8f1ff" stroke="#98b7e1" />
    <text x="119" y="153" text-anchor="middle" font-size="19" font-weight="700">用户任务</text>
    <text x="119" y="176" text-anchor="middle" font-size="13" fill="#4b5563">问题 / 目标 / 当前状态</text>
  </g>

  <g>
    <circle cx="342" cy="158" r="72" fill="#fef3c7" stroke="#e2c36f" stroke-width="3" />
    <text x="342" y="149" text-anchor="middle" font-size="22" font-weight="700">Thought</text>
    <text x="342" y="173" text-anchor="middle" font-size="13" fill="#4b5563">当前判断 / 下一步计划</text>
  </g>

  <g>
    <circle cx="542" cy="158" r="72" fill="#e0f2e5" stroke="#8bc09b" stroke-width="3" />
    <text x="542" y="149" text-anchor="middle" font-size="22" font-weight="700">Action</text>
    <text x="542" y="173" text-anchor="middle" font-size="13" fill="#4b5563">搜索 / 检索 / 调工具</text>
  </g>

  <g>
    <circle cx="742" cy="158" r="72" fill="#ece8ff" stroke="#b5abef" stroke-width="3" />
    <text x="742" y="149" text-anchor="middle" font-size="22" font-weight="700">Observation</text>
    <text x="742" y="173" text-anchor="middle" font-size="13" fill="#4b5563">环境反馈 / 新证据</text>
  </g>

  <g>
    <rect x="808" y="242" width="140" height="60" rx="18" fill="#dff4f0" stroke="#8dc7bd" />
    <text x="878" y="272" text-anchor="middle" font-size="18" font-weight="700">最终答案</text>
  </g>

  <line x1="194" y1="158" x2="264" y2="158" stroke="#5b6b7f" stroke-width="3" marker-end="url(#react-arrow)" />
  <line x1="414" y1="158" x2="470" y2="158" stroke="#5b6b7f" stroke-width="3" marker-end="url(#react-arrow)" />
  <line x1="614" y1="158" x2="670" y2="158" stroke="#5b6b7f" stroke-width="3" marker-end="url(#react-arrow)" />
  <path d="M 742 230 C 700 292, 470 292, 386 222" fill="none" stroke="#5b6b7f" stroke-width="3" marker-end="url(#react-arrow)" />
  <text x="548" y="312" text-anchor="middle" font-size="15" fill="#4b5563">新 observation 会重新触发下一轮 thought</text>
  <line x1="792" y1="204" x2="850" y2="242" stroke="#5b6b7f" stroke-width="3" marker-end="url(#react-arrow)" />
</g>
ReAct 的价值不在某个单独提示词,而在于把“推理、行动、观察、再推理”固化成了一个可复用的循环接口。

为什么这种结构如此强大

ReAct 之所以重要,是因为它解决了两个长期存在的问题:

1. 纯语言推理容易闭门造车

模型如果一直只在上下文里“想”,很可能越想越错,还越想越自信。外部行动让它能中途校正。

2. 纯工具调用缺少全局任务意识

如果模型只会不停查资料、调工具,没有显式思路,那么动作很容易发散、重复或无效。

ReAct 让模型保持一种更像人类问题求解的状态:

  • 先形成当前假设
  • 再做一个能减少不确定性的动作
  • 根据反馈调整下一步

这就是为什么它对多跳问答、交互式决策和环境任务特别有吸引力。

ReAct 的提示模板到底做了什么

ReAct 在实践上非常依赖提示模板。典型示例中,模型不会被要求“一次性直接给答案”,而是示范成这样的格式:

  • Thought: 我现在需要确认什么
  • Action: 我接下来执行什么动作
  • Observation: 动作返回了什么结果

这种格式的价值不只是让输出更整洁,而是把求解过程显式拆成几个不同角色:

  • Thought 负责计划与判断
  • Action 负责与环境交互
  • Observation 负责把环境反馈重新带回推理

从工程角度看,这等于给后续 Agent 框架定义了一个非常自然的接口协议。

ReAct 为什么特别适合知识密集型任务

在知识密集型任务中,错误往往不是因为模型完全不会推理,而是因为:

  • 事实记错了
  • 检索到了错误信息
  • 没有验证中间结论

ReAct 的优势就在于它允许模型边推理边补证据。例如多跳 QA 时,模型可以:

  1. 先推断当前缺什么信息
  2. 再去检索或搜索
  3. 根据返回结果更新思路
  4. 最后再整合答案

这和单纯 RAG 很不一样。RAG 更像“先检索一批,再统一生成”;ReAct 更像“边走边查,按当前思路决定下一步”。

所以你可以把 ReAct 理解成更 agentic 的信息获取方式。

它和 Agent 的关系到底是什么

很多人会把 ReAct 直接等同于 Agent。更准确的说法是:

ReAct 不是所有 Agent 的定义,但它为现代 LLM Agent 提供了一个非常经典的最小工作模式。

为什么这么说?因为一个最基本的 Agent 通常需要三样东西:

  • 当前任务状态
  • 对外部环境的动作能力
  • 根据反馈更新计划的能力

ReAct 正好把这三件事都最小化地表达出来了。也因此,很多后来的 Agent 框架即使实现完全不同,内部思路仍会沿着 ReAct 这条线展开。

它和 Toolformer 的关系

ReAct 和 Toolformer 很容易被放在一起讨论,但两者切入点并不相同:

  • ReAct 更关注“推理与行动如何交替组织”
  • Toolformer 更关注“模型如何学会在合适位置调用工具”

你可以把两者理解成 Agent 世界的两个互补问题:

  • ReAct 解决流程结构
  • Toolformer 解决工具调用能力

后来的很多系统,其实同时吸收了两边思想:既要有合理的思考-行动轨迹,也要有足够稳健的工具调用决策。

局限:ReAct 不是写出 Thought / Action 就万事大吉

虽然 ReAct 非常经典,但它也有明显局限:

1. 思路文本不一定可靠

Thought 看起来合理,不等于真的在做严格推理。它依然可能只是“像推理的文字”。

2. 动作选择可能低效

模型可能频繁做无效搜索、重复动作,导致成本和延迟暴涨。

3. 长链任务容易漂移

步骤一多,系统会面临上下文膨胀、错误累积和状态管理问题。

4. 真实工具环境比论文设置复杂得多

工业场景里会出现权限、超时、错误重试、结构化参数、不可逆操作等问题,远比论文里的环境交互复杂。

因此,ReAct 更像一种最经典的“原型协议”,不是完整生产系统。

从今天看,ReAct 最重要的遗产是什么

ReAct 留下的最大遗产,是它让社区接受了一件事:

语言模型不必只做一次性文本映射,它可以成为一个在环境中逐步行动的决策体。

这个转变极其重要,因为它把 LLM 的角色从“回答生成器”推进到“过程控制器”:

  • 它可以决定是否搜索
  • 可以决定先做什么后做什么
  • 可以根据新观测修正原计划

很多今天的 Agent 设计、工作流编排、Browser Agent、代码 Agent,本质上都建立在这种观念转换之上。

读这篇论文时最该抓住什么

如果你只想抓住主线,请记住:

  1. ReAct 的核心不是单纯 CoT,也不是单纯工具调用,而是两者交替。
  2. 它的重要性在于把“思考-行动-观察”组织成可复用结构。
  3. 它为后来的 Agent 设计提供了最经典的起点范式。

理解这三点后,你再去看工具调用、工作流系统或更复杂的 Agent 框架,会更容易判断它们是在 ReAct 哪一层继续扩展。

延伸阅读

相关内容

沿着相近主题继续阅读,加深对方法边界与实践场景的理解。