文章

ReAct 论文阅读:把语言模型从回答器变成会行动的 Agent

ReAct 论文阅读:把语言模型从回答器变成会行动的 Agent

原文信息

  • 论文:ReAct: Synergizing Reasoning and Acting in Language Models
  • 作者:Shunyu Yao, Jeffrey Zhao, Dian Yu, Nan Du, Izhak Shafran, Karthik Narasimhan, Yuan Cao
  • 会议:ICLR 2023
  • arXiv:2210.03629
  • 项目页:react-lm.github.io

这篇文章是现代 LLM Agent 很重要的起点之一。它真正提出的问题不是“怎么让模型回答得更好”,而是:

一个语言模型如何在任务过程中一边推理,一边行动,并根据外部反馈继续调整后续行为?

先澄清几个概念

ReAct 里的 Reasoning 不是一个单独的 Python 工具,也不是模型内部显式存在的 reasoning.py 模块。它在论文里主要表现为模型生成的自然语言 Thought。

Thought 的作用不是直接改变外部环境,而是写入当前上下文,帮助模型维护计划、记录状态、分解目标、解释观察结果,并决定下一步动作。

Action 则是真正作用于外部环境的操作,例如:

1
2
3
Search[entity]
Lookup[string]
Finish[answer]

或者在文本游戏里:

1
2
3
go to cabinet
open drawer
take knife

Observation 是环境返回给模型的信息。模型下一步会基于完整轨迹继续生成 Thought 或 Action。

所以 ReAct 不是固定的“先想一步,再做一步”,而是允许模型在轨迹里交错插入推理和行动。

ReAct 要解决什么问题

论文对比了两类已有范式。

第一类是 Reasoning-only,典型代表是 Chain-of-Thought prompting。模型会生成中间推理步骤,但它主要依赖内部参数知识。问题是它没有外部 grounding,容易出现事实幻觉,也可能在错误推理链上继续传播错误。

第二类是 Acting-only。模型可以调用搜索、点击网页、移动、拿取物体等动作,但缺少显式计划和状态追踪。它可能会重复无效动作,也可能忘记当前目标和已经完成的子目标。

ReAct 的核心想法是把两者合在一起:

1
2
3
4
5
reason to act:
    用推理决定下一步该做什么。

act to reason:
    用行动获取外部信息,再把观察结果纳入后续推理。

这就是它标题里的 synergizing reasoning and acting。

方法:把 Thought 也放进 Agent 轨迹

论文从一个通用 agent 设定出发:agent 在每个时间步接收观察,基于历史上下文选择动作。

普通 agent 的动作空间可以写成:

1
A = {Search, Lookup, Click, Move, Take, Finish, ...}

ReAct 做的事情是扩展动作空间:

1
A_hat = A ∪ L

其中 L 是语言空间,也就是 Thought。这一步很关键:论文不是把 Thought 当作最终答案前的解释,而是把它当成 agent 轨迹中的一种中间操作。

区别在于:

1
2
3
4
5
Action:
    会作用于外部环境,并得到 Observation。

Thought:
    不作用于外部环境,只写入上下文,影响后续生成。

一个典型 HotpotQA 轨迹类似:

1
2
3
4
5
6
7
8
9
10
11
12
Question: Aside from the Apple Remote, what other device can control Front Row?

Thought 1: I need to search Front Row and find how it can be controlled.
Action 1: Search[Front Row software]
Observation 1: Front Row is a discontinued media center software...

Thought 2: I need to look up what devices control it.
Action 2: Lookup[controlled]
Observation 2: Front Row can be controlled by an Apple Remote or keyboard function keys.

Thought 3: The answer is keyboard function keys.
Action 3: Finish[keyboard function keys]

这里真正重要的是闭环:

1
上下文 -> Thought/Action -> Observation -> 新上下文 -> 下一步

用伪代码理解 ReAct Agent

可以把 ReAct 写成下面这个最小 agent loop:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def react_agent(task, llm, environment, max_steps=10):
    trajectory = []

    for _ in range(max_steps):
        prompt = build_prompt(task, trajectory)
        next_step = llm.generate(prompt)
        step = parse_step(next_step)

        if step.type == "thought":
            trajectory.append({
                "type": "thought",
                "content": step.content,
            })
            continue

        if step.type == "action":
            trajectory.append({
                "type": "action",
                "name": step.name,
                "args": step.args,
            })

            if step.name == "finish":
                return step.args["answer"], trajectory

            observation = environment.step(step.name, step.args)
            trajectory.append({
                "type": "observation",
                "content": observation,
            })
            continue

    return None, trajectory

这个伪代码里有一个容易误解的点:agent runtime 不需要强制每个动作后都生成 Thought。

更准确地说,每一步模型都可以生成:

1
Thought

或者:

1
Action

知识推理任务里 Thought 往往很密集,因为每次检索结果都需要被解释和转化为下一次查询。交互决策任务里 Thought 可以更稀疏,只在目标分解、状态更新、错误恢复、下一阶段规划时出现。

因此 ReAct 不是机械模板:

1
Thought -> Action -> Observation -> Thought -> Action -> Observation

而是更一般的轨迹:

1
Thought / Action / Observation / Action / Observation / Thought / Action ...

实验一:知识密集型推理

论文首先测试 HotpotQA 和 FEVER。

