递归 Agent 优化(RAO):用 RL 训练 LLM 递归分解任务
作者:AVB(@neural_avb) 原文:Recursive Agent Optimization using RL, explained clearly 论文:arXiv:2605.06639
什么是 RAO
Recursive Agent Optimization(递归 Agent 优化),简称 RAO。这篇论文用强化学习(RL)训练 LLM Agent,使其能够在 Python REPL 中生成并协调递归子 Agent。
可以理解为:递归语言模型 + 强化学习 的结合体
RAO 训练 LLM 如何分而治之(divide and conquer):将大任务切分成小块,委派给子 Agent(这些子 Agent 还可以继续生成和委派更多子 Agent),并在多个正交子问题上并行工作。
实验环境
作者刻意选择了两个非常小的本地模型:
- Dense LM:Qwen3-4B-Instruct
- MoE:Qwen3-VL-30B-A3B-Instruct
并选择了需要仔细规划和分治策略的长程/长上下文任务:
- TextCraft-Synth:Minecraft 风格的合成游戏,Agent 需要收集材料并合成物品。Agent 需要自然发现和解决子问题(如:收集木材 → 制作木板 → 制作木棍)。
- Oolong-Real:包含困难 D&D 转录本的长程任务,转录本长达 10-12 页,是非常困难的检索任务。
- DeepDive:通过在知识图上执行受控游走构建的具有挑战性的 QA 对,需要多跳、迭代网络搜索和综合分散信息。
还做了 ART-E 数据集上的消融研究,Agent 需要在用户收件箱中搜索相关邮件来回答问题。
执行树与生成动作
当语言模型被赋予任务 X 时,它产生一个「轨迹」(trajectory/rollout)——即 Agent 为尝试完成任务所采取的一系列观察与动作序列。
在此过程中,Agent 可能选择在委派子任务 X1、X2 等上生成子 Agent。这创建了一个有根执行树:
- 每个节点 = 一个 Agent 实例解决一个任务
- 根节点 = 原始任务
- 节点的子节点 = 该 Agent 决定委派的子任务
递归通过单个 async 函数实现:
result = await async_launch_subagent(
goal="find all papers about X", ...
)
策略本身决定:
- 是否委派(即调用 launch_subagent)
- 子任务描述是什么(即 goal 是什么)
- 请求什么输出格式
- 是否并行化子节点(通过
await asyncio.gather) - 如何聚合结果(所有结果返回后,主 Agent 必须聚合以形成答案)
选择 REPL 的强大之处:
- 返回类型不受限制——子节点可以返回字符串、字典、结构化对象
- 父节点可以用普通 Python 链式、组合或转换子输出
- 子节点可以顺序运行(如果有依赖)或并发运行(通过 asyncio)
- 最关键:子 Agent 返回输出时,主 Agent 不会直接将其加载到上下文。输出保存在 Python 变量中,Agent 可以在加载到上下文之前操作这些变量(如打印切片、长度或验证键)
REPL 型 Agent 近来流行,因为它们天然支持递归架构 + 审慎的上下文管理。
本地节点奖励(信用分配)
标准 RLVR 中,只有根节点在任务完成时获得奖励信号。在递归设置中,这带来问题:假设任务需要深度 3 的递归树,等到输出传播并获得奖励时,最底层的叶子 Agent 已经相隔数百个 token,信用分配基本失效。
RAO 用本地成功信号解决这个问题。一个 LLM judge(使用 gpt-5-mini)评估子任务输出并生成奖励。
LLM judge 基于两个因素奖励节点:
- Term 1:该节点是否解决了其被分配的任务?(直接成功信号)
- Term 2:该节点的子节点(如果存在)是否在其给定的子任务上成功?(委派奖励信号)
对于 Term 2(委派奖励),作者对子节点的成功率取平均而非求和或计数。这防止了退化策略——Agent 不会为了收集奖励而生成 100 个琐碎子任务。Agent 只因其委派质量而非数量获得奖励。
作者报告,委派奖励在训练早期最有用,此时模型还不知道如何委派。
策略优化目标
RAO 在优化中使用了三个技巧:
1. 多任务目标(Multi-Task Objective)
普通 RL Agent 训练:任务分布 → 采样任务 → 运行 rollout → 计算梯度 → 更新。简单。
但递归 Agent 中,每个 rollout 自动在多个深度生成任务。从单个根任务自然生成深度 1 和深度 2 的额外任务,称为策略诱导子任务分布。
RAO 同时训练每个深度的任务。同一策略(语言模型)被优化为在深度 0(根规划器)、深度 1(中层委派者)和深度 2+(叶子执行器)上都成为好 Agent。
训练目标是结合根任务和子任务的多深度混合。这也是基于课程的学习形式,因为子任务由模型自身生成。
子任务几乎总是比根任务简单——模型只在认为它们是可管理的子问题时才会生成子任务。
训练早期,模型在根任务上失败(太难),但仍能从较简单的深度 1 和深度 2 子任务中获得奖励。随着训练进行和模型能力提升,它自然生成更难更复杂的子任务。无需人工课程设计。
2. Leave-One-Out(LOO)基线
对于每个根任务,RAO 采样 G 个独立的 rollout 树。目标是找出哪些独立树导致高奖励——然后最大化该行为。
为此,必须计算每个 rollout 的优势(advantage)。rollout g 中轨迹 τ 的优势:
A(g) = rollout g 的奖励 - 除 g 外所有其他 rollout 的平均奖励
即 rollout g 的基线就是所有其他 rollout 的根奖励均值。这称为「leave one out」,因为计算基线时不考虑当前轨迹。
每个子 Agent 节点的优势单独计算:
优势 = 本地奖励 - 基线
对于每个子 Agent:
- 计算本地奖励(回顾上文:取决于当前 Agent 和生成子 Agent 的成功)
- 基线使用与 rollout 内所有节点相同的 leave-one-out 基线
- 每个子 Agent 的优势 = 其本地奖励与同组所有其他独立 rollout 平均奖励的比较
- 注意:子 Agent 基线不是相对于同一 rollout 内的兄弟姐妹/父/子节点计算的!基线来自同一任务的所有其他 rollout
3. 深度级逆频率加权(Depth-Level Inverse Frequency Weighting)
实际问题:如果 Agent 生成 4 个子节点,每个子节点又生成 4 个,则有 1+4+16=21 个轨迹节点,但只有 1 个根。如果简单平均所有梯度,叶子节点以 16:1 主导更新,导致模型过度优化为好的叶子执行器,而欠优化为好的根规划器。
需要好的规划(顶部)和好的执行(叶子)。修复方法:按深度级逆频率加权。
设 B_d = 批次中所有深度 d 的轨迹集合。深度 d 每个轨迹的权重:
w = α / |B_d|
其中 α 是确保总批次权重保持不变的归一化常数,|B_d| 是该深度的轨迹数量。
直观上,这为批次中出现更频繁的深度分配较小权重,减少树中人口稠密的层级主导学习的倾向,同时归一化保持整体更新幅度大致不变。
关键实验结果
1. 上下文管理技能在训练后期进化
Oolong-Real 训练中,递归 Agent 被限制在 32K 上下文窗口,但需要处理 55K+ token 的文档。它最初尝试将整个输入文档打印到根上下文,立即填满并崩溃自己的上下文窗口。
然后完全自主地,它放弃了这个策略,学会了正确方法:将输入分块,每块委派给子 Agent。
2. 即使不需要递归,递归也有帮助
即使在完整的 40K 上下文窗口(足以解决任务而不需要递归)下,递归 Agent 仍然比单 Agent 基线训练更快、表现更好。
为什么?因为子 Agent 奖励充当密集过程奖励。每个子任务完成在任务中期就给出一个训练信号,而非只在最后。分而治之仍然 unbeatable。
3. 分而治之是可迁移的
模型只在中等难度 TextCraft 任务上训练。评估时,它泛化到更难的任务。论文假设这是因为分而治之是一种可迁移策略——如果你学会了很好地分解中等问题,你可以递归地应用相同的分解模式更深地解决更难的问题。
4. 30B 模型接近 Claude、o3 和 GPT-5-mini
在 Oolong-Real 上,训练的递归 Agent 平均奖励达到 0.320。一个仅在此特定任务上用 RL 训练的 30B 开放模型,几乎匹配万亿参数前沿模型!
5. 顺序任务更慢但更聪明
在 DeepDive 上,递归 Agent 比单 Agent 慢 18 倍。单 Agent 快是因为它早早放弃并走捷径。递归 Agent 在它能独特解决的困难任务上使用深度 4+,在需要的地方精确分配更多计算。两者都能解决的任务平均深度约 2.9,但只有递归 Agent 能解决的任务平均深度约 4。
6. RAO 在简单任务上也有帮助吗?
作者在 ART-E(简单邮件搜索基准)上测试 RAO。与需要大量顺序依赖网络搜索的 DeepDive 形成鲜明对比。RAO 仍帮助模型在训练早期学得更快。但最终性能趋于一致,即单 Agent 追上了递归 Agent。
这符合直觉:如果任务短、适合一个上下文窗口、不受益于分解,递归增加开销而没有有意义的收益。在 TextCraft-Synth 的各难度级别上也出现相同模式。简单任务上,单 Agent 和 RAO 方法收敛到相似成功率。差距只在中等和困难难度上变得巨大——正是分而治之和扩展视野重要的地方。
训练细节问答
Q:我们是否将整个树作为一个序列更新? A:不。每个节点的轨迹独立更新。哪些 token 获得 logit 更新?只有每个节点自己轨迹中 Assistant 生成的 token。
Q:父节点是否训练子节点的输出 token? A:不。子节点结果保存在 Python REPL 变量中。根 Agent 必须显式打印才能将其加载到上下文。子节点的完整内部推理和轨迹永远不会出现在父节点的上下文中。
Q:给定轨迹的基线如何计算? A:rollout g 的基线在其所有节点间共享。LOO 通常给出无偏基线,因为所讨论轨迹的奖励被排除在基线平均之外。
Q:子 Agent 的优势如何计算? A:本地 Agent 的优势 = 该子 Agent 的本地节点奖励 - 基线奖励。本地节点奖励使用 LLM-as-a-judge 计算,结合节点自身成功和委派奖励(即子节点的成功)。基线使用上述 LOO 策略计算。所有子 Agent 使用相同基线!
写在最后
RAO 的核心洞见是:递归分解不仅是一种实现技巧,更是一种可学习的智能策略。 通过 RL 训练,小模型可以学会何时委派、如何分解、怎样聚合——并在实践中证明,分而治之的能力可以跨任务迁移,甚至让 30B 模型逼近前沿水平。
对于 Agent 系统设计者来说,RAO 提供了一个重要启示:与其追求更大的模型,不如设计更好的分解和协调机制。