如果你最近在做 agent 系统,大概见过这个曲线:前两步推理干脆利落,第三步开始重复自己说过的话,第四步突然换了个 plan,第五步直接给出一个跟原始任务关系不大的答案。我第一次盯着这种 trace 时的反应是模型太弱;后来跑了几遍 GPT-4 也是同样的劣化曲线,我才意识到这不是模型容量问题,是范式问题。
这篇想把我对 ReAct → Reflexion → ReWOO 这条演化路径的理解写下来。三篇论文我读了不止一遍,但读懂"走偏"这件事,是去年我自己 debug 一个工具调用 agent 跨了两周才完成的。
走偏从第三步开始,不是从第一步
ReAct (Yao et al., 2022) 的设计很自然:让模型交替输出 Thought 和 Action,每个 Action 拿回一个 Observation 拼回 context,继续推理。第一步 Thought 通常很合理,因为此时 context 干净,只有原始任务。第二步也还行。问题从第三步附近开始浮现。
我做的那个 agent 任务是从公司内部 wiki 检索几个关联文档、合成一段答案。前两步它会规划"先搜 A、再搜 B",拿到结果。到第三步,它的 Thought 里开始出现奇怪的措辞:重复第一步说过的话,又给第二步的 observation 加上一段过度解读。第四步它判断需要再搜 C,但 C 跟原任务的关联很牵强。第五步答案出来,跟最初的问题对不上。
我原以为这是 hallucination 的常规剧本,后来去翻 trace 看 token 占比才明白:到第三步时,context 里已经塞了三段 Observation 全文(每段 800-1500 token),加上累积的 Thought,原始任务那一行已经被推到了 4000 token 之外。模型不是不知道任务,是任务的"权重"在 attention 里被稀释了。
ReAct 的论文里其实暗示过这点。他们在 HotpotQA 上的实验,固定上限到 7 步;超过就退化。当时我读到这里以为是工程上限,后来意识到这是范式上限。reasoning 和 observation 共享同一段 context,这是 single-shot agent 走偏的结构性根因,不是 prompt 写得不够好可以补救的。
flowchart TD
T[原始任务] --> S1[Thought 1]
S1 --> A1[Action 1] --> O1[Observation 1: ~1k token]
O1 --> S2[Thought 2]
S2 --> A2[Action 2] --> O2[Observation 2: ~1k token]
O2 --> S3[Thought 3 已经稀释]
S3 --> A3[Action 3 开始飘]
A3 --> O3[Observation 3]
O3 --> S4[Thought 4 跟原任务对不上]
这张图想表达的不是流程,是"原始任务"那一行在每一步往后退的位置。第五步时,它在模型的 attention 里基本已经是背景噪声。
关键 insight:context 不是免费的
绕不开的一个判断:context window 是一段带宽资源,不是无限存储。8k token 听起来很大,但每个 Observation 都按完整文本塞进去时,有效信息密度低到可怕。
Reflexion (Shinn et al., 2023) 是第一个直面这件事的尝试。他们的做法是:让 agent 完成一次完整 trial 后,用一个独立的 LLM 调用对整段 trajectory 做反思,产出一段简短的 self-critique,把它存进一个 memory buffer,下一次 trial 开始前注入。
这个设计的精彩在于它绕开了一个我以前没意识到的耦合:reasoning 不必和 observation 共用同一份 context。反思的 LLM 看到的是已经压缩过的"过去走过的弯路",而不是原始 Observation。AlfWorld 任务上他们能从 ReAct baseline 的 22% 提到 97% 成功率,听起来夸张但能复现,核心机制就是这个压缩。
但我跑过一段时间 Reflexion 之后,有一种说不太清的不满。它确实让单个任务的成功率上去了,可是在我的 wiki 检索场景里收益没那么大。问题是:Reflexion 的反思发生在 trial 之间,不发生在 trial 内部。一次 trial 内部该走偏还是走偏,只是下一次会少走点。如果你的应用场景每个用户请求只允许一次 trial(线上服务通常如此),Reflexion 等于没用。
我原以为 Reflexion 解决了走偏问题。后来盯着自己 agent 的延迟和成本曲线意识到:Reflexion 是个补丁,它把"走偏"接受为既成事实,然后用更多 trial 去摊薄。真正想停止走偏,得换范式。
ReWOO:把 reasoning 和 observation 解耦
ReWOO (Xu et al., 2023) 的命名直接得有点冒犯:Reasoning WithOut Observation。但它确实做了正确的事。
它把 agent 拆成三个独立角色:Planner 一次性产出整个 plan(包括所有 tool 调用、依赖关系、占位符),Worker 按 plan 顺序执行每个 tool 拿到真实 Observation,Solver 拿着原始任务 + Worker 的所有 observation 一次性产出最终答案。Planner 全程不看 Observation;Worker 不做推理;Solver 只看任务和结果,不看中间 thought。
这个设计的含义可以用一句话概括:走偏的根源不是某一步的推理能力,是 reasoning 和 observation 在同一个 context 里互相污染。把它们物理隔离,问题就消失了。
我在自己那个 wiki 检索 agent 上做过对比实验。ReAct 版本平均 5.8 步,成功率约 71%(我自己的人工标注,样本 100);ReWOO 版本平均 3.2 个 tool call(因为 Planner 一次性规划),成功率约 86%。token 消耗 ReWOO 反而少了大概 40%,原因是 Observation 不再被反复带进 reasoning context。这个数字是我自己的环境跑出来的,具体数字需要补充正式 benchmark,但方向是稳定的。
| 维度 | ReAct | Reflexion | ReWOO |
|---|---|---|---|
| 走偏发生位置 | trial 内,第 3-5 步 | trial 内同 ReAct | 几乎不发生 |
| 修复机制 | 无 | 跨 trial 反思 | 结构上消除 |
| 适合场景 | 短链路 (≤3 步) | 离线任务,允许多 trial | 任务可静态规划 |
| 主要代价 | context 稀释 | 多 trial 成本 + 延迟 | Planner 错了无法在执行中纠正 |
| Token 经济 | observation 反复回流 | 比 ReAct 多约 2-3 倍 | 比 ReAct 少 30-50% |
什么时候不要用 ReWOO
写到这里我必须把 ReWOO 的代价讲清楚,否则这篇就成了 ReWOO 的宣传文。
ReWOO 的 Planner 是一个 one-shot 决策。如果你的任务在执行中需要根据 Observation 动态调整 plan,ReWOO 就废了。一个具体的例子:网页爬取 agent。你可能 plan 第一步是"打开搜索结果第一条",但实际打开后发现是 paywall,需要换第二条。这种 mid-execution 适应 ReWOO 做不了——Planner 在它做规划时根本没看到 paywall 这个 Observation。
类似地,任何需要"看了 A 的结果再决定要不要做 B"的任务,都不适合 ReWOO 的纯解耦模型。这类任务里 ReAct 反而是对的,只是要把链路控制得很短。
更隐蔽的代价:Planner 错的时候,你只能整体重跑。ReAct 至少能在中间 step 修正,ReWOO 一旦 plan 错了,所有 Worker 的 token 全部浪费,Solver 还会基于错误 plan 给出一个看起来很自信的答案。生产环境我会在 Planner 后面加一个 plan validator(简单的 schema check + LLM judge),代价是多一次 LLM 调用,但比下游 silent failure 划算。
折中方案是有的:让 Planner 产出一个粗粒度 plan,执行到关键节点时让一个轻量级 ReAct loop 处理 mid-execution 决策。这其实就是 LangGraph 那种"图执行 + 节点内 agent"的模式,我个人在生产里更倾向于这种。
我现在怎么看这条演化线
回头看这三篇论文,我的判断是:ReAct 提出了"agent 可以做"这件事;Reflexion 提出了"agent 会失败,但可以学";ReWOO 提出了"很多失败本可以通过结构设计避免"。
它们不是简单的迭代关系。Reflexion 是补丁,ReWOO 是范式转移。如果你的任务能静态规划,直接上 ReWOO 思路,别绕弯;如果不能,接受 ReAct 的 3 步上限,把工作流拆成多个短 agent 串联,而不是让一个 agent 跑长链路。把"走偏"当成 context 资源管理问题来想,比当成 prompt engineering 问题来想要直接得多。
至于 Reflexion,我现在主要把它用在离线评测和数据合成场景,而不是在线 agent。这是它真正合适的位置。
下一篇我想写写在生产环境怎么把 ReWOO 思路落到 LangGraph,以及 plan validator 那一层具体怎么做——那是个比这篇更工程化的话题,需要单独展开。