HotpotQA 是多跳问答,需要跨多个 Wikipedia 页面推理。FEVER 是事实验证,需要判断 claim 是 SUPPORTS、REFUTES 还是 NOT ENOUGH INFO。

ReAct 在这两个任务中使用一个简化 Wikipedia API:

1
2
3
4
5
6
7
8
Search[entity]:
    搜索实体页面,返回前几句,或返回相似实体建议。

Lookup[string]:
    在当前页面中查找包含指定字符串的下一句。

Finish[answer]:
    输出最终答案并结束任务。

实验结果里,ReAct 明显优于 Act-only,说明推理能帮助模型更好地使用检索动作和综合最终答案。

但 ReAct 单独并没有在所有场景里压过 CoT。HotpotQA 上,ReAct 的 EM 是 27.4,CoT 是 29.4,CoT-SC 是 33.4。FEVER 上,ReAct 是 60.9,高于 CoT 的 56.3,也略高于 CoT-SC 的 60.4。

最好的结果来自 ReAct 和 CoT-SC 的组合:

方法HotpotQA EMFEVER Acc
Standard28.757.1
CoT29.456.3
CoT-SC33.460.4
Act-only25.758.9
ReAct27.460.9
CoT-SC -> ReAct34.264.6
ReAct -> CoT-SC35.162.0

这个结果很重要。它说明 ReAct 不是简单地“替代 CoT”,而是在某些地方补足 CoT。

可以这样理解:

1
2
3
4
5
6
7
8
9
10
CoT:
    更擅长用模型内部知识组织推理结构,
    但容易事实幻觉。

ReAct:
    更擅长通过外部检索 grounding,
    但依赖搜索结果质量,也受限于动作空间。

ReAct + CoT-SC:
    同时利用内部知识和外部信息。

实验二:交互式决策任务

论文还测试了 ALFWorld 和 WebShop。

ALFWorld 是文本家居环境,agent 需要完成类似“把清洁过的刀放到柜台上”的任务。任务可能有很多地点和物体,需要长程规划、探索、状态追踪和常识判断。

WebShop 是网页购物任务,agent 需要搜索商品、比较属性、选择选项并购买符合要求的商品。

这类任务里,ReAct 的优势更明显。

ALFWorld 上:

方法总成功率
Act best-of-645
ReAct avg57
ReAct best-of-671
ReAct-IM best-of-653
BUTLER best-of-837

WebShop 上:

方法ScoreSuccess Rate
Act62.330.1
ReAct66.640.0
IL59.929.1
IL+RL62.428.7
Human Expert82.159.6

这里的核心结论是:在长程交互任务里,Thought 的价值不只是解释,而是帮助 agent 做状态管理。

比如:

1
2
3
4
我已经找到了刀。
现在需要清洗它。
清洗通常要去 sinkbasin。
清洗完成后再把它放到 countertop。

如果没有这种中间状态,Act-only 很容易连续执行动作但忘记自己为什么这么做。

Failure modes:ReAct 不是万能的

论文有一段很值得看的人类标注分析。它比较了 ReAct 和 CoT 在 HotpotQA 上的成功和失败类型。

ReAct 的优势是幻觉少。成功样本中,ReAct false positive 只有 6%,CoT 是 14%。失败样本中,CoT 有 56% 是 hallucination,而 ReAct 是 0%。

但 ReAct 也有自己的问题:

1
2
3
4
5
6
7
8
Reasoning error:
    Thought 写错,或者无法跳出重复步骤。

Search result error:
    搜索结果为空,或者没有返回有用信息。

Label ambiguity:
    预测其实合理,但和数据集标签不完全匹配。

这说明 ReAct 的可靠性依赖几个条件:

  • 外部工具返回的信息要有用。
  • 模型要能根据 Observation 改写下一步计划。
  • 模型不能陷入重复 Thought/Action。
  • 动作空间本身要足够表达任务需要。

所以 ReAct 降低了纯 CoT 的事实幻觉,但把一部分风险转移到了工具质量、检索策略和闭环控制上。

我对这篇文章的理解

ReAct 的贡献不是发明了工具调用,也不是简单加了一个 Thought: 标签。它真正重要的地方在于把 LLM 从一次性回答器改造成了一个交互式 policy。

在这个视角下,LLM 每次输出的不一定是最终答案,而是下一步轨迹项:

1
2
3
可能是 Thought,用来维护内部计划;
可能是 Action,用来查询或改变外部环境;
可能是 Finish,用来结束任务。

这带来了一个更 general 的 agent mental model:

1
Agent = LLM policy + trajectory context + environment feedback + action space

ReAct 之后,很多 agent 框架本质上都在工程化这个循环:

  • prompt 里定义可用工具;
  • runtime 解析模型输出;
  • 执行工具调用;
  • 把 observation 写回上下文;
  • 继续让模型决定下一步。

后来的 function calling、LangGraph、AutoGen、MCP,都可以看成是在不同层面把这个循环做得更稳定、更结构化、更可控。

总结

如果只用一句话总结 ReAct:

ReAct 把自然语言推理变成 agent 轨迹中的一种中间操作,让模型可以在外部反馈中持续更新计划、状态和行动。

它的关键不是每一步都要显式 Thought,而是 agent 在需要时能用语言维护自己的任务状态,并把外部 Observation 纳入后续决策。

这也是为什么 ReAct 虽然形式简单,却成为 LLM Agent 领域的基础论文:它定义了一个最小但完整的闭环。

本文由作者按照 CC BY 4.0 进行授权