Single-Rollout Asynchronous Optimization for Agentic Reinforcement Learning
异步 RL 已经解决了"快"的问题,但没解决"稳"和"好"的问题。SAO 的主张是:把 GRPO 的组内采样换成每条 prompt 只采一条轨迹,再用一套价值模型工程把随之而来的方差压住,同时用更激进的双侧 token 级裁剪控制 off-policy 漂移。结果是可以稳定训练一千步,并在数学推理与 SWE-Bench 上全面超过 GRPO。
主流 LLM RL 管线仍是同步、批次交错的:策略先生成一整批 rollout,全部收齐后才开始优化。对于 agentic 与代码类任务,轨迹长度差异极大——短的很快跑完,长的成为拖尾,大量 GPU 在等最慢的那条轨迹时空转。异步 RL 让 rollout 到达即消费,利用率和 wall-clock 都更好。
但异步引入两个新麻烦:
一条轨迹可能由多个版本的 rollout 模型生成。要精确做重要性采样,就得保留一长串历史 checkpoint {πold(1)…πold(N)},工程上不可行;off-policy 程度也变得不可预测,训练容易崩。
GRPO 用组内平均做 advantage,因此整组必须等最慢的那条轨迹才能进训练——异步在这里被人为地重新同步了。而且真实在线环境往往每条 prompt 只给一次反馈,根本凑不出一个组。
GRPO 的组,既是异步管线里的隐式同步屏障,也是在线场景里拿不到的东西。SAO 的做法是干脆去掉组,用价值模型(critic)来提供 baseline。
SAO 的两处核心改动——DIS 与 Skip-Observation GAE——都是直接改在 PPO 的骨架上的。原文第 2 节只用半页带过,这里把原始实现补全,这样后面每一处改动改的是什么、为什么能改,才看得清楚。
本节中,裁剪代理目标、价值损失、GAE 三条式子是论文第 2 节明确写出的;TRPO 动机、熵正则、value clipping、训练循环、RLHF 适配属于 PPO 原论文与主流实现的标准做法,是我补的背景,原文未展开。
策略梯度的基本形式是 ∇J(θ) = E[∇log πθ(a|s) · Â]——朝着"高 advantage 动作"的方向抬概率。问题在于它是严格 on-policy 的:期望必须在当前策略下取,所以每更新一次参数,之前采的数据就全部作废,样本效率极低。
解决办法是重要性采样:用旧策略 πθold 采的数据去估计新策略的目标,代价是乘一个概率比:
这下一批数据可以复用多次了,但新麻烦来了:只有当 πθ 和 πθold 足够接近时这个估计才有效。若不加约束地最大化上式,优化器会把 rt 推到很大,一步走出旧数据的有效范围,训练直接发散。所有后续的裁剪、KL 惩罚、掩码机制,本质上都在处理这一件事。
TRPO 的答案是加硬约束:max LPG(θ) s.t. E[KL(πθold ‖ πθ)] ≤ δ。理论漂亮,但要解带约束的优化问题,需要共轭梯度 + Fisher 向量积,实现复杂、和参数共享/dropout 之类的结构也不好配合。
PPO 的想法是:与其把信任域写成约束,不如把它编进目标函数本身——让"走太远"变成一件没有额外收益的事,这样普通的一阶 SGD 自己就不会往那边走。于是有了裁剪代理目标:
关键在那个 min:取两项的较小值,等于取真实目标的一个悲观下界。它的效果是只在"对目标有利的方向"上封顶——
Â>0(这个动作比预期好,想抬概率):收益在 r = 1+ε 处封顶,再往上抬也拿不到更多,梯度归零。但向下没有底——r 再小,min 也会选中未裁剪项。Â<0(想压概率):收益在 r = 1−ε 处封顶。但向上没有顶——r 再大,惩罚项照样一路线性下探。换句话说,PPO 的裁剪天生是单侧的,哪一侧生效取决于 Â 的符号。这就是 SAO 后面要动刀的地方:
r=1,即 θ = θold 的起点。虚线段是被 min 封顶的区域——那里 L 关于 θ 是常数,梯度恰好为 0;注意 min 的作用是移除激励而非施加惩罚。两张图各有一条实线尾巴伸出信任域却没有封顶,这正是同步训练里能忍、异步训练里会致命的缺口。实际实现中策略与价值网络常共享主干,因此三项合成一个标量损失:
工程实现里通常还会给价值损失也加一层裁剪,防止 critic 单步跳变:LVF = max( (V−R̂)², (clip(V, Vold−ε, Vold+ε) − R̂)² )。
 该怎么算?两个极端都不好用——单步 TD 偏差大,蒙特卡洛回报方差大。GAE 把两者用 λ 连续地插值起来:
λ=0 时退化为单步 TD(低方差、高偏差);λ=1 时退化为蒙特卡洛优势 R − V(st)(无偏、高方差)。后面 Skip-Observation GAE 动的就是那个递归式里的"下一步"。
SAO 沿用 VAPO 的做法,把 λ 做成随轨迹长度变化的量。要理解它,先看固定 λ 的病根:λk 是 k 步之外的 TD 残差在 Ât 里的权重,所以有效视野约为 1/(1−λ) 步——一个以 token 计的绝对长度。而 agentic 轨迹的长度差着一两个数量级:
| 终端 reward 传到轨迹开头还剩多少 | 短轨迹 2 400 token | 长轨迹 94 000 token |
|---|---|---|
| 固定 λ = 0.95(视野约 20 token) | ≈ 0 | ≈ 0 |
| 固定 λ = 0.9999(视野约 1 万) | 0.79 | 0.00008 |
| 固定 λ = 0.99999(视野约 10 万) | 0.98 | 0.39 |
没有哪个固定值能同时伺候两端:要么短轨迹退化成纯蒙特卡洛,要么长轨迹的前半段完全收不到结果信号。打个比方,固定 λ 相当于规定"只记得最近二十分钟"——任务半小时够用,任务持续三天就等于开头做的一切都没有功劳可言。
自适应的做法是把绝对视野换成相对视野:不再规定"往回传两万个 token",而是规定"往回传整条轨迹长度的 α 倍"。代入 λ = 1 − 1/(αl):
| 长度自适应,α = 1.5 | 短轨迹 2 400 token | 长轨迹 94 000 token |
|---|---|---|
| 该条轨迹自己算出的 λ | 0.999 72 | 0.999 993 |
| 终端 reward 传到开头还剩 | 0.51 | 0.51 |
所以"自适应"的全部含义是:同一个 batch 里长短轨迹用不同的 λ,但每条轨迹的终端 reward 传到自己开头时剩下的比例相同。轨迹越长,λ 自动调得越贴近 1、衰减越慢,恰好抵消掉路变长。α 是唯一的旋钮,含义是"信号能传几条轨迹那么远":α 越大越接近蒙特卡洛,α→∞ 即 λ=1。
实现上 λ 是一个逐序列的向量而不是标量,且 l 只数模型生成的 token,环境反馈 token 不计入——这与下面 Skip-Observation GAE 跳过观测 token 的递归是同一套口径。
rt ≠ 1 完全是"数据复用 K 个 epoch"制造出来的。第一个 epoch 的第一个 minibatch 上 θ = θold,所有 rt 恰好等于 1,裁剪根本不生效;随着参数在这批数据上被更新,rt 才逐渐偏离 1,裁剪开始起作用。换句话说,同步 PPO 里 off-policy 的程度是可控、可预测、且由自己决定的。而异步 RL 打破的正是这个前提——那里的 off-policy 来自 rollout 引擎与训练侧之间不受控的版本漂移,这就是下一节 DIS 要解决的问题。
st = prompt + 已生成的 token 前缀 (q, y<t),action at = 下一个 token,一条轨迹 = 一次完整生成。动作空间 = 整个词表。rt = 0,只有序列末尾拿到一个标量(reward model 打分,或 SWE-Bench 这种可验证任务的 0/1 判定)。所以中间位置的 δt 其实就是纯粹的估值增量 γVt+1 − Vt。−β·log(πθ/πref) 作为逐 token 的负奖励塞进 rt,防止模型漂离 SFT 模型太远。注意这和上面的 rt(θ) 是两码事:一个是对参考模型的正则,一个是对旧策略的重要性比。把上面的骨架和 SAO 逐条对齐,改动其实只有四处,其余原样保留:
| PPO 组件 | SAO 的处理 |
|---|---|
概率比 rt = πθ/πθold | 改:换成 πθ/πrollout,直接读 rollout 落盘的 log-prob(§03) |
min + clip(单侧封顶) | 改:换成双侧掩码 f(x),出界即归零,与 Â 符号无关(§03) |
| GAE 逐 token 递归 | 改:Skip-Observation,动作直连动作,跳过环境反馈 token(§04) |
LVF 全参更新、每策略步一次 | 改:冻结注意力只更 MoE;每策略步更新 K=2 次(§04) |
| Actor-Critic 架构本身 | 保留 —— 这是 SAO 与 GRPO 路线的根本分野 |
| λ 的取值 | 改用长度自适应 λ = 1 − 1/(αl),沿用 VAPO(见本节 ④) |
| N 个 actor 并行 + K epoch 复用 | 被异步管线取代:轨迹到达即用,每 prompt 一条 |
解耦式 PPO 需要维护三个模型:当前策略 πθ、旧策略 πθold、rollout 策略 πrollout,分别用 πθ/πθold 修正陈旧性、πθold/πrollout 修正训练-推理不一致。SAO 做了两处简化:
rt(θ) = exp(log πθ(at|st) − log πrollout(at|st))。既省掉一次旧策略推理,也免去了历史模型集合的追踪。A>0 且 r>1+εh,或 A<0 且 r<1−εl);SAO 把信任域收紧为 [1−εl, 1+εh],落在区间外的 token 直接从梯度中剔除。{πθold(1)…πθold(N)} 这个集合在实际系统里维护不起。DIS 干脆承认这一点:行为策略就是 rollout 引擎本身,而它每个 token 的 log-prob 在生成时顺手就存下来了,零额外开销。把上一节那两条"不设防的尾巴"换个画法,就能看清 DIS 改掉了什么。PPO 的封顶哪一侧生效取决于 Â 的符号,于是在最该拦截的极端 off-policy 区域恰好漏掉一半;DIS 则不看符号,只看 r 是否落在信任域内:
Â<0 且 r 极大时,PPO 会以权重 r(可能是几十上百)推一个负向梯度——这正是异步下训练崩溃的典型来源。DIS 把两条尾巴一起清零,与 Â 的符号无关。论文实测超参:数学推理 εl=0.3, εh=5.0(信任域 0.7–6.0),代码智能体 εl=0.8, εh=3.0(信任域 0.2–4.0)——区间本身并不窄,所谓"激进"体现在出界即归零,而不是把边界收得多紧。这也顺带解释了图 4(c):VAPO 的 clip ratio 近乎恒为零,不是因为它没有发散 token,而是它压根没在拦。这与 Ling Team 的 IcePop 机制思路相近,但 SAO 更简单:连 πθold 一起去掉,仍能稳定训练。代价是接受一定的受控 off-policy 偏差,换来的是计算复杂度大幅下降,以及避免"用单个可能已陈旧的 old policy"带来的误差。
换成每 prompt 一条轨迹后,轨迹一生成完就能立刻进训练,off-policy 程度最小化。但代价是梯度方差高(类似 REINFORCE),必须靠一个足够好的价值模型来压。论文给出四条实用设计:
单轨迹 RL 的主要不稳定来源,是策略与价值函数之间的相互依赖:Vφ 不准 → advantage 噪声大 → 破坏性的策略更新。因此解耦二者的更新频率:策略每更新 1 次,价值网络更新 K 次(实验取 K = 2),让价值估计先追上当前策略再用于计算 advantage。
前期实验发现价值模型的梯度范数显著大于策略模型,进一步分解显示不稳定主要来自全注意力层,MoE 层则相对稳定。于是 RL 阶段冻结 Vφ 的注意力模块,只优化 MoE 投影层。作者的假设是:预训练的注意力权重已具备足够的语义定位能力,限制优化范围本身就是一种正则化。
Agentic 轨迹的结构是 T = [a₀, o₀, a₁, o₁, …],其中 a 是模型动作、o 是环境反馈。标准 GAE 会计算相邻 token 间的价值差,但从"动作末尾 → 观测开头"这个跨越是模型视角下的断点——o 并非模型生成,在此处算 advantage 等于让价值模型去预测一个外部环境状态,纯属引入噪声。
SAO 的做法是修改 Bellman 目标,跳过环境反馈 token,把当前动作的价值直接接到下一个动作的价值上:
理解这个公式的关键不在数学,而在"时间轴上的下一步是谁"被重新定义了。标准 GAE 的递归 Ât = δt + γλÂt+1 中,Ât 完全由 t+1 处的 V 与  决定。在 agentic 轨迹上,动作最后一个 token 的"t+1"恰好是观测的第一个 token——于是 credit 传递被迫穿过整个环境输出块。SAO 把这条链改成动作直接接动作:
Ât 依赖 Ât+1,所以箭头向左)。上图中,从 a₁ 末位 token 到 a₂ 首位 token 之间隔着 M 个环境 token,这 M 段递归既不对应任何模型决策,又要求 critic 去估计"环境输出到一半"时的期望回报——纯噪声,却实打实地进了 Â(a₁,ₙ)。下图把这段整体跳过:a₁,ₙ 的下一步直接是 a₂,₀,展开后每一项都对应模型真实做出的一次选择。要强调的是,跳过只发生在动作↔观测的边界上。动作块内部相邻 token 之间仍是老老实实的逐 token 递归,所以这依然是 token 级 GAE,而不是把一个 turn 压成一个点——后者正是下面消融掉的 step-level 方案。
附录还比较了另一种思路——把每个 agent step 当作一个 action 做 step-level value/GAE(step 平均值 或 末 token 值)。结论是两种 step-level 变体都不如 token-level(同为 400 步:85.8 / 87.3 vs 89.8 on AIME2025),作者归因于 token 级监督信号更细粒度,更能捕捉复杂推理轨迹中的逻辑转折。
价值估计的"冷启动"是主要瓶颈之一。显著扩大价值预训练语料规模,能在训练早期就提供稳健的初始化,让单轨迹与快价值更新机制真正发挥作用。
§07 的超参表里 λcritic = 1 与 λpolicy = 1 − 1/(αl) 并列,容易被当成笔误,其实是刻意解耦。GAE 那趟递归产出两样东西:给 actor 的 advantage,和给 critic 的回归目标 R̂t = Ât + V(st)。教科书 PPO 里一个 λ 同时决定两者,VAPO 与 SAO 把它们拆开,各走各的 λ,代价是多一趟无梯度的递归。
λcritic = 1 时整条求和 telescoping 抵消,每个 token 的回归目标就等于这条轨迹最终拿到的那个 reward。用四个 token 的小例子看得最清楚,最终 reward = 1:
| 位置 t | critic 当前预测 Vt | λcritic=1 的目标 | λcritic=0.5 的目标 |
|---|---|---|---|
| 0 | 0.3 | 1.00 | 0.64 |
| 1 | 0.5 | 1.00 | 0.78 |
| 2 | 0.6 | 1.00 | 0.95 |
| 3 | 0.9 | 1.00 | 1.00 |
右边那一列是问题所在:λ 一旦小于 1,目标就被拉向 critic 自己的预测——t=0 处的 0.64 里有 0.30 直接来自它自己输出的 V₀。目标里含着自己,于是两件坏事同时发生:explained variance 虚高(等于在考核它预测一个部分由自己写出的答案),以及冷启动的 critic 会把自己的错误自我确认下来。λcritic=1 让目标是百分之百的真实结果,这对一个随机初始化的 value head 尤其要紧。
因为 λ=1 本身已经是长度无关的。长度自适应要治的病是"λ<1 时有效视野是一个绝对 token 数",而 λ=1 的视野永远是"整条轨迹到底",天然按长度伸缩。把公式写全就看得很清楚:λ = 1 − 1/(αl) 在 α→∞ 时就是 1,λcritic=1 已经在这个家族里,只是站在端点上。对它再套一个有限的 α,等于主动把它从 1 拉下来。
方差账也支持这个选择:终端奖励取值在 [0,1]、中途没有随机奖励累积,蒙特卡洛目标的方差上界只有 0.25,自举能省下的方差有限,却要付出目标被污染的代价。actor 那边情况正相反——单轨迹、无组 baseline,方差是真问题;而偏差只是把 token 之间的相对权重挪一挪,不会自我放大。
本小节非原文内容,是我们在 35B MoE 上复现这套 critic 配方时的分析与实测,放在这里因为它恰好是 ⑤ 的直接推论。
价值模型工程通常拿 explained variance(EV)验收,常见门槛是 EV > 0.4 才允许 actor 开始训练。但 λcritic=1 带来一个容易被忽略的后果:回归目标在一条轨迹内部是常数。于是 token 级 EV 的分母 Var(R) 百分之百来自轨迹之间——
为什么说 advantage 完全由这个形状决定:λpolicy<1 时把 Ât 展开,终端 reward 的权重是 λl−t,其余项全是 critic 相邻预测的差分。按 α=1.5 代入:
| α = 1.5 | 轨迹开头 | 中点 | 末尾 |
|---|---|---|---|
| 终端 reward 在 Ât 里的权重 | 0.51 | 0.72 | 1.00 |
也就是说轨迹前半段的 token,advantage 里有近一半不来自真实结果,而来自 critic 自己那条曲线的起伏。曲线要是噪声,这一半就是噪声。
还有一个判别式的推论。λ=1 时 Ât = R − V(st),只要 V 落在奖励区间内,成功轨迹的每个 token 同号为正、失败轨迹全为负——那等于零信用分配,把浪费掉的几十个 turn 和真正解决问题的那几个 turn 一视同仁地强化。λ<1 才让符号出现差异:
含义很直白:critic 认为位置 0 时局面很好,紧接着崩掉,虽然最后救了回来,位置 0 那个 token 还是要被扣分。符号翻转完全由 critic 曲线的下坡驱动,这正是信用分配唯一的入口。
黄金标准仍然是 MC 真值:从第 k 个 turn 的前缀重新 rollout N 条,用经验均值作为 V*(sk) 去和 critic 预测比对。代价是要能从中途前缀续跑,而且必须连同工作区的文件系统状态一起恢复,否则重放的不是同一个 state。
一个实测提醒:critic 的 score head 是无 squashing 的线性输出,V 并不被限制在奖励区间内。我们在 35B 上实测 values 跨越 −1.27 到 +2.02,所以即便 λ=1,成功轨迹里也会出现负 advantage 的 token——上面"全同号"的说法只在 V 落在 [0,1] 时成立。
数学推理部分:以 Qwen3-30B-A3B-Thinking-2507 为底座,先在 GPT-OSS-120B 产出的 TIR(工具集成推理)数据上 SFT 3 个 epoch,再用该模型初始化策略与价值模型。代码部分直接以 Qwen3-30B-A3B 为起点,用 OpenHands 作为 scaffold。
四个推理基准在"带 Python 工具"设定下评测,baseline 为 Qwen3-30B-A3B 的 SFT 模型;SWE-Bench Verified 的 baseline 为 Qwen3-30B-A3B。SWE-Bench 一栏的 GRPO 为 GRPO (w/ DIS)。
| 模型 / 方法 | AIME2025 | BeyondAIME | HMMT Nov 25 | IMOAnswer |
|---|---|---|---|---|
| 参考模型 | ||||
| Claude-Sonnet-4.5 | 87.0 | 62.0 | 81.7 | 65.8 |
| GPT-5 High | 94.6 | 74.0 | 89.2 | 76.0 |
| GLM-4.7 | 95.7 | — | 93.5 | 82.0 |
| Qwen3-30B-A3B 基线 | ||||
| 原模型(w/ python) | 14.6 | 10.5 | 17.3 | 7.8 |
| 原模型(w/o python) | 85.0 | 63.0 | 76.7 | 55.3 |
| SFT(w/ python) | 80.4 | 53.3 | 75.2 | 53.3 |
| GRPO(w/ python) | 84.2 | 54.8 | 76.0 | 55.8 |
| 本文方法 | ||||
| SAO(ours) | 97.3 | 74.8 | 88.3 | 74.0 |
| SAO(仅用 DIS) | 94.2 | 71.5 | 86.7 | 71.3 |
| GRPO + DIS | 93.5 | 70.8 | 84.0 | 70.0 |
| 变体 | AIME2025 | BeyondAIME |
|---|---|---|
| SAO(完整) | 97.3 | 74.8 |
| 去掉快价值更新(critic 每批只更新 1 次) | 95.0 | 69.8 |
| 去掉冻结注意力(价值模型全参更新) | 90.6 | 74.5 |
| Vanilla VAPO(不用 DIS) | 91.3 | 69.0 |
| Running-mean baseline(滑窗均值代替 critic) | 79.8 | 55.3 |
Running-mean 变体维护每条 prompt 最近 8 个奖励的滑动窗口均值作为 baseline,是"不用参数化价值模型也能做单轨迹 RL"的简单替代。它表现尚可,但与 SAO 差距很大(−17.5 / −19.5),说明一个训练良好的价值模型是必需品而非可选项。
这一节是论文最有说服力的动机论证。真实在线环境每条 prompt 只有一条轨迹反馈,GRPO 这类依赖组内相对奖励的方法结构上就用不了;而 SAO 靠 critic 提供 advantage,天然可以从单条轨迹更新。
作者设计了一个模拟在线写作任务:奖励标准分阶段切换,依次偏好三种文风——可爱系、中二系、古典系。系统提示要求模型从四个候选风格(学术、可爱、中二、古典)中选一个;候选集在前两阶段为「学术 / 可爱 / 中二」,末阶段换为「古典 / 可爱 / 中二」。奖励由 GLM-4.7 作为 LLM judge 给出,r = rquality × rstyle,二者均为 0/1。
每次奖励偏好切换后,SAO 都能快速抑制原先主导的风格并重新对齐到新目标。对照的 Running-Mean 方法(滑窗 128 个最近奖励作为 baseline)则表现出明显的适应滞后——历史窗口的惯性使 baseline 在切换后仍被旧分布的奖励污染,恢复更慢、稳定水平也更低。SAO 的价值模型是状态相关的 baseline,能动态跟踪奖励迁移。
| 项目 | 数学推理(TIR) | 代码智能体(SWE) |
|---|---|---|
| 底座模型 | Qwen3-30B-A3B-Thinking-2507 + TIR SFT | Qwen3-30B-A3B-Thinking-2507(直接训) |
| batch / group size | 128 / 1 | 同左 |
| 最大长度 | 128k tokens | 128k tokens |
| 策略学习率 | 1 × 10⁻⁶ | 同左 |
| 裁剪区间 (εlow, εhigh) | 0.3 / 5.0 | 0.8 / 3.0 |
| 价值模型学习率 | 5 × 10⁻⁶,λcritic=1(§04⑤),10 步 warmup | 同左 |
| GAE | 长度自适应,λpolicy = 1 − 1/(αl),α = 1.5 | 同左 |
| critic 更新频率 K | 2(每批 2 次价值更新) | 同左 |
| 评测设置 | top-p 1.0、temp 1.0、最多 50 轮;AIME/HMMT/IMO 取 16 次均值,BeyondAIME 取 4 次 | OpenHands scaffold,最多 300 轮交互 |
GRPO 对照组为 16 条 prompt × 8 个采样 = 同样的 batch size 128,保证算力对齐。