先看全局:难在哪,又是怎么解的
一、困境:四难连锁
改代码、跑终端这类长程 agentic 任务,一条轨迹动辄几万 token、生成又慢。要在这种任务上做 RL,会撞上四个难处,一环扣一环、层层放大,把 GRPO 的地基掀翻:
四条难处叠在一起,逼得只能退回 single-rollout 的 critic PPO 当骨架。那这套骨架整体长什么样?看下半张图。
二、全局解法:一条轨迹的完整训练循环
具体怎么做:一条超长交互撞到上限时,让模型自己写摘要、压成一条分段的逻辑轨迹继续跑(CompactionRL,同时修好分段带来的信用分配错乱);这条轨迹不凑组、单条就异步送进 learner 训练(SAO,同时修好异步带来的策略滞后与价值估计不稳)。两步接在同一条轨迹上,前一步的输出正是后一步的输入。
跟着一条轨迹,走完它的训练生命周期
① 为什么难训 → ② 压缩自己继续跑 → ③ 多段当成一条逻辑轨迹做信用分配 → ④ 异步送去训练 → ⑤ 异步滞后下稳定更新 → ⑥ 证据与边界。
① 为什么 GRPO 在这里不成立
四难里"长"这一环最先发作,先把它放大看——轨迹撑爆窗口只能压缩续写,而压缩一进训练就切出段数段长全乱的多段,GRPO"每题一组等长轨迹"的前提就塌了:
② 轨迹太长:让 agent 压缩记忆、继续跑
第一步落到 rollout 侧。关键一点:摘要不是被动预处理,而是模型自己生成的 token,和执行动作一样进 RL、共享同一个奖励 R——所以它是可训练的。压缩怎么重建窗口、把有效 horizon 撑到约 4×,看下图:
消融佐证:只换摘要器(执行策略不动)SWE 就差 6.5 分(49.0→55.5)——摘要决定后续动作能看到什么,是性能关键的决策,所以非训不可。
③ 把切开的多段,重新当成一条逻辑轨迹
rollout 侧吐出的不再是一条完整轨迹,而是被压缩切开的变长多段(exec + summary)。训练时要把它们重新看成同一条逻辑轨迹——否则段数、段长的乱结构会在两个地方作妖,各配一个修正:
- 长度不均 → token-level loss。若按"段"平均 loss,题A 被切 3 段就在统计里算 3 份,压缩多的题被凭空放大权重。改成所有段的 token 倒进一个大池子、除以 token 总数 $|M|$,切几段都不影响权重。
换分母 = 把"按被切几刀计费"改成"按实际写了多少字计费"。消融去掉它掉分最狠:66.8 → 60.0。
- 跨段信用错乱 → cross-trajectory GAE。每段末尾都挂着同一个终局奖励,靠前的段会被误以为"马上到终点"。按"这段后面还剩多少 token"打个折扣,把它到真正终点的时间距离恢复回来。
错误世界拆成三张独立卡片——分段各自算 GAE 时,每段都把自己的段尾当成"终点"(前两段插伪终点旗 R′₁、R′₂、锚点×1),只有 σ₃ 尾才是真终点。丢失的信息:把三段拼回一整条,σ₁ 尾离真终点还差 1000 token、σ₂ 尾还差 800——这段距离感没了,前段被高估。修正世界:给每段整体乘 (γλ)^N>s 把"还差多远"补回来,前段 advantage 被压回真实大小。
想看两个修正的公式?点开 ↓(PPO 裁剪项标配不重复)
token-level loss(只改归一化分母):
$$ L_\pi = -\frac{1}{|M|} \sum_{(s,i)\in M} \min\!\big( \rho_{s,i}\hat{A}_{s,i},\ \mathrm{clip}(\rho_{s,i},1-\epsilon,1+\epsilon)\hat{A}_{s,i} \big) $$
怎么读:$M$ 是这一 batch 里所有段的所有可训练 token,分母用它的数量 $|M|$ 而非样本/段数。所以"题A 切 3 段"不会被算 3 份,只占 2000 个 token 的份量。$\rho_{s,i}$、$\epsilon$ 是 PPO 原样裁剪项,没动。
cross-trajectory GAE(把终局奖励正确折扣回每段):
$$ \hat{A}_{s,i} = (\gamma\lambda)^{N_{>s}}\, A^{\text{loc}}_{s,i},\qquad N_{>s}=\sum_{j>s} n_j $$
怎么读:先在每段内部照常算局部 GAE 得 $A^{\text{loc}}$,再乘折扣因子 $(\gamma\lambda)^{N_{>s}}$,$N_{>s}$ 是"这段之后还剩多少可训练 token"。因为每段末尾都挂同一终局奖励,靠前的段会被误以为"马上到终点";折扣按真实剩余距离把它推远。合起来 token 的总折扣正好是 $(\gamma\lambda)^{N_{>s}+n_s-i}$ = 它在拼接后完整轨迹里到终点的真实距离。
④ 生成完一条,就异步送进 learner
轨迹在 rollout 侧压缩、切段、拿到终局奖励后,就该送去训练了。但"怎么送"又踩中"慢 / 异步"这条线:要高吞吐就得异步,而异步和 GRPO 的组采样天生打架——下图三条时间线对比:
异步本想"谁跑完先训谁",但 GRPO 的组同步屏障(一组 G 条凑齐才算一个样本)又把它拖回"等最慢那条"。再叠加长轨迹生成期间模型还在更新带来的 policy lag(off-policy 偏差不可预测),SAO 干脆退回 single-rollout:一条就训、不用凑组,从根上绕开组屏障 + online 单反馈两个死结。
🔗 两者最可能的接口在这里。rollout 侧交出的,是一条被压成若干 exec/summary 段的逻辑轨迹,每个 token 还带着当时生成它的 πrollout log-prob(后面 DIS 要用)。SAO 接住的正是这样一条"带日志概率的单轨迹":不凑 GRPO 组、直接异步丢进 buffer 送去更新。前一步(CompactionRL)负责把长任务压成一条可训练轨迹,后一步(SAO)负责让这条单轨迹在异步滞后下稳定地学——两者都建在 single-rollout critic PPO 主干上,机制天然对得上。注意:这个"咬合"是基于两篇机制兼容的合理重构,论文并未同框描述二者如何拼接(详见 ⑥)。
⑤ 在异步滞后下,稳定地更新 policy
轨迹进了 learner,接下来这三招(DIS / 价值三件套 / Skip-Obs)都是为了让这条异步来的单轨迹能稳定地学。DIS · 先解决"重要性采样怎么算"这一环。标准做法要拿旧策略 $\pi_{old}$ 当基准,可异步下一条轨迹是好几个版本接力采出来的、$\pi_{old}$ 根本说不清。
光换基准还不够:用 $\pi_{rollout}$ 近似会引入一点可控的 off-policy 偏差,DIS 的另一半用双侧硬掩码兜住。它跟标准 PPO 的 clip 看着像、干的却相反,下图对着比一遍。
信任域宽到离谱——比 PPO 的 ≈0.2 大一个量级,还随任务调(数学 0.3 / 5.0,SWE-Bench 0.8 / 3.0)。之所以敢开这么宽,全靠越界的极端值被归零删干净,留下的正常区间才放得开。
价值三件套 · 把 critic 训好
单轨迹每题只有一条样本、没有组内比较来平均噪声,方差全靠一个估得准的 critic 压——三件套(地基→追赶→稳梯度)就是"怎么把它训好":
三招都只为一件事:把 critic 估准。单轨迹没有组内比较来平均噪声,advantage 可不可信全押在这个 critic 上——所以 SAO 把工程投入几乎全砸在它身上(值不值,看 ⑥ 的消融)。
还有第四招 Skip-Observation GAE——为什么光把环境反馈 token 挡出 loss 还不够,得连 GAE 递推也一起躲开它,下图拆开看:
关键区分:loss-mask(agentic RL 标准动作)挡的是 policy loss;Skip-Obs 动的是 GAE 递推里的 V(x_next)。前者挡不住后者——mask 掉 loss,V(o) 照样从 δ 的 V(x_next) 溜进 advantage。两条独立的路,这也是为什么"不训练环境 token"还不够。
想看 DIS 与 Skip-Obs 的公式?点开 ↓
DIS 比率 + 硬掩码(丢掉 π_old,只留 π_rollout):
$$ r_t(\theta) = \exp\big(\log\pi_\theta(a_t|s_t) - \log\pi_{rollout}(a_t|s_t)\big) $$
$$ f(x;\epsilon_\ell,\epsilon_h) = \begin{cases} x, & 1-\epsilon_\ell < x < 1+\epsilon_h \\ 0, & \text{otherwise} \end{cases} $$
怎么读:比率直接用 rollout 日志里的 log-prob 相减取指数,绕开旧策略追踪。校准函数 $f$ 是硬开关——窗内原样 $f(x)=x$,窗外归零(不是夹到边界值)。推理任务 $\epsilon_\ell{=}0.3,\epsilon_h{=}5.0$,比标准 PPO 宽一个量级。
Skip-Observation GAE(跳过环境反馈 token):
$$ \delta = r_t + \gamma V(a_{i+1,0}) - V(a_{i,N}),\qquad \hat{A}(a_{i,N}) = \delta + \gamma\lambda\hat{A}(a_{i+1,0}) $$
怎么读:$a_{i,N}$ 是动作 $i$ 的最后一个 token,$a_{i+1,0}$ 是下一个动作的第一个 token(不是紧邻的 observation)。$\delta$ 里的 $V(\cdot)$ 从"紧邻的环境 token"改指到"下一个动作 token",GAE 递推从此不碰任何环境 token 的 value——把环境的随机性隔离在 advantage 之外。