ReAct 是 Agent 的灵魂范式,但它绝不是万能的。把 ReAct 用在错的场景,会得到又慢又贵又不稳的系统。这一节我们坦诚地把 ReAct 的三大局限摊开——延迟、上下文膨胀、错误累积——并讨论什么场景该用、什么场景该换。
ReAct 在「需要推理 + 需要行动 + 环境不确定」的任务上是最佳选择之一。但在以下场景,ReAct 反而是错的:
| 反例场景 | 为什么不该用 ReAct | 该用什么 |
|---|---|---|
| 简单单步查询 | 一步就能完成,何必多轮 | 直接 Function Calling |
| 工具结果可预测的批量任务 | 串行等待浪费 | ReWOO 并行 |
| 纯推理题(无工具) | 不需要 Action | CoT / Self-Consistency |
| 已知固定流程的自动化 | 不需要每步推理 | 工作流引擎 |
| 对延迟极敏感的实时场景 | ReAct 太慢 | 预编译 + 缓存 |
⚠️ 最常见的滥用:给一个简单查询套上 ReAct,结果 Agent「想一步、查一步、想一步」绕好几轮才给出答案。用户体感极差,成本却是直接调用的几倍。ReAct 是为复杂任务设计的,简单任务用它就是过度工程。
ReAct 每一步都要「调 LLM → 等响应 → 调工具 → 等结果 → 再调 LLM」。这个串行链条让 ReAct 的延迟远高于单次调用。
一个 N 步的 ReAct 任务,总延迟约为:
总延迟 ≈ N × (LLM 调用 + 工具调用 + 网络往返)
| 步数 | 典型延迟 |
|---|---|
| 1-3 步 | 几秒 |
| 5-10 步 | 几十秒到一两分钟 |
| 15+ 步 | 几分钟 |
| 场景 | 延迟敏感度 | ReAct 是否合适 |
|---|---|---|
| 实时对话 | 极高 | 不合适(5+ 步用户已不耐烦) |
| 后台批处理 | 低 | 合适 |
| 交互式助手 | 中 | 部分合适(需优化) |
| 离线分析 | 极低 | 完全合适 |
| 策略 | 做法 |
|---|---|
| 并行化 | 用 ReWOO 把无依赖步骤并行 |
| 流式输出 | 边生成边显示,改善用户体感 |
| 预算控制 | 设步数上限,超时降级 |
| 预计算 | 高频查询预缓存结果 |
| 小模型加速 | 简单步骤用小 LLM,复杂步骤用大 LLM |
💡 延迟体感管理:对交互场景,让用户看到 Agent 在做什么比真正降低延迟更重要。流式输出 Thought/Action/Observation 给用户看,能极大改善「等待焦虑」。这是 ReAct 的一个意外优势——它的推理过程外显,天然适合流式展示。
第 4.2 节已讨论过:ReAct 每轮追加 Thought + Action + Observation,上下文单调增长。这一节从「危害」与「根本对策」角度再深化。
<svg viewBox="0 0 720 320" xmlns="http://www.w3.org/2000/svg"> <!-- 坐标轴 --> <line x1="60" y1="280" x2="680" y2="280" stroke="#475569" stroke-width="2"/> <line x1="60" y1="40" x2="60" y2="280" stroke="#475569" stroke-width="2"/> <text x="680" y="300" font-size="12" fill="#475569" text-anchor="end">循环步数 →</text> <text x="40" y="40" font-size="12" fill="#475569" text-anchor="end" transform="rotate(-90 40 40)">↑ 量级</text> <!-- 成本曲线 --> <path d="M 80 240 Q 200 220 320 180 T 640 60" stroke="#db2777" stroke-width="2" fill="none"/> <text x="650" y="60" font-size="11" fill="#db2777">成本/Token</text> <!-- 延迟曲线 --> <path d="M 80 250 Q 200 240 320 220 T 640 130" stroke="#ca8a04" stroke-width="2" fill="none"/> <text x="650" y="130" font-size="11" fill="#ca8a04">延迟</text> <!-- 注意力稀释 --> <path d="M 80 100 Q 200 140 320 200 T 640 270" stroke="#2563eb" stroke-width="2" fill="none"/> <text x="650" y="270" font-size="11" fill="#2563eb">注意力质量</text> <text x="360" y="30" font-size="13" fill="#475569" text-anchor="middle" font-weight="bold">ReAct 步数增加,三方面同时恶化</text> </svg>
步数增加时,成本与延迟上升、注意力质量下降三件事同时发生。这不是线性恶化——到了某个临界点,Agent 质量会断崖式下降。
步数 1-5: 线性增长,质量稳定 步数 5-15: 成本明显,注意力开始稀释 步数 15-25: 接近窗口上限,关键信息可能被遗忘 步数 25+: 断崖,Agent 开始重复、跑偏、忘记目标
⚠️ 不要让 ReAct 跑太长。生产 ReAct 的步数上限通常设在 10-25 步。超过这个范围,与其继续硬撑,不如换策略——任务分解、外部记忆、人类介入。
上下文膨胀的根本对策是把记忆从「上下文」搬到「外部存储」。这是第 5 章的核心主题:
| 方式 | 把什么搬出去 |
|---|---|
| 摘要 | 旧轨迹压缩成摘要 |
| 事实抽取 | 关键事实存到独立 Memory |
| 向量库 | 旧轨迹转入 RAG,按需检索 |
ReAct 是一个串行链条——任何一步出错,错误会沿链条传播并放大。
| 模式 | 机制 | 例子 |
|---|---|---|
| 错误传播 | 上一步错的信息被下一步用作输入 | 查错股票代码 → 后续计算全错 |
| 错误放大 | 错误信息触发更多错误调用 | 错数据 → 错推理 → 错工具调用 → 更错数据 |
| 错误循环 | Agent 反复尝试同一错误方法 | 同样参数调 5 次都失败,但不停换 |
[错误传播示例] Thought 1: 苹果股票代码是 APPL (拼错了,正确是 AAPL) Action 1: get_stock("APPL") Observation 1: Not found Thought 2: 奇怪,可能数据源问题。换个工具查 APPL。 Action 2: search("APPL stock") Observation 2: 没有这只股票 Thought 3: 数据源都坏了,我直接估计苹果股价... (错误从拼写错一路放大成"数据源都坏了")
| 策略 | 做法 |
|---|---|
| 重复检测 | 连续 N 步相似动作则强制换策略 |
| 错误归因 | 失败时让 LLM 分析根本原因 |
| 重置 | 严重偏离时回到上一个检查点 |
| 人类介入 | 连续失败 N 次触发求助 |
💡 错误归因的关键:失败时不能只让 Agent「再试一次」——它会用同样的方法再失败。要让 Agent 分析「为什么失败」——是参数错?工具坏?任务本身不可解?只有归因正确,才能避免循环。这是 Reflexion 思想在单任务内的应用。
把上面的「不该用」反过来,ReAct 适合的判据是:
| 判据 | 解释 |
|---|---|
| ✅ 任务需要多步推理 | 不是一步查询 |
| ✅ 需要调用外部工具 | 不是纯闭卷推理 |
| ✅ 工具结果不确定 | 不能预先打包(否则用 ReWOO) |
| ✅ 步数可控(<25) | 不会跑到断崖 |
| ✅ 延迟可接受 | 不是实时场景 |
| ✅ 需要可解释性 | Thought 外显便于审计 |
满足四条以上,ReAct 是合理选择。
以下任一情况,应当考虑换范式或组合:
| 负向判据 | 换成 |
|---|---|
| ❌ 任务步骤明确、工具结果可预测 | ReWOO / Plan-and-Execute |
| ❌ 纯推理无工具 | CoT / Self-Consistency |
| ❌ 简单单步查询 | 直接 Function Calling |
| ❌ 步数会超过 25 | 任务分解 + 多个 ReAct 子循环 |
| ❌ 延迟极敏感 | 预编译 + 缓存,或离线批处理 |
| ❌ 需要多路探索 | ToT / LATS |
| ❌ 固定流程自动化 | 工作流引擎 |
最后,给一个 ReAct 是否「成熟到能上生产」的自检清单:
| 维度 | 必备项 |
|---|---|
| 格式稳健 | 有解析失败兜底(错误回喂) |
| 上下文管理 | 滑动窗口 + 摘要压缩 |
| 停止条件 | 步数 + 时间 + Token 三道闸 |
| 错误处理 | 重复检测 + 错误归因 |
| 可观测性 | 完整轨迹日志,便于调试 |
| 成本控制 | 单任务预算上限 |
| 降级方案 | 超时/失败时的兜底回答 |
| 人类在环 | 不可逆操作前确认 |
⚠️ ReAct 的成熟度 = 它失败时的优雅程度。一个生产级 ReAct 不是「能跑通演示」,而是「即便失败,也能清晰报告、控制损失、降级响应」。这份清单上的每一项,都是把 ReAct 从「演示」推向「生产」的必经一步。第 8.3 节会进一步讨论 Agent 系统性挑战。
本章深入剖析了 ReAct 这一 LLM Agent 的灵魂范式。从 Thought-Action-Observation 的循环结构(4.1),到 Prompt 模板与上下文管理的工程实现(4.2),再到 Reflexion/Self-Ask/ReWOO/LATS 四大变体(4.3),最后坦诚讨论了它的三大局限与适用边界(4.4)。
读者现在应当能:
下一章(第 5 章)将转向 Agent 的另一核心模块——记忆。ReAct 的上下文膨胀问题、跨任务经验积累问题,都要靠记忆系统来解决。