ReAct 与 Agent:多步任务的执行循环、容错与边界

Agent 是大模型应用中最容易被神化的概念。它并不是某个神秘模型,也不是“自动完成一切”的万能系统。从工程角度看,Agent 是一种任务执行架构:用大模型做决策,用工具执行动作,用观察结果反馈下一步,直到任务完成或失败退出。

ReAct 是经典的 Agent 模式之一,它把 Reasoning 和 Acting 结合起来,也就是“思考、行动、观察”的循环。

1. ReAct 的基本循环

ReAct 的核心结构是:

Thought: 分析当前任务需要什么
Action: 选择一个工具或结束动作
Action Input: 给工具的参数
Observation: 工具返回结果
Thought: 基于观察继续判断
...
Final Answer: 最终回答

应用程序在每一轮做三件事:

  1. 把任务、工具说明和历史观察交给模型。
  2. 解析模型输出的动作。
  3. 执行动作并把结果追加回上下文。

这就是 Agent 能处理多步任务的原因。它不是一次生成全部答案,而是在外部反馈中逐步推进。

2. Agent 是系统,不是模型

一个可运行 Agent 至少包含:

  • LLM:负责理解和决策。
  • Prompt:定义任务、工具和输出格式。
  • Tools:提供外部能力。
  • Parser:解析模型动作。
  • Executor:执行工具并管理循环。
  • Memory:保存中间过程。
  • Guardrails:限制权限和风险。
  • Logger:记录每一步行为。

如果只把模型换成更强的,Agent 不一定更稳定。工具描述不清、解析器脆弱、循环无上限、错误不反馈,都会让系统失败。

3. 动作空间越小,Agent 越可靠

Agent 的自由度越高,越容易出现不可控行为。工程上应尽量缩小动作空间:

  • 只提供完成任务必要的工具。
  • 工具名称要清晰可区分。
  • 每次动作只做一件事。
  • 参数结构固定。
  • 不允许调用不存在的工具。
  • 不相关任务直接结束或回复无法处理。

动作空间不是越丰富越好。工具越多,模型选择成本越高,误选概率也会上升。

4. 输出解析决定 Agent 是否能跑起来

ReAct 依赖模型输出固定格式,但模型输出并不总是稳定。可能出现:

  • 缺少 Action 字段。
  • JSON 格式错误。
  • 工具名称拼错。
  • 参数类型不对。
  • 同时输出多个动作。
  • 输出思考文本和 JSON 混在一起。

因此执行器需要强健的解析层。常见做法包括:

  • 使用 Pydantic 定义动作模型。
  • 从响应中提取 JSON 代码块。
  • 对常见错误做规范化。
  • 解析失败时返回默认错误动作。
  • 将错误作为 Observation 反馈模型。

解析层不是辅助功能,而是 Agent 的骨架。没有稳定解析,Agent 循环就无法继续。

5. Observation 是模型与现实世界的接口

工具返回结果会以 Observation 形式进入下一轮。Observation 的质量会影响模型下一步决策。

好的 Observation 应该:

  • 简洁。
  • 明确成功或失败。
  • 包含必要数据。
  • 避免冗余日志。
  • 对错误给出可操作信息。

例如:

{
  "status": "input_required",
  "message": "缺少日期参数"
}

比一句“查询失败”更有用。因为模型可以根据它决定向用户追问日期,而不是盲目重试。

6. 短期记忆与聊天历史要分开

Agent 执行过程中会产生大量中间步骤:

Action(...)
Observation(...)

这些属于任务短期记忆,不一定要永久进入用户聊天历史。聊天历史用于理解用户上下文,短期记忆用于完成当前任务。如果混在一起,后续对话会被工具日志污染。

一个更清晰的设计是:

  • chat_history 保存用户可见对话。
  • scratchpad 保存当前任务步骤。
  • state 保存流程变量。
  • persistent_memory 保存长期偏好或档案。

这种分层能显著提升可维护性。

7. 循环控制:Agent 必须知道何时停止

Agent 最大风险之一是无限循环。常见原因包括:

  • 模型一直选择错误工具。
  • 工具一直返回失败。
  • Prompt 没有明确结束动作。
  • 解析失败后不断重试。
  • 用户问题超出工具范围。

必须设置停止条件:

  • 最大迭代次数。
  • 最大工具调用次数。
  • 连续失败上限。
  • 明确的 Final Answer 动作。
  • 需要用户补充信息时中断。

高质量 Agent 不是永远尝试,而是在合适的时候停下来,并告诉用户需要什么。

8. ReAct 与工具调用 Agent 的关系

Function Calling 解决的是“如何让模型输出工具调用”。ReAct 解决的是“如何组织多轮工具调用和观察反馈”。

可以这样理解:

Function Calling:单次或多次工具调用的接口机制
ReAct:围绕工具调用构建任务执行循环
Agent:包含模型、工具、记忆、执行器和边界控制的完整系统

在简单计算或查询场景中,Function Calling 足够;在需要动态计划、失败重试、多步执行时,ReAct 更合适。

9. Agent 的评估方式

Agent 评估不能只看最终答案,还要看执行路径:

  • 是否选对工具。
  • 参数是否正确。
  • 是否多调用了不必要工具。
  • 工具失败后是否合理恢复。
  • 是否在规定轮数内完成。
  • 是否遵守权限边界。
  • 最终回答是否基于工具结果。

可以记录每条任务的轨迹:

query -> action_1 -> observation_1 -> action_2 -> observation_2 -> final

这比只保存最终回答更有诊断价值。

10. 什么时候不应该使用 Agent

Agent 很有吸引力,但并不是所有问题都适合。以下场景通常不建议优先使用 Agent:

  • 流程固定且只有一两步。
  • 工具调用规则完全确定。
  • 响应时间要求极低。
  • 操作风险很高但缺少确认机制。
  • 输出必须严格可复现。
  • 任务可以用普通程序规则稳定解决。

如果一个需求可以用“参数抽取 + 固定工具调用 + 模板化回答”完成,就不必引入 Agent 循环。Agent 的价值在于动态决策和多步恢复,而不是替代所有业务逻辑。

11. Agent 的工程成熟度模型

可以把 Agent 成熟度分为几个阶段:

  • 演示级:能调用工具,但缺少错误处理。
  • 可用级:有输出解析、工具校验和最大轮数。
  • 工程级:有状态隔离、日志、回放和失败恢复。
  • 生产级:有权限控制、人工确认、评估集和监控指标。

很多系统停留在演示级,却被误认为已经具备生产能力。真正的 Agent 工程重点不是“它能自动做什么”,而是“它做错时系统能否发现、限制和恢复”。

12. 小结

ReAct Agent 的本质是一个受控执行循环:

模型决策 -> 工具执行 -> 结果观察 -> 再次决策 -> 结束或追问

构建 Agent 的重点不是让模型显得“自主”,而是让自主性被工具边界、解析器、状态管理、失败处理和权限控制约束在可用范围内。一个可靠 Agent,一定是有边界、有日志、有停止条件的系统。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