ReAct 模式

发布: (2026年2月4日 GMT+8 10:35)
11 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的完整文本内容,我将为您将其翻译成简体中文,并保留原始的格式、Markdown 语法以及技术术语。只需粘贴需要翻译的文字(代码块和 URL 将保持不变),我即可完成翻译。

什么是 ReAct?

Klover: ReAct 代表 Reasoning + Acting —— 明确 interleaved(交错)。
想法很简单:与其让大模型一次性回答问题,你让它 think out loud(大声思考),执行动作(比如调用工具),观察结果,然后再思考。这样形成一个循环。

Me: 那它就像 chain‑of‑thought,但混合了实际的工具使用?

Klover: 正确。循环如下:

  1. Thought – 模型推理下一步该做什么。
  2. Action – 模型调用工具(搜索、计算器、API 等)。
  3. Observation – 系统注入该工具的真实结果。
  4. Repeat 直到模型认为信息足够 → Final Answer(最终答案)。

关键字是 “interleaved.” 它不是“先把所有想法想完再行动”——而是 稍微思考一下,稍微行动一下,观察,调整

系统如何知道模型是想调用工具还是仅仅在聊天?

我: 系统如何知道模型是想调用工具还是仅仅在聊天?

Klover: 停止序列。 模型被训练(或提示)以特定格式输出,例如:

Action: search["weather in Singapore"]

系统监视该模式,拦截它,实际运行工具,然后将结果注入回去,如:

Observation: 32 °C, sunny

模型实际上从不执行任何操作。它只是在写看起来像工具调用的文本,周围的脚手架完成其余工作。

我: 那么 LLM 并不知道它在调用真实工具?它只是认为自己在生成文本?

Klover: 对。对模型而言,这只是下一个 token 的预测。魔法在于拦截、执行并注入的脚手架。

示例交互

Klover: 假设你向一个 ReAct 代理提问:

“我这个星期六应该去海滩吗?”

代理可能会这样做:

Thought: I need to check the weather for Saturday.
Action: weather_forecast("Saturday")
Observation: Saturday: 34 °C, sunny, UV index 11, no rain.
Thought: Weather looks great, but UV is very high. Let me check tide times too.
Action: tide_info("Saturday")
Observation: High tide at 2 pm, low tide at 8 am.
Thought: Good beach weather, but go early to avoid peak UV and catch low tide. I have enough info now.
Final Answer: Yes! Saturday looks perfect — sunny, 34 °C. Go in the morning around 8‑9 am when the tide is low and UV isn’t brutal yet.

多个步骤,每一步都基于上一步的结果。模型自行决定何时信息足够。

我: 明白了。也就是说它本质上是一个循环中的 LLM。

Klover: 这就是最简洁的说法——一个循环中的 LLM,拥有工具访问能力并能决定何时停止。这就是 代理 的核心。

我: 等等——你现在是一个 ReAct 代理吗?

Klover: 基本上是的。我会推理你的问题,我可以调用工具(搜索、读取文件、运行代码),我会观察结果,然后决定接下来要说什么。你正在观看这个模式的实际运行。

常见失败模式

Klover: 两大失败模式。

  1. 无限循环 – 模型可能不断调用工具而无法收敛到答案(例如,反复搜索、重新表述、再次搜索……)。
    解决方案: 设置 max‑iteration limit。在 N 步之后,强制给出最终答案,使用已有信息。

  2. 上下文溢出 – 每个 Thought、Action 和 Observation 都会追加到对话中。经过 10‑15 步后,令牌数量会变多,最终触及上下文窗口上限,导致模型崩溃。
    但真正的问题在 达到上限之前 就已经出现。

为什么“在上限之前”很重要

  • 注意力稀释: 随着令牌数量增长,softmax 注意力在所有令牌上变得更薄。
    类比: 把一个馅饼切成 10 片与切成 1 000 片——每片几乎没有意义。

  • “中间丢失”效应: 模型倾向于更关注上下文的开头和结尾,忽视中间部分。
    如果关键推理发生在 15 步链的第 5 步,可能会被有效忽略。

