ReAct Inside —— 从 Message 到 State,看懂 AI Agent 的工作原理

很多人第一次接触 ReAct(Reason + Act)时,会以为它只是在 Prompt 里加了 Thought / Action / Observation 三个字段。

但实际上,ReAct 的核心并不是 Prompt 格式,而是 Agent 的状态机(State Machine)

本文从工程实现的角度,讲清楚 ReAct 在 LLM 内部到底是怎么运转的,以及它和现代 Function Calling、Tool Calling 之间的关系。

一、什么是 ReAct?

ReAct(Reason + Act)出自 2022 年的论文《ReAct: Synergizing Reasoning and Acting in Language Models》,作者是 Shunyu Yao 等人,由普林斯顿大学与 Google Research 合作完成。

它的核心思想其实很简单:

让 LLM 在推理(Reason)的过程中,可以随时调用外部工具(Act),再拿工具返回的信息继续推理。

打个比方。传统 LLM 像一个闭卷考试的学生,题目一给,凭脑子里记住的东西一口气把答案写完:

User
    │
    ▼
LLM
    │
    ▼
Answer

ReAct 则像一个开卷、还能上网查资料的学生。遇到不确定的地方,他会先想”我得查一下”,去翻书、查天气、算一笔账,拿到结果再接着往下写:

User
    │
    ▼
LLM
    │
Thought      ← 我该做什么
    │
Action       ← 我去查天气
    │
Tool         ← 工具真正执行
    │
Observation  ← 查到的结果
    │
LLM
    │
Thought      ← 根据结果继续想
    │
Answer

它最大的改变是:

模型不再一次性吐出最终答案,而是可以”思考 → 执行 → 拿到反馈 → 再思考”。

二、很多人最大的误解

几乎所有入门文章都会画这样一张图:

Thought
   ↓
Action
   ↓
Observation

于是很多人得出两个结论:

  • Observation 是 Action 的一部分;
  • Thought、Action、Observation 都只是 Prompt 里的不同字段。

这两个结论都不准确。

要讲清楚,得先区分两个完全不同的概念:

  • Message(消息):Agent 和外界之间真正传递的东西,是通信协议。
  • State(状态):Agent 脑子里的内部状态,描述它”想到哪一步了”。

后面几节,我们就顺着这两个概念把问题拆开。

三、从 Message 的角度看 ReAct

假设用户问了一个很日常的问题:

上海今天适合跑步吗?

在整个过程中,真正产生的 Message 是这几条:

User Message                ← 用户:上海今天适合跑步吗?
        │
        ▼
Assistant Message #1        ← 模型输出
        │
        ├── Thought          我得先查一下天气
        └── Action(weather)  调用 weather("Shanghai")
        │
        ▼
Tool Message                ← 工具返回
        │
        └── Observation      26℃,湿度 90%,有雨
        │
        ▼
Assistant Message #2        ← 模型再次输出
        │
        ├── Thought          下雨又潮湿,不太适合
        └── Final Answer     不太建议,今天有雨

这里有两个关键点:

  • Thought 和 Action 通常在同一条 Assistant Message 里,它们是模型一次输出的两个部分。
  • Observation 不是模型输出的,它是 Tool 返回的一条独立 Message。

也就是说,从 Message 的层面看,参与对话的只有三类角色:User、Assistant、Tool。

四、为什么 Observation 必须独立成一条消息?

先说一个容易混淆的点:从内容上看,Observation 确实就是 Action 的返回值。

比如模型发出动作:

Action: weather("Shanghai")

工具执行后返回:

26℃
Humidity: 90%
Rain: true

这段返回,就是 Observation。

那既然内容上是一回事,论文为什么还要把 Observation 单独拎出来?

关键不在内容,而在 来源

Assistant
    │
    └── Action       来自模型(模型"想要"做什么)

Tool
    │
    └── Observation  来自外部世界(真实发生了什么)

Action 来自模型,Observation 来自真实环境,二者绝对不能由同一个角色生成。

