ReAct 状态与编排

一句话本质

ReAct 不是"思考一下 → 调个工具 → 结束",而是 让模型在"状态"的支撑下,循环决定下一步去哪里(分支)、做什么(节点)、要不要再来一轮(回环)


1. 什么是 ReAct

ReAct = Reason(思考)+ Act(行动)

核心思想:让大语言模型像人一样解决问题——不是一次性给答案,而是先思考、再行动、再根据行动结果继续思考,形成闭环循环

1.1 基本流程

text
用户输入 ↓ 存入对话状态(state) ↓ 模型思考 → 判断是否需要调用工具 ↓ ┌─ 不需要工具 ─→ 直接输出答案 → 结束 │ └─ 需要工具 ─→ 生成工具调用参数 → 执行工具 ↓ 工具结果写入 state ↓ 模型继续思考 → 决定是否继续调用工具 ↓ 直到不需要工具 → 结束

1.2 为什么 ReAct 有效

  • 更符合人类解决问题的方式:先想再做
  • 能把复杂任务拆解成多个小步骤
  • 中间过程可解释,可以看到模型为什么这么做
  • 依赖"思维链"能力,模型不断拆解"下一步该做什么"

2. 状态管理:ReAct 的记忆基础

2.1 为什么"思考+行动"不够

"思考+行动"只是表面行为状态才是让这个行为能连续进行下去的记忆基础

2.2 状态里需要记录什么

一本"工作记录本",至少包含:

内容说明
用户最初的问题任务起点
历史对话 / 消息每轮思考和工具结果必须保存,否则下一轮模型无法接上
工具调用结果weather_tool、calculator 等结果,供下一轮推理使用
当前执行到第几步配合 max_steps 防止死循环
是否结束终止条件判断

2.3 最简单的状态结构

json
{ "messages": [ { "role": "user", "content": "183+192-90,顺便判断难度" }, { "role": "assistant","content": "我先算加法" }, { "role": "tool", "name": "add", "content": "375" }, { "role": "assistant","content": "再做减法" }, { "role": "tool", "name": "sub", "content": "285" } ], "step_count": 2, "finished": false, "final_answer": null }

3. 图编排:ReAct 的流程骨架

3.1 什么叫"图编排"

ReAct 的执行过程不是一条死板的直线,而是一个会分支、会循环的结构。只要有分支和回环,本质上就是"图"。

图编排的核心是解决三个问题:

问题回答
下一步去哪里分支(Branch)
这一步做什么节点(Node)
要不要再来一轮回环(Loop)

3.2 图编排不是可视化画图

最小版 ReAct 图:

text
开始 ↓ 模型节点(模型思考 + 生成调用参数) ↓ 分支节点:要不要调用工具? ↓ ┌─ 是 ─→ 工具节点(执行工具) │ ↓ │ 工具结果写入 state │ ↓ └──────────────→ 回到模型节点 ↑(回环) ↓ 否 ─→ 结束节点(输出最终答案)

3.3 分支、节点、回环

分支(Branch)

"路怎么走"的问题:

  • 是否使用工具
  • 用哪个工具
  • 工具执行完后是继续推理还是直接结束

节点(Node)

"每走到一个位置,要做什么"的问题。可插入的节点类型:

节点类型作用
start入口
pre handlerprompt 预处理、安全检查
chat model模型思考节点
tool node工具执行节点
post handler结果校验、清洗、重试
branch条件分支判断
end结束

回环(Loop)

ReAct 和普通线性流程最大的区别:

  • 不是走一遍就结束
  • 而是 模型 → 工具 → 模型 → 工具 → 模型
  • 可以反复循环多轮,直到满足结束条件

4. ReAct vs n8n:核心区别

n8nReAct
性质业务流程自动化给模型一个可循环决策框架
流程人提前把流程基本写死人只搭骨架,模型在骨架里动态决定怎么走
分支决定人预设好模型动态决定
典型场景固定链路:收邮件→解析→写入→通知灵活任务:先查天气→再判断→再给建议

5. 为什么需要图编排

简单场景下 while 循环就够了,但任务复杂后图编排就很有价值:

5.1 更容易加分支

text
需要搜索 → search_tool 需要计算 → calc_tool 需要查数据库 → db_tool 需要人工确认 → human_node

5.2 更容易加中间节点

在模型前加:

  • prompt 预处理
  • 安全检查
  • 上下文裁剪
  • 记忆检索

在工具后加:

  • 工具结果校验
  • 结果清洗
  • 错误重试
  • 总结归纳

5.3 更容易观察和调试

你能清楚看到:

  • 卡在哪个节点
  • 是模型判断错了
  • 还是工具调用失败了
  • 还是状态没更新好

6. 最小实现示例

6.1 伪代码

python
state = init_state(user_input) while state.step_count < max_steps: model_output = call_model(state) if model_output.requires_tool: tool_result = call_tool( model_output.tool_name, model_output.args ) state.messages.append(tool_result) state.step_count += 1 else: state.final_answer = model_output.answer state.finished = True break

这段代码没有画图,但它已经是一个完整的 ReAct 图:

  • 节点1:模型
  • 节点2:工具
  • 节点3:结束
  • 边1:需要工具就去工具节点
  • 边2:工具完成后回模型节点
  • 边3:不需要工具就结束

6.2 Mermaid 流程图

graph TD
    A[开始] --> B[初始化状态]
    B --> C[模型思考]
    C --> D{需要调用工具?}
    D -->|是| E[执行工具]
    E --> F[工具结果写入状态]
    F --> G[步数 +1]
    G --> C
    D -->|否| H[输出最终答案]
    H --> I[结束]
    C -->|步数超限| I
Rendering diagram...

7. 核心配置项

配置项作用
tools / model指定使用哪些工具和模型
message modifier在消息送给模型前做预处理
max_steps限制最多循环多少步,避免死循环
tool return directly某些工具调用后可直接返回
stream tool checker流式输出时检查工具调用是否完整
execute concurrently控制工具是并行还是串行执行

8. 开发者视角的最小理解

把 ReAct 拆解成三件事:

  1. 一个循环 — 模型反复决定要不要继续调用工具
  2. 一个状态对象 — 把消息、工具结果、步数、最终答案都存起来
  3. 一个分支逻辑 — 要工具 → 去工具节点;不要工具 → 结束