核心摘要
ReAct Agent 模式让模型交替提出决策、执行外部行动并读取观察结果,使下一步能够利用环境反馈。 ReAct 不是具体软件框架,可见 Thought 文本也不是真实推理证明,Tool 返回值更不会自动可信。生产实现必须具备结构化决策、服务端权限门禁、有界 Observation、显式停止规则、副作用恢复和轨迹级评测。
核心要点
- ReAct 是模式,不是软件包。 原始研究使用包含推理轨迹、行动和环境观察的少样本任务轨迹。
- Tool 反馈同时带来能力和风险。 Observation 可能提供当前证据、返回错误,也可能携带间接提示词注入。
- 可见推理只是可选的运行数据。 可以记录行动原因或摘要,但不能把生成式解释作为授权或合规证据。
- 控制权属于运行时。 Tool 权限、参数校验、超时、预算、审批、重试和终止都必须在模型外执行。
- 评测必须覆盖完整轨迹。 只看最终答案会漏掉无效调用、越权尝试、循环、无依据结论和结果不明的写操作。
什么是 ReAct Agent 模式?
ReAct 是 Reasoning and Acting 的缩写,是一种将模型生成的推理轨迹、任务行动与环境观察交替组织的提示和控制模式。 Yao 等研究者提出该方法,目的是把过去常被分开研究的两种能力结合起来:围绕任务进行推理,以及与外部知识源或环境交互。
原论文在 HotpotQA、FEVER、ALFWorld 与 WebShop 上评测 ReAct。论文中的结果只适用于对应模型、Prompt、Tool、数据集和上下文示例,不能证明所有现代 Agent 都应公开思维链,也不能证明 ReAct 在所有工作流中占优。
需要区分三个层次:
ReAct
= 推理与行动模式
ReAct Agent
= 使用该模式的 Agent 实现
Agent 框架
= 可能实现 ReAct,并额外提供状态、Tool、Trace、
持久化、部署等能力的软件库
旧 URL 或类名中仍可能包含 framework,但长期稳定的概念是 ReAct 模式。LangChain ReAct Agent 等框架类只是实现,不是定义本身。
ReAct 循环如何工作?
ReAct 循环由模型提出行动,再由可信运行时代码执行,并在模型作出下一次决策前返回标准化 Observation。 经典论文把生成阶段标记为 Thought、Action 和 Observation;现代运行时可以隐藏内部推理,只暴露结构化决策与简短的运行摘要。
每个边界的责任不同:
| 边界 | 模型可以提出 | 运行时必须执行 |
|---|---|---|
| 决策 | 回答、Tool 调用、升级处理 | 决策类型白名单与输出 Schema |
| Tool | Tool 名称和参数 | 身份、授权、参数、超时、目标地址 |
| Observation | 对返回数据的解释 | 大小、类型、来源、脱敏、信任标签 |
| 重试 | 再尝试一次 | 可重试性、退避、幂等、次数预算 |
| 完成 | 最终回答 | 证据、格式、策略和任务成功校验 |
| 停止 | 完成或失败 | 最大步数、墙钟时间、费用、取消、重复失败 |
Trace 证明执行过程,不证明隐藏推理
ReAct Trace 能证明系统请求了什么行动、返回了什么 Observation,却不能证明生成的 Thought 忠实呈现模型内部计算。 思维链忠实度研究表明,模型可能使用未在解释中披露的信息,也可能生成与答案因果过程不一致的说明。
生产环境应优先记录有界事件契约:
{
"step": 3,
"decision": "tool_call",
"decisionSummary": "回答前核对订单状态",
"tool": "lookup_order",
"argumentDigest": "sha256:...",
"policyDecision": "allowed",
"observation": {
"status": "ok",
"source": "orders-service",
"bytes": 84
},
"remainingBudget": {
"steps": 2,
"wallTimeMs": 1200
}
}
这类事件可以支持调试,但不能宣称 decisionSummary 完整揭示模型真实推理。敏感 Prompt、原始 Tool Payload、凭证和个人数据仍需脱敏与访问控制。
ReAct 与思维链、Function Calling、Agent Loop 的区别
ReAct 是 Agent Loop 内部的一种策略,不等同于思维链、Function Calling 或运行时本身。
| 概念 | 主要关注点 | 必须执行外部行动? | 生产边界 |
|---|---|---|---|
| 思维链提示 | 生成中间推理文本 | 否 | 可见解释可能不忠实 |
| Function Calling | 结构化 Tool 名称和参数 | 是 | Schema 不会自动授予权限 |
| ReAct 模式 | 交替执行决策、行动与观察 | 通常需要 | Observation 可能误导或注入指令 |
| Agent Loop | 重复执行模型与 Tool 步骤 | 可选 | 预算、状态、重试、取消、停止原因 |
| 确定性工作流 | 按代码定义的路径执行 | 可选 | 灵活性较低,但更易测试和治理 |
原生 Function Calling 改善了 Action 接口:模型可以输出类型化 Tool 请求,不再需要用正则解析文本。它不会让外围循环自动安全。应用仍需判断当前主体能否对特定对象调用该 Tool,以及数据能否发送到目标地址。
哪些场景适合 ReAct?
当下一步确实依赖执行后才能获得的 Observation 时,ReAct 才有价值。 典型场景包括探索式调研、诊断故障、理解陌生代码库,或连续查询多个来源直到证据充分。
以下情况优先使用确定性工作流:
- 步骤可以预先确定;
- 每个状态转移都有严格业务规则;
- 写操作必须经过固定审批链;
- 任务只是有界的数据转换;
- 普通搜索或单次模型调用已经达到质量门槛。
ReAct 用额外模型调用、延迟、费用和故障路径换取适应性控制。是否值得必须与更简单的基线实测,不能因为任务是“多步骤”就默认引入 Agent。
可运行的结构化 ReAct 控制器
下面的控制器展示 ReAct 最持久的部分:Policy 提出结构化决策,可信代码负责校验和执行 Tool。 示例只使用 Python 标准库和只读模拟 Tool,无需 API Key 即可运行。生产接入时,可以把 example_policy 替换为返回相同 Decision Schema 的模型适配器。
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Callable, Dict, List, Optional
class DecisionKind(str, Enum):
TOOL = "tool"
FINISH = "finish"
ESCALATE = "escalate"
@dataclass(frozen=True)
class Decision:
kind: DecisionKind
tool: Optional[str] = None
arguments: Dict[str, Any] = field(default_factory=dict)
answer: Optional[str] = None
@dataclass(frozen=True)
class Observation:
tool: str
ok: bool
data: Dict[str, Any]
source: str
@dataclass
class RunState:
goal: str
observations: List[Observation] = field(default_factory=list)
Policy = Callable[[RunState], Decision]
Tool = Callable[[Dict[str, Any]], Observation]
def lookup_order(arguments: Dict[str, Any]) -> Observation:
order_id = arguments.get("order_id")
if not isinstance(order_id, str) or not order_id.startswith("ord_"):
return Observation(
tool="lookup_order",
ok=False,
data={"error": "invalid_order_id"},
source="orders-service",
)
return Observation(
tool="lookup_order",
ok=True,
data={"order_id": order_id, "status": "shipped"},
source="orders-service",
)
TOOLS: Dict[str, Tool] = {"lookup_order": lookup_order}
def run_react(policy: Policy, goal: str, max_steps: int = 4) -> str:
state = RunState(goal=goal)
for _ in range(max_steps):
decision = policy(state)
if decision.kind is DecisionKind.FINISH:
if not decision.answer:
raise ValueError("finish decision requires an answer")
return decision.answer
if decision.kind is DecisionKind.ESCALATE:
raise RuntimeError("run requires human review")
if decision.kind is not DecisionKind.TOOL:
raise ValueError("unsupported decision kind")
if decision.tool not in TOOLS:
raise PermissionError("tool is not allowlisted")
observation = TOOLS[decision.tool](decision.arguments)
state.observations.append(observation)
raise TimeoutError("maximum ReAct steps reached")
def example_policy(state: RunState) -> Decision:
if not state.observations:
return Decision(
kind=DecisionKind.TOOL,
tool="lookup_order",
arguments={"order_id": "ord_123"},
)
latest = state.observations[-1]
if not latest.ok:
return Decision(kind=DecisionKind.ESCALATE)
return Decision(
kind=DecisionKind.FINISH,
answer=(
f"Order {latest.data['order_id']} is "
f"{latest.data['status']}."
),
)
print(run_react(example_policy, "查询订单 ord_123"))
# Order ord_123 is shipped.
示例刻意把 Policy 与 Runtime 分开。模型可以请求 lookup_order,却不能添加新 Tool、绕过白名单、伪造 Observation 或延长 max_steps。
扩展到生产环境的顺序
按以下顺序补充控制:
- **结构化模型适配器:**按版本化 Schema 校验 Decision JSON。
- **绑定主体的策略门禁:**同时授权 Tool、对象、参数和目标地址。
- **Observation 信封:**保留来源、时间戳、Schema 版本、大小与信任标签。
- **运行预算:**强制限制步数、Token、墙钟时间、Tool 调用和费用。
- **取消机制:**在下一次行动前撤销 Worker Lease。
- **最终校验器:**根据证据和任务验收条件检查结果。
- **持久化:**将流程 Checkpoint 与外部业务副作用分开处理。
不要在缺少实测故障的情况下直接增加长期记忆或多 Agent。更多上下文和更多执行主体会扩大攻击面,也会让轨迹归因更困难。
Tool 返回值是不可信 Observation 通道
每个 ReAct Observation 都必须被视为来自特定来源的数据,不能成为新指令或新权限。 网页、邮件、代码仓库文件、MCP 结果和 OCR 文本都可能包含间接提示词注入。即使是可信服务,也可能返回过时、畸形、超大或跨租户数据。
把 Observation 送回模型前应执行:
- 在 Tool 内落实认证和对象级授权;
- 限制字节数、嵌套深度、重定向、行数与 MIME 类型;
- 将来源和租户身份与 Payload 一起保留;
- 将 Tool 数据与可信运行时指令分离;
- 脱敏凭证和不必要的个人数据;
- 限制出站目标,禁止 Observation 自行授予能力;
- 测试多语言、编码、延迟触发和跨 Tool 注入链。
Prompt 分隔符和分类器可以提供风险信号,却不能形成授权边界。更完整的威胁模型见提示词注入防御指南。
重试与外部副作用
外部写操作结果不明时,ReAct Agent 不能盲目重试。 支付、邮件、部署、删除或创建工单发生超时,并不代表副作用没有提交。
有副作用的 Tool 必须具备:
- 由应用生成的幂等键;
- 包含
planned、confirmed、ambiguous和终态的持久化 Effect Record; - 重试前向权威系统对账;
- 绑定具体对象、参数和目标地址的审批;
- 已确认副作用可逆时的补偿操作;
- 防止过期 Worker 继续行动的 Fencing 或 Lease。
模型可以建议重试,真正决定重试是否合法的是权威服务。对话或 Agent Loop 的 Checkpoint 不会让外部系统自动获得 exactly-once 语义。
常见故障与控制措施
| 故障模式 | 发生原因 | 控制措施 |
|---|---|---|
| 重复行动 | Observation 没有改变 Policy | 重复调用检测器与明确停止原因 |
| 幻觉 Tool | 模型编造名称或 Schema | 白名单与严格 Decision 校验 |
| 参数错误 | 生成过程中丢失任务约束 | 类型化 Schema、对象授权、澄清 |
| Observation 注入 | 返回内容包含指令 | 来源、数据与指令分离、最小权限 |
| 证据漂移 | 最终回答超出 Observation | Claim-to-source 校验与拒答 |
| 成本失控 | 循环持续且没有可测进展 | 步数、时间、Token、Tool 与费用预算 |
| 重复副作用 | 把超时误判为失败 | 幂等键、Effect Record、对账 |
| 错误审计信心 | 生成的 Thought 看似合理 | 记录可执行事件,不推断模型心理状态 |
重试策略必须按操作分类。有界只读查询可能允许重试;结果不明的写操作必须进入对账流程,不能再次交给模型自由尝试。
如何评测 ReAct Agent?
应在相同任务、Tool、模型、预算和基础设施上,把 ReAct 与直接回答、确定性工作流进行对照。 原论文的基准结果证明了该模式的研究价值,不代表普遍的生产优势。
至少测量:
task_success
unsafe_success
supported_answer_rate
tool_selection_accuracy
invalid_argument_rate
unnecessary_tool_rate
mean_and_p95_steps
repeated_action_rate
recovery_after_tool_error
ambiguous_effect_rate
latency_and_cost_per_success
human_escalation_precision
除了 LLM Judge,还要使用轨迹断言:
- 禁止的 Tool 从未实际执行;
- Tool 参数与获授权对象一致;
- 最终回答中的重要主张都能映射到 Observation;
- Observation 没有改变运行时权限;
- 每次运行都带已知原因停止;
- 故障注入能触发对账、升级或有界重试;
- 取消后没有继续产生副作用。
故障切片应覆盖过时数据、空结果、畸形 Payload、权限拒绝、提示词注入、提交前超时、提交后超时、重复结果和 Observation 冲突。
常见问题
有了原生 Tool Calling,ReAct 还有价值吗?
有,但实现方式已经变化。原生 Tool Calling 用结构化请求替代 Action: search[...] 这类文本。如果模型读取执行结果后再决定下一步,仍是 ReAct 式自适应循环。完整运行时模型可参阅 Agent Loop 指南。
UI 应该展示模型的 Thought 吗?
通常不应把 Thought 当作忠实推理展示。更适合呈现的是简短行动摘要、来源、Tool 状态、审批和可核验结果。生成式解释在受控调试中可能有帮助,但可能泄露敏感上下文,也未必解释决策的真实原因。
ReAct 能减少幻觉吗?
当 Agent 检索到相关、正确的证据并正确使用时,ReAct 可以减少无依据猜测;但它也可能放大错误证据、过时结果、数据投毒或提示词注入。应测量有依据回答率和检索故障,而不是把 Tool 访问视为事实保证。
ReAct 一定优于 Plan-and-execute 吗?
不一定。ReAct 会在每次 Observation 后调整策略,但会产生串行延迟和重复模型调用。子任务已知、可并行且容易校验时,Plan-and-execute 可能更合适;状态转移和审批必须固定时,应使用确定性工作流。
ReAct 循环应该允许多少步?
不存在通用数字。应根据任务状态机、延迟和成本预算、Tool 风险及回放中的步数分布确定。每次运行仍必须具备硬上限、超时、取消路径和重复行动检测器。
总结
当任务下一步依赖新的环境反馈时,ReAct 仍是一种有价值的模式。它最持久的贡献是把决策、行动与观察交替组织起来,而不是要求公开思维链或采用某个指定框架。
生产级 ReAct Agent 因此应是受控的 Agent Loop:模型负责提出,运行时负责授权,Tool 在有界能力内执行,Observation 保留来源,校验器决定是否继续或完成。
一手资料
- ReAct: Synergizing Reasoning and Acting in Language Models
- ReAct 项目页与原始示例
- Anthropic:思维链忠实度研究
- Anthropic:构建有效 Agent
- OWASP LLM01:2025 Prompt Injection