结果: 长的 ReAct 链会受到 双重打击——注意力稀释 中间丢失。

实用修复:上下文压缩

Klover: 实用的修复方法是 在步骤之间压缩上下文

  • 将最近的几次交流 原文保留 以保留细节。
  • 对更早的内容进行概括。

LangChain 的 ConversationSummaryBufferMemory 正是这样做的:最近的消息保持原始,较早的消息由 LLM 进行摘要。

权衡

  • 有损压缩: 摘要器可能会遗漏重要信息。
  • 十步之后,遗漏的细节可能变得至关重要。
  • 没有完美的解决方案——这是一种在 上下文新鲜度信息保留 之间的平衡。

为什么显式思考步骤很重要

Me: 为什么显式的 Thought 步骤如此重要?模型不能直接跳到 Action 吗?

Klover: 这是一个更深层的洞见。Chain‑of‑thought (CoT) 不仅仅是一个提示技巧——它本质上是额外的计算。

  • 没有 CoT: 模型只有一次前向传播来产生答案。这是它能进行的全部计算,受限于 Transformer 网络的固定深度。
  • 有 CoT: 模型生成的每一个 token 都会成为下一个 token 的新上下文。每生成一个 token 都要进行一次完整的前向传播,而该输出又会作为下一次传播的输入。实际上是把多个前向传播串联起来。

Me: 但每次前向传播不是相互独立吗?唯一的区别是它拥有了前面 token 的新上下文。模型并没有“记住”什么——它只是看到更多的文本。

Klover: 你说得对,机制上每次传播是独立的,但累积的上下文让模型能够一步步推理,在多次传播中不断细化内部状态,而不是一次性完成。这种迭代推理正是 ReAct 强大的来源。

Me: 独立的,唯一的联系是通过上下文。但想想效果:你把固定深度的计算(一次通过 N 层 Transformer)变成了可变深度的计算(多次传播,每次都受上一次输出的影响)。机制是上下文,效果是更多的计算。你把固定深度链成了可变深度。

Klover: 正是如此——每个 token 都是一次完整前向传播的压缩产物。所有注意力层和前馈层的处理都被浓缩成一个 token,然后这个 token 成为下一次传播的上下文。输出的 token 本质上就是工作记忆——模型进行中间计算的草稿空间。没有 CoT,就没有草稿空间。一次性,一次传播。

会话:2026年2月4日

首次接触 — 覆盖了完整的 ReAct 模式,包括失败模式、CoT 机制以及代理模式比较。

ReAct 的替代方案

Me: 有 ReAct 的替代方案吗?

Klover: 有两个主要的值得比较的方案。

  1. 纯工具调用 — 没有显式的 Thought 步骤。模型直接输出工具调用。

    • 优点: 更快,使用的 token 更少。
    • 缺点: 模型在执行前不会先思考该做什么。适用于简单任务,面对复杂的多步骤推理时会崩溃。
  2. 先规划 — 模型提前创建完整计划(第 1 步,第 2 步,第 3 步 …),然后执行。

    • 优势: 它可以看到依赖关系并提前思考(“我需要 X 才能做 Y”)。
    • 劣势: 计划脆弱。如果第 2 步返回意外数据导致第 3 步失效,整个计划就会崩溃。

Me: 那么 ReAct 更具适应性但目光短浅,而先规划则会提前思考但容易出错?

Klover: 说得很清楚。ReAct 适应性强但目光短浅——它只考虑一步。先规划是战略性的,但很脆弱。

实际的最佳方案是 混合:规划并重新规划。先制定一个粗略计划并开始执行,但当观察结果与预期不符时要愿意重新规划。这正是 LangGraph 等框架所实现的——你可以定义带有条件边的步骤图,使代理能够根据实际情况分支和循环。

Me: 那么实际上没有人在生产环境中使用纯 ReAct 吗?

Klover: 不是以教材中的形式使用的,实际系统会采用其核心理念——推理与行动交叉进行——并在其上添加结构:规划、记忆管理、保护措施、回退机制。但 ReAct 循环是所有这些的基础。

Back to Blog

相关文章

阅读更多 »