← 全部文章

Agent 的 ReAct 循环:让模型在「思考」与「行动」之间闭环

从 ReAct 论文出发讲清 Agent 的核心骨架:Thought → Action → Observation 的循环结构、为什么它能减少幻觉,以及工程落地的关键注意事项。

摘要:ReAct(Reasoning + Acting)把「推理轨迹」与「工具调用」交织成一个循环:Thought → Action → Observation,然后重复,直到模型给出最终答案。本文从论文动机讲到循环结构,给出可直接落地的伪代码,并梳理工程落地的取舍。

从两个失败的极端说起

在 ReAct 之前,「让语言模型完成复杂任务」有两条路线,各有明显缺陷:

  • 纯推理(Chain-of-Thought):模型只能「想」,无法访问外部信息或执行工具。答案完全依赖训练数据里的记忆,遇到需要实时数据或精确计算的场景就会出错——而且错得很有自信。
  • 纯行动(Act-only,如 WebGPT 的早期形态):模型会调用搜索、计算器等工具,但没有推理轨迹引导。它容易「走一步看一步」:一次失败的行动会直接污染后续所有步骤,错误像滚雪球一样累积。

ReAct 论文(Yao et al., 2022,标题 ReAct: Synergizing Reasoning and Acting in Language Models)的主张很简单:把两者交替起来。推理轨迹(Thought)引导下一步行动,而工具返回的观察(Observation)反过来修正推理。两者互相校准,形成闭环。

循环结构:Thought → Action → Observation

一次典型的 ReAct 轨迹长这样:

Thought: 我需要查一下天气 API 的返回格式,再决定如何回答。
Action:  search["weather api docs"]
Observation: {"city": "Shanghai", "temp_c": 28, "forecast": {"tomorrow": {"high": 32}}}

Thought: 用户问的是「明天会不会热」,我需要拿到明天的预报。
Action:  search["Shanghai tomorrow weather"]
Observation: {"date": "tomorrow", "high": 32, "condition": "sunny"}

Thought: 明天 32°C 且晴天,可以给出结论了。
Action:  finish["明天上海 32°C,晴天,比较热,建议穿短袖。"]

关键在于:模型的每一步都有依据。它先说明「为什么这样做」(Thought),再执行(Action),最后根据真实结果(Observation)决定下一步。整个循环受两个约束保护——工具调用失败不致命(失败会作为 Observation 返回,模型可以自己纠错),推理跑偏也能被观察结果拉回来。

可落地的伪代码

把上面的循环翻译成代码,核心不超过 20 行:

async function runReAct(goal: string, tools: Tool[], maxIterations = 25): Promise<string> {
  const messages: Message[] = [{ role: "user", content: goal }];

  for (let i = 0; i < maxIterations; i++) {
    // 1. 模型输出:可能包含 Thought + Action,或直接给出最终答案
    const output = await llm.complete(messages);

    if (output.type === "final") {
      return output.answer; // 模型认为任务完成
    }

    // 2. 解析出结构化的工具调用
    const action = parseToolCall(output.text);

    // 3. 执行工具,拿到 Observation
    const result = await executeTool(action.name, action.args);

    // 4. 把这次交互追加回上下文,进入下一轮
    messages.push(
      { role: "assistant", content: output.text },
      { role: "tool", content: result, toolCallId: action.id },
    );
  }

  throw new Error(`达到最大迭代次数 ${maxIterations},任务未完成`);
}

这段代码就是一个最小可用的 Agent 骨架。你正在使用的很多 coding agent(包括本站作者日常使用的工具)本质上都是这个循环的工程化版本:模型在循环里读文件、执行命令、观察结果,直到任务完成或触达迭代上限。

为什么 ReAct 有效

收益 机制
减少幻觉 关键事实来自工具的真实观测,而不是模型的记忆
错误可恢复 一次工具失败只是一条 Observation,模型能据此换策略
可观测、可调试 Thought / Action / Observation 全程留痕,失败可以精确定位到某一轮
复杂任务可分解 推理轨迹天然把大目标拆成一连串可验证的小步骤

工程落地的五个要点

  1. 工具协议要结构化:用 JSON schema 声明工具参数,比让模型自由写 shell 命令可靠得多;解析失败要有重试与报错路径。
  2. 循环上限必须有maxIterations 同时是成本上限和死循环保险,没有它的一次幻觉可能让 token 账单失控。
  3. 错误当作 Observation 返回:工具抛异常时,把错误信息作为正常的 Observation 交给模型,让它自行纠错,而不是中断整个循环。
  4. 流式推送轨迹:把每一轮的 Thought 实时流给 UI,用户能「看着它思考」,体验和信任感都大幅提升。
  5. 持久化 messages:循环状态就是消息数组,落盘后天然支持恢复、回放和审计。

局限与取舍

ReAct 不是银弹,有三个代价要认清:

  • 成本线性增长:每一轮都是一次完整 LLM 调用,token 消耗随迭代次数直线上升。
  • 错误会累积:一旦某条 Observation 被污染(比如工具返回了误导性数据),后续推理都会建立在错误之上。
  • 简单任务不该用:一句话能回答的问题,跑 5 轮循环纯属浪费。这也是很多 Agent 系统引入「路由」的原因——简单任务直接单次回答,困难任务才进入循环。

小结

ReAct 之所以成为 Agent 的主流骨架,是因为它把语言模型最强的部分(推理)和最弱的部分(精确执行、实时信息)用循环补全了:思考引导行动,行动反馈思考。理解了这一个循环,你就理解了 Agent 的「心脏」——剩下的多智能体协作、规划器-执行器、记忆系统,都是在这个循环之上生长的。

下一篇可以聊聊:当单循环不够用时,如何在 ReAct 之上叠加「规划」层,让 Agent 处理更长时间跨度的任务。