ReAct(Reason + Act)的核心是把”思考”和”行动”塞进同一个 LLM 解码流——每一步生成 Thought 推理、Action 工具调用,再把 Observation 反馈回上下文,循环往复直到 Finish。在内部 Agent 跑生产时翻车过几次,纯 CoT 经常幻觉事实、纯 Action 不会分解目标,ReAct 把两个失败模式同时治住。下面把范式结构、可靠性机制、生产踩坑点拆开讲清。
一、ReAct 解决了什么具体问题
ReAct 论文(Yao et al., 2022,ICLR 2023)针对的是两类失败模式:
- 纯 CoT(只思考不行动):推理完全发生在模型内部,与外部世界无交互。优势是擅长算术、常识、符号推理;劣势是知识型任务上幻觉严重——尤其在多跳问答里,编一个桥接实体就整条链废掉。Google Research 的论文里直接给出结论:CoT 在 HotpotQA 上的事实核查准确率是 56.3%,而 ReAct 是 60.9%。
- 纯 Action(只行动不思考):把文本上下文映射到文本动作,行动直接但无高层规划。WebGPT、SayCan 都是这条路。代价是模型不会分解目标、不会跟踪子目标、碰到意外 Observation 不会回退。
ReAct 的核心主张是这两类失败是互补的——给模型”思考用的草稿纸”和”行动用的手”,放在同一段轨迹里,比二者单独跑都强。论文在 HotpotQA、FEVER、ALFWorld、WebShop 四个 benchmark 上验证了这个判断,ALFWorld 上 ReAct 比 Act-only 提升 34 个百分点。
二、ReAct 的三段式循环
ReAct 的标准循环是 Thought → Action → Observation 三段一循环:
- Thought(思考):模型用自然语言写”现在缺什么信息、下一步该查什么”。这一步是 CoT 的延续,承担规划、记忆、异常处理。
- Action(行动):从预定义的小动作词汇表里选一个执行。Wikipedia 问答里是 Search[entity]、Lookup[string]、Finish[answer];ALFWorld 里是 go、take、open 这类游戏动词;WebShop 是 search、click[button] 之类。动作词汇保持小且固定,是 few-shot prompting 能 work 的前提。
- Observation(观察):环境执行 Action 后返回的字符串,append 到 prompt 里但不参与模型生成。
模型在 Thought 和 Action 之间交替——生成 Thought 后生成 Action,Action 触发的 Observation 注入上下文,解码继续,直到 Action 是 Finish[answer] 终止。Thought 和 Action 共享同一个自回归解码流,因此每一步工具调用都受模型自身先验思考的条件化,每条 Observation 都可以推翻仍在进行的计划。
三、ReAct 与 CoT 的对比
ReAct 不是替代 CoT,而是把 CoT 嵌入 Action 循环。下面把两者在任务类型、失败模式、可解释性三个维度对比。
| 维度 | 纯 CoT | 纯 Action | ReAct |
|---|---|---|---|
| 知识型任务 | 易幻觉 | 缺规划 | 推理+外部事实校验 |
| 交互型任务 | 无法行动 | 无高层规划 | 规划+行动 |
| 失败模式 | 编造桥接实体 | 无法回退 | 思考可审计 |
| 可解释性 | 推理可见,事实不可见 | 行动可见,规划不可见 | 思考+行动都可见 |
| 工具调用 | 不可 | 强 | 强(条件化于思考) |
| 上下文管理 | 自包含 | 需维护 action history | Observation 自动注入 |
ReAct + CoT 组合在 HotpotQA 上做到 35.1% 准确率(6-shot),超过纯 CoT 的 29.4%,也超过纯 Act-only 的 25.7%。这条组合也是论文的核心推荐——内部先做 ReAct 的多跳检索、推理遇到瓶颈再退回 CoT 采样投票。
四、生产环境的五个可靠性机制
ReAct 论文里没强调工程化,但生产里必须补五条才能稳住。
4.1 限制最大步数
LLM 在 Thought/Action 之间可能陷入死循环——同一个工具反复调用、返回同样的 Observation。必须强制 max_steps,超过即中断返回。
4.2 工具错误显式注入
工具调用失败时,模型默认行为是”假装 Observation 是合理的”——这正是幻觉的入口。生产里要捕获异常、把错误信息(如 ToolError: timeout after 5s)显式塞回上下文,让模型决定重试或换路径。
4.3 上下文压缩
ReAct 跑长链路会塞满 Observation。生产中定期对历史 Observation 做摘要,把”原文 1000 token 摘要到 100 token”再回填。
4.4 格式一致性
Thought/Action/Observation 的字面前缀必须严格不变。模型对 prompt 格式非常敏感,一个 Action: 写成 Action:(全角冒号)就解析失败。可以用正则或 Pydantic 强制解析。
4.5 Human-in-the-Loop
Google Research 的论文专门做了实验:允许人类在 ReAct 推理过程中编辑 Thought(比如把幻觉句子改对),下游准确率立刻回升。生产中暴露一个”审阅 Thought、批准 Action”的接口,是处理高风险任务的关键兜底。
五、ReAct 在生产中的实现骨架
下面是一段精简的 ReAct Agent 实现,展示 Thought/Action/Observation 的循环与终止逻辑:
import re
REACT_PROMPT = """你是一个 ReAct Agent。按以下格式回应:
Thought: 你的推理
Action: 工具名(参数)
Observation: 系统回填的执行结果
... 重复直到完成 ...
Final Answer: 最终答案
可用工具: {tools}
"""
def run_react_agent(query, tools, llm, max_steps=10):
history = [{"role": "user", "content": REACT_PROMPT.format(tools=tools) + f"\nQuestion: {query}"}]
for step in range(max_steps):
response = llm.complete(history)
history.append({"role": "assistant", "content": response})
if "Final Answer:" in response:
return response.split("Final Answer:")[-1].strip()
m = re.search(r"Action:\s*(\w+)\((.*?)\)\s*$", response, re.S)
if m:
tool_name, tool_input = m.group(1), m.group(2).strip()
try:
observation = tools[tool_name](tool_input)
except Exception as e:
observation = f"ToolError: {e}"
history.append({"role": "user", "content": f"Observation: {observation}"})
else:
return f"解析失败:未找到 Action 或 Final Answer(step={step})"
return "已达最大步数,未得到答案。"
效果上,这段代码展示了 ReAct 的几个工程要点:用正则强制解析 Action、把工具异常显式回填、max_steps 兜底、Final Answer 作为终止信号。生产里再叠加”Observation 摘要压缩””Thought 审计回调””并行工具调用”就能撑住大部分场景。
六、ReAct 之后的演进
ReAct 2022 年提出后,工程界沿三条路径演进:
- 结构化工具调用:OpenAI Function Calling、Anthropic Tool Use 把 ReAct 的”自然语言 Action”换成 JSON schema,减少解析失败。
- 有状态图(Stateful Graph):LangGraph 用图替代 ReAct 的线性循环,支持分支、回退、并行节点。LangGraph 的检查点机制把 ReAct 的”中间状态可恢复”工程化。
- 长期自治 Agent:AutoGPT、MetaGPT、ChatDev 把 ReAct 循环拉长到数百步,配合”任务树 + 子 Agent”管理复杂度。代价是对小模型不友好——循环越长越容易跑偏。
新项目里,ReAct 更像是”协议层”——LangGraph、AutoGen、smolagents、Letta 这些框架的运行内核都能找到 ReAct 的影子。理解 ReAct 才能调好这些上层框架。
到这里,ReAct 范式从论文动机、生产可靠性到代码骨架就讲完了。
常见问题(FAQ)
Q1:ReAct 适合所有 Agent 场景吗?
不适合简单单步任务;多步、需外部信息、有失败回退需求的任务收益最大。
Q2:ReAct 和 CoT 怎么配合?
ReAct 做多跳检索与工具调用,CoT 负责最后推理与汇总,ReAct + CoT 论文里有直接验证。
Q3:ReAct 跑长链路会卡在哪?
容易卡在循环调用、上下文溢出、工具错误幻觉——必须配 max_steps、错误回填、Observation 压缩。