为什么这么较真?因为如果 Observation 也由模型自己写,模型就能假装工具已经执行成功,编造一个根本没发生的结果。

举个例子,假设这是模型自己一口气写出来的:

Action:
Search("Apple CEO")

Observation:
Tim Cook

如果 Observation 也是模型生成的,那它完全可以瞎编 —— 哪怕搜索压根没执行,它也能”查到”一个名字,甚至编出一个错误答案。

所以现代 Agent 一定会把工具的真实返回,作为一条独立 Message 插回上下文。这样模型才被迫面对真实结果,而不是自说自话。

五、为什么 Thought 和 Action 又要分开?

这是另一个容易绕晕的地方。

既然 Thought 和 Action 在同一条 Assistant Message 里:

Assistant Message
    Thought
    Action

论文为什么还要把它们拆开讲?

原因还是回到那两个概念:

  • Message 是通信协议 —— 描述”对外发出了什么”。
  • Thought / Action 是 Agent 的内部状态 —— 描述”脑子里在干什么”。

它们说的是两件事。Thought 和 Action 分别对应决策的两个阶段:

Thought:  我要知道天气          ← Decision(决定做什么)
   ↓
Action:   weather("Shanghai")   ← 模型提出的执行指令

用一句话区分:

  • Thought 是”我决定下一步做什么”;
  • Action 是”我真正发出的执行指令”。

论文真正想表达的,是 LLM 如何一步步做出决策,而不是 API 长什么样。所以它在概念上把决策(Thought)和执行(Action)分开描述。

一个常被忽略的细节:Action 其实跨了两个角色

这里还有一层很多人没注意到的东西:Action 并不是一个单一动作,它内部又分成两半。

  • 第一半:LLM 提出动作。模型只是输出一段”我想调用 weather("Shanghai")“的意图,它本身并不会、也没能力真正去查天气。
  • 第二半:Agent 执行动作。Agent 运行时(也就是我们写的那段代码/框架)解析这段意图,真正去调用天气 API、跑数据库查询、执行 shell 命令。

Observation,就是第二半”执行”之后拿回来的结果

用角色把整条链路串起来会更清楚:

LLM     │  Thought         我得查天气
        │  Action(intent)  我"想"调用 weather("Shanghai")   ← 只是提出
        ▼
Agent   │  执行 Action      真正去调 weather API             ← 真正干活
        │  Observation     26℃,有雨                         ← 执行结果
        ▼
LLM     │  Thought         有雨,不适合

所以”Action → Observation”严格来说不是模型一个人完成的:模型负责提出,Agent 负责执行并取回结果。这也正好呼应第四节——Observation 必须独立,因为它来自 Agent 的真实执行,而不是模型的想象。

Action 是逻辑概念,不等于 function calling

还有一点要强调:Action 是论文里的逻辑概念,它并没有被”焊死”成 AI message 里的某个 function call 字段。

论文中的 Action,本质是”Agent 决定并执行一次对外操作”这个抽象行为。它可以有很多种落地方式:

  • 早期是让模型按格式输出一行文本,比如 Search[Apple CEO],再由 Agent 用正则解析后执行;
  • 现在主流是 function calling / tool calling,模型直接吐出结构化的 tool_calls
  • 也可以是模型输出一段代码,由 Agent 丢进沙箱里跑(Code Act)。

这些都是同一个 Action 概念的不同工程实现。function calling 只是目前最流行的那一种,而不是 Action 的定义本身。把”Action”和”function calling”画等号,恰恰是只看到了 Prompt/Message 层,没看到背后的 State 层。

六、State 才是 ReAct 的真正核心

理解了上面两节,就能看出:真正的 ReAct,本质是一个状态机

Thought
   │
   ▼
Action
   │
   ▼
Observation
   │
   ▼
Thought
   │
   ▼
Action
   │
   ▼
Observation
   │
   ▼
  ...

如果写成代码,大致是这样一个循环:

while not finished:
    thought = llm(history)            # LLM:决策 + 提出动作
    action = choose_tool(thought)     # 取出模型想调用的工具
    observation = run(action)         # Agent:真正执行,拿回结果
    history.append(observation)       # 拼回上下文,进入下一轮

四个要素各司其职:

  • Thought:Agent 当前的决策;
  • Action:Agent 请求执行的动作;
  • Observation:环境给回来的反馈;
  • History:不断累积的上下文。

整个循环反复进行,直到模型认为可以收尾,输出最终答案。

七、现代 Function Calling 里,Thought 去哪了?

如果你用过 OpenAI、Claude、Gemini 的工具调用,会发现它们其实不再输出这样的文本:

Thought:
...

Action:
...

而是直接吐出结构化的工具调用:

{
    "tool_calls": [
        {
            "function": "weather",
            "arguments": {
                "city": "Shanghai"
            }
        }
    ]
}

程序执行工具后,把结果作为一条 tool 消息塞回去:

{
    "role": "tool",
    "content": "26℃, humidity 90%, rain"
}

最后再调一次 LLM 得到最终答案:

User
   ↓
Assistant(tool_call)
   ↓
Tool(result)
   ↓
Assistant(final answer)

整个过程里,已经看不到 Thought 了。

但这不代表 Thought 消失了:

Thought 没有消失,只是从”显式写在 Prompt 里”变成了”模型内部的隐式推理(Hidden Reasoning)”。

现代模型通常不会把这段推理过程直接暴露给开发者(推理模型会把它放进单独的 reasoning 字段)。决策这一步依然存在,只是藏到了模型内部。

八、ReAct Inside:站在 LLM 内部看全流程

如果把视角切到 LLM 内部,整个流程可以画成这样:

                +----------------+
                | User Message   |
                +--------+-------+
                         |
                         ▼
              +-------------------+
              | Internal Reasoning|
              | (Thought)         |
              +--------+----------+
                       |
                       ▼
              +-------------------+
              | Tool Selection    |
              | (Action)          |
              +--------+----------+
                       |
                       ▼
              +-------------------+
              | Tool Execution    |
              +--------+----------+
                       |
                       ▼
              +-------------------+
              | Observation       |
              | (Tool Message)    |
              +--------+----------+
                       |
                       ▼
              +-------------------+
              | Internal Reasoning|
              | (Thought)         |
              +--------+----------+
                       |
                       ▼
                 Final Answer

真正在循环的,是这三个动作:

Reason → Act → Observe → Reason → ...

而不是很多人以为的:

Prompt → Prompt → Prompt → ...

换句话说,循环的主体是状态的流转,而不是一段段文本格式的堆叠。

九、用三个层次理解 ReAct

把前面的内容收一下,可以从三个层次来看 ReAct。

第一层是 Prompt。论文里的 Thought / Action / Observation,只是为了方便把推理轨迹展示出来,是给人看的”展示格式”。

第二层是 Message。现代 Agent 真正交换的消息只有三类:User、Assistant、Tool。这是落到 API 上的”通信协议”。

第三层是 State,也是真正的核心。它描述的是 Agent 内部的状态流转:

Decision(决策)
   ↓
Execution(执行)
   ↓
Environment Feedback(环境反馈)
   ↓
Decision(再决策)

这套状态机,才是 ReAct 的本质。

十、总结

一句话总结 ReAct:

ReAct 不是一种 Prompt 模板,而是一种 Agent 的状态机。

理解它,关键是分清三个层次:

  • Prompt 层Thought / Action / Observation,只是用来表达推理过程的展示格式。
  • Message 层User / Assistant / Tool,是实际的 API 通信协议。
  • State 层Thought → Action → Observation,是 Agent 真正的内部状态机。

现代 Function Calling 虽然不再显式输出 Thought,但底层依然遵循同样的状态转换:

Reason → Act → Observe → Reason → ...

所以可以这样理解二者的关系:

Function Calling 是 ReAct 的工程实现;ReAct 是 Function Calling 的设计思想。

如果觉得这篇文章对你有帮助,欢迎点赞、收藏加关注。后续持续分享更多有价值的内容。你的支持是我创作的最大动力!

Comments

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注