ReAct:把推理与行动接成环境反馈闭环
原始来源
Shunyu Yao 等,2022 年提交、2023 年发布 ICLR 版本:ReAct: Synergizing Reasoning and Acting in Language Models,本页按 arXiv v3 阅读。
本页是原创学习导读,不含原文复刻;论文版权与完整内容以原始来源为准。
学习目标
读完后,你应该能做到四件事:
- 从输入、状态、动作、观察和停止条件五个角度描述 ReAct,而不是只记住三个英文标签。
- 手工走完一条
Thought → Action → Observation → … → Finish轨迹,并指出每一步究竟改变了什么。 - 解释为什么知识检索任务适合较密集的推理—行动交替,而长程交互任务往往只需要稀疏的计划更新。
- 根据失败轨迹判断问题来自推理、工具、参数、观察还是结束条件。
一句话抓住它
ReAct 把模型生成的语言推理轨迹与环境中的真实动作交错起来:推理决定下一步怎样行动,行动取得的新观察又反过来修正后续推理。
先澄清 Thought 的含义
论文中的
Thought是模型在任务轨迹里生成的可见语言文本。它可以用来记录计划、证据和进度,但不能被当成模型真实、完整的隐藏心理过程。现代系统也不必公开长篇隐藏思维链;短计划、状态摘要或结构化决策理由,同样可以承担闭环中的“语言状态更新”职责。
为什么只“想”或只“做”都不够
设想一个需要查两次资料才能回答的问题。只做 Chain-of-Thought(CoT)时,模型可以写出连贯步骤,却只能依赖参数中已有的知识;如果第一步记错事实,后面的推导可能在错误前提上继续展开。只做 Action 时,模型虽然能搜索,但它必须从一长串问题、搜索结果和历史动作中直接猜下一个命令,容易忘记子目标、重复搜索或过早提交答案。
ReAct 处理的正是这两个缺口:
- reason to act:先用语言分解目标、选择工具、整理参数和决定何时停止。
- act to reason:通过搜索、页面操作或环境动作取得模型原本没有的新事实,再据此更新判断。
论文把一般 Agent 写成“观察上下文后选择动作”的策略。ReAct 的关键改动,是把原来的任务动作空间 扩成 ,其中 是语言空间。属于 的 Thought 不改变外部世界,只把一段有用信息追加到轨迹上下文;属于 的 Action 会作用于环境,并产生下一条 Observation。
先认清五个对象
| 对象 | 在论文中的作用 | 是否改变外部环境 | 工程实现时要保存什么 |
|---|---|---|---|
| Task | 定义目标与约束 | 否 | 原始请求、成功标准、资源限制 |
| Thought | 分解目标、提取观察、跟踪进度或调整计划 | 否 | 简短计划或状态摘要;不要冒充已验证事实 |
| Action | 搜索、查询、点击、移动或提交答案 | 是 | 工具名、参数、调用编号和时间 |
| Observation | 环境对 Action 的返回 | 环境已经发生变化或被查询 | 原始返回、来源、错误码和可信度 |
| Finish | 结束本次轨迹 | 可能提交最终输出 | 最终答案及其依据,或明确的失败状态 |
这里还隐含一个核心状态 context:任务、此前的 Thought、Action 和 Observation 都按顺序进入它。模型的下一次选择必须基于这个更新后的上下文,而不是每轮重新从原问题开始。
完整机制:闭环究竟怎样转起来
flowchart TD Q["Task:目标与验收条件"] --> C["Context:任务 + 当前轨迹"] C --> D{"下一步需要什么?"} D -->|"整理目标、证据或异常"| T["Thought:更新语言状态"] T --> C D -->|"需要外部信息或改变环境"| A["Action:选择工具与参数"] A --> E["Environment:执行动作"] E --> O["Observation:事实、状态或错误"] O --> C D -->|"证据和成功条件已满足"| F["Finish:答案或完成信号"] C -->|"超步数、循环或不可恢复错误"| X["Fail:带原因停止"]
一次循环可以拆成六步:
- 读取当前上下文。 模型看到任务,以及到目前为止已经发生的轨迹。
- 决定是否需要语言状态更新。 如果目标还未分解、观察需要解释或计划必须调整,就生成一段 Thought。Thought 只追加上下文,不会凭空产生环境证据。
- 选择可执行动作。 动作必须来自该任务允许的动作集合,例如
search[entity]、lookup[string]、网页点击或文本游戏命令。 - 让环境执行。 模型本身不能宣布“搜索成功”或“物品已拿到”;执行器实际调用工具或环境。
- 接收原始观察。 搜索片段、页面状态、成功提示或错误信息被追加到上下文,下一轮必须据此决策。
- 检查终止。 满足任务成功条件时调用
finish[...];若达到最大步数、重复无进展或遇到不可恢复错误,则以失败结束,而不是无限循环。
论文在知识任务里使用一个很小的 Wikipedia 动作空间:search[entity] 返回页面开头或相似实体,lookup[string] 在当前页面中继续查找包含给定字符串的句子,finish[answer] 提交答案。这个受限设计很重要:ReAct 不是让模型随意“说一个动作”,而是让语言策略在可执行动作协议里选择。
从头走完一条轨迹
下面是原创教学示例,不是论文中的实验样本。假设本地知识库中有两篇可搜索条目,任务是:
“《Vector Gardens》的第一作者所在实验室由谁创立?”
| 步骤 | 轨迹内容 | 状态发生了什么变化 |
|---|---|---|
| Task | 给出上面的问题 | 建立两个隐含子目标:找第一作者,再找其实验室创立者 |
| Thought 1 | 先查论文条目,确定第一作者及机构 | 形成当前计划;还没有新增事实 |
| Action 1 | search[Vector Gardens] | 请求环境查询知识库 |
| Observation 1 | “第一作者:Lin Wei;单位:River Lab” | 获得可追踪的外部证据,第二个子目标变成查 River Lab |
| Thought 2 | 已知实验室名,下一步查询其历史 | 从观察中提取实体并更新进度 |
| Action 2 | search[River Lab] | 发起第二次查询 |
| Observation 2 | “River Lab 于 2012 年由 Amina Rao 创立” | 取得回答所需事实 |
| Thought 3 | 问题询问创立者,证据指向 Amina Rao | 核对问题与证据是否对齐 |
| Finish | finish[Amina Rao] | 提交答案并结束 |
这个例子里,真正可作为答案依据的是两条 Observation,而不是 Thought 里的判断。Thought 1 如果猜“可能是 Amina Rao”,那也只是待验证假设;只有环境返回证据后才能升级为结论。
如果第一次搜索返回“没有结果”,正确的闭环不是重复完全相同的动作,而是生成一次计划修正,例如改查论文作者、使用标题别名或更换检索工具。环境反馈的价值就在于它能让后续策略与原计划不同。
一个可实现的最小伪代码
def react(task, tools, max_steps=8):
trajectory = [{"type": "task", "content": task}]
for step in range(max_steps):
decision = model_decide(trajectory, allowed_actions=tools.schemas)
if decision.type == "note":
# 可见的短计划或状态摘要;不把它登记成外部事实
trajectory.append({"type": "thought", "content": decision.text})
continue
if decision.type == "finish":
if success_criteria_met(decision.answer, trajectory):
return {"status": "success", "answer": decision.answer,
"trajectory": trajectory}
trajectory.append({"type": "observation",
"content": "finish rejected: evidence incomplete"})
continue
result = tools.execute(decision.tool, decision.arguments)
trajectory.append({"type": "action", "tool": decision.tool,
"arguments": decision.arguments})
trajectory.append({"type": "observation", "content": result})
if repeated_without_progress(trajectory):
return {"status": "failed", "reason": "loop detected",
"trajectory": trajectory}
return {"status": "failed", "reason": "step budget exhausted",
"trajectory": trajectory}这段伪代码比三个标签多做了三件生产系统必需的事:限制动作协议、在提交前检查成功条件、为循环和超步数提供明确失败出口。它仍只是教学骨架;真实系统还要处理权限、超时、不可信工具输出、重试和审计。
为什么有时密集思考,有时稀疏思考
ReAct 不是规定每个 Action 前都必须写一大段 Thought。论文采用了两种不同节奏:
- 知识密集型推理任务:HotpotQA 与 FEVER 需要多跳检索。示例轨迹通常密集地交替 Thought、Action 和 Observation,用 Thought 分解问题、从检索片段提取事实、重写搜索词并综合答案。
- 长程交互决策任务:ALFWorld 和 WebShop 可能包含很多连续动作。如果每次移动都重复计划,会增加上下文和错误机会。论文让模型只在目标分解、子目标完成、遇到异常或需要常识判断时稀疏地产生 Thought,其余时刻直接行动。
因此,“推理越多越好”不是论文结论。更准确的原则是:只在会改变动作选择的决策点更新语言状态。例如打开抽屉后马上取出已经看到的物品,通常不需要重新规划;搜索无结果、物品位置与预期不同或商品属性冲突时,才需要更新计划。
如何诊断一条失败轨迹
| 失败类型 | 轨迹特征 | 应检查什么 | 可能的修复 |
|---|---|---|---|
| 推理/计划错误 | 观察正确,但选择了无关子目标 | 任务分解、证据到结论的映射 | 重写示例、增加结构化子目标 |
| 工具选择错误 | 用计算器解决检索问题,或反过来 | 工具描述和路由规则 | 缩小动作空间、增加工具选择测试 |
| 参数错误 | 工具正确但查询词、对象 ID 错 | 参数是否来自最新 Observation | Schema 校验、实体复制与约束 |
| 无信息观察 | 搜索为空或只返回无关片段 | 检索词是否应改写、工具覆盖是否足够 | 查询改写、备用检索器 |
| 重复循环 | 相同 Action 和 Observation 反复出现 | 是否记录已尝试动作和进展 | 重复检测、强制改计划或停止 |
| 过早 Finish | 尚缺关键证据就提交答案 | 成功标准是否可机器检查 | 引用要求、验收器、缺证据拒绝 |
| 观察不可信 | 工具结果含错误、提示注入或过期信息 | 来源、时间和交叉验证 | 结果隔离、可信度标记、二次验证 |
论文对 HotpotQA 轨迹的人工作误差分析很有启发:ReAct 的失败不只来自幻觉,还常来自推理错误、重复步骤和无信息搜索。也就是说,接入真实知识库不会自动得到正确答案;如果 Agent 不会从坏观察中恢复,闭环仍然会失败。
原典证据应该怎样读
论文用冻结的 PaLM-540B 和少量人工轨迹,在四类任务上比较 Standard、CoT、Act 与 ReAct:
- 在 HotpotQA 上,单独 ReAct 的 EM 为 27.4,略低于 CoT 的 29.4;在 FEVER 上,ReAct 的准确率为 60.9,高于 CoT 的 56.3。结合 ReAct 与 CoT self-consistency 的切换策略在这两个任务上取得了更高结果。这说明外部检索和内部推理有互补性,而不是 ReAct 在所有知识任务上都压倒 CoT。
- 在 ALFWorld 的受控试验中,最佳 ReAct 提示的总体成功率为 71%,高于最佳 Act 的 45% 与论文引用的 BUTLER 37%。论文将优势与目标分解、进度跟踪和常识定位联系起来。
- 在 WebShop 上,ReAct 成功率为 40.0%,Act 为 30.1%,但专家人类为 59.6%。稀疏推理有帮助,系统仍明显没有解决充分探索和查询改写问题。
- HotpotQA 的人工轨迹分析只抽取了有限样本,并且不同错误百分比属于该分析设置,不能当成今天所有工具 Agent 的通用故障率。
证据边界
这些结果来自 2022 年前后的模型、少样本提示、受限动作空间和特定基准。论文证明的是“在这些设置中,把推理轨迹与环境反馈交错起来有系统价值”,不是“任何现代模型加上三个标签就必然变好”,更不是“应该向最终用户公开模型的完整隐藏思维过程”。
原典贡献与今天的工程解释
| 原典在 2022–2023 年直接展示的内容 | 今天可以继承的工程解释 |
|---|---|
| 用少量示例提示冻结模型生成交错的 reasoning trace 与 task action | 用结构化状态摘要、工具调用和环境事件实现同一个闭环,不必复刻文字格式 |
| Wikipedia、文本游戏和网页环境返回 Observation | 工具输出必须经过执行器,模型不能自行编造执行结果 |
| 人能检查轨迹并区分内部判断和外部信息 | 生产日志应区分模型提议、工具证据、验证结果和最终决策 |
| 在单次任务里维护计划和进度 | 跨任务学习、长期记忆和技能积累是其他机制,不能从 ReAct 自动推出 |
ReAct 最持久的贡献不是某个 Prompt,而是一条可检查的控制循环。今天的框架可能把 Thought 改成 plan_update,把 Action 编码成 JSON,把 Observation 存成带来源的事件;只要“决策—执行—真实反馈—再决策”的因果顺序仍在,核心机制就仍然成立。
自测:确认自己真的会用
- Thought 与 Observation 都会进入上下文,它们最关键的区别是什么?
- 为什么 ALFWorld 一类长程任务不需要在每个动作前都生成长 Thought?
- 某 Agent 连续三次执行
search[Front Row],都得到相同空结果。请判断故障类型,并给出下一步与停止条件。 - 请为“查询今天北京天气并决定是否带伞”写出最短完整轨迹,必须包含成功验收条件。
参考答案
- Thought 是模型生成的计划或判断,不会改变外部环境;Observation 是环境执行 Action 后返回的事实、状态或错误。前者是待检验决策材料,后者才是外部反馈。
- 连续移动或操作常可直接执行;只有目标分解、子目标切换、异常和不确定性较高的决策点需要更新计划。过密 Thought 会增加成本、上下文和重复错误。
- 这是“无信息观察 + 重复循环”。下一步应更换查询词或工具,例如查询
Front Row software;若改写后仍无信息,或达到最大检索次数,就以证据不足失败退出。Task:决定是否带伞;Thought:需要当前降雨或短时预报;Action:调用天气工具并传入北京与当天日期;Observation:返回降雨概率、时段和数据时间;Thought:按预先定义阈值判断;Finish:输出建议并引用观察。验收条件是取得未过期的北京当日预报且规则已执行;工具失败或数据过期不能宣告成功。
下一篇:Toolformer:让模型学会调用工具 · 返回 里程碑原典目录