GLM-5.2 的长程 agentic RL
是怎么把一条超长轨迹训起来的

拆解基于两篇技术报告:CompactionRL(arXiv:2607.05378)· SAO / Single-Rollout Async Optimization(arXiv:2607.07508)· 清华 & Z.AI · 两篇均声明已落地 GLM-5.2 训练产线

先看全局:难在哪,又是怎么解的

一、困境:四难连锁

改代码、跑终端这类长程 agentic 任务,一条轨迹动辄几万 token、生成又慢。要在这种任务上做 RL,会撞上四个难处,一环扣一环、层层放大,把 GRPO 的地基掀翻:

四难连锁:不是四个并列的坑,而是一环扣一环、层层放大 ① 长 上下文放不下,得压缩 ② 慢 完成久、方差大,得异步 ③ 异步 跨多个策略版本 policy lag ④ 稀疏 通常只有终局一个奖励 压缩→切成变长多段 异步生成期模型还在更新 四条叠加 → GRPO「每题一组等长完整轨迹」前提全线崩塌 只能退回 single-rollout 的 critic PPO 当骨架
四条难处叠在一起,逼得只能退回 single-rollout 的 critic PPO 当骨架。那这套骨架整体长什么样?看下半张图。

二、全局解法:一条轨迹的完整训练循环

具体怎么做:一条超长交互撞到上限时,让模型自己写摘要、压成一条分段的逻辑轨迹继续跑(CompactionRL,同时修好分段带来的信用分配错乱);这条轨迹不凑组、单条就异步送进 learner 训练SAO,同时修好异步带来的策略滞后与价值估计不稳)。两步接在同一条轨迹上,前一步的输出正是后一步的输入。

Rollout 侧 · 线上边采边压缩 接口 Learner 侧 · 异步消化 任务入队 Agent×环境交互产 token 上下文撞顶T_comp 压缩→摘要summary 段 摘要重建窗口继续执行 切多段·循环 终局奖励 R★ 任务成败 一条逻辑轨迹:切成变长多段 exec 1000 sum exec 800 sum exec… 每 token 带 π_rollout log-prob(DIS 用) 段数/段长乱 → 埋两个坑,靠 learner 侧跨段 GAE + token loss 兜 ↘ 异步 Buffer 不凑组 送入 Learner · 单轨迹就训、不等 GRPO 组 这三件联合构成 loss(并用,非先后) Critic 三件套+Skip-Obs · 出 V 跨段 GAE修分段信用·CompactionRL DIS 双侧 IS · 修 lag token 级 PPO 更新 · 分母=全 batch |M|抹平段长差·CompactionRL 更新 policy + value → 得到新权重 新权重异步推回 rollout CompactionRL(压缩·信用) SAO(异步·稳定) 异步回环:边采边训、不互等

跟着一条轨迹,走完它的训练生命周期

为什么难训 → 压缩自己继续跑 → 多段当成一条逻辑轨迹做信用分配 → 异步送去训练 → 异步滞后下稳定更新 → 证据与边界。

四难里""这一环最先发作,先把它放大看——轨迹撑爆窗口只能压缩续写,而压缩一进训练就切出段数段长全乱的多段,GRPO"每题一组等长轨迹"的前提就塌了:

推理时:轨迹太长怎么办 ✗ 无压缩 step 1 · 2 · ⋯ · N 撞墙即停 任务没做完就 GG ✓ 压缩续写 step 1 · 2 ⋯ 压成摘要 摘要+最近几步→新窗口 接着跑… 固定预算 下能一直跑 但进 RL 训练后:压缩把 GRPO 的地基掀了 GRPO 想要的 · 结构整齐 每题 G 条完整、等价轨迹凑一组 rollout 1 · 完整一条 rollout 2 · 完整一条 rollout 3 · 完整一条 rollout 4 · 完整一条 G 条 = G 个样本,组内均值当 baseline ✓ 长度一致 → advantage 好算 压缩给的 · 数量/长度都乱 同题不同 rollout 触发时机不同 r1 → 3 段 r2 → 1 段(没触发) r3 → 2 段 r4→4段 G 条 → ΣKg 段,段数/段长全不一样 ✗ 凑不成整齐的一组 → 组内归一化失效 摘要 解法 → 丢掉组采样,退回 critic PPO 单轨迹训练 靠 critic 给每个 token 估 advantage,不需要"凑一组横向比较"——对"一题切几段、各多长"零约束,天然适配压缩

第一步落到 rollout 侧。关键一点:摘要不是被动预处理,而是模型自己生成的 token,和执行动作一样进 RL、共享同一个奖励 R——所以它是可训练的。压缩怎么重建窗口、把有效 horizon 撑到约 4×,看下图:

旧窗口 · 几万 token 快撞预算上限 压缩 系统提示 Summary 最近 2 步 ≈ 几千 token · 摘要保长程 · 最近 2 步保即时状态 新窗口继续跑 有效 horizon ≈ 4× 每段(Exec + Summary)全进训练 · 共享同一奖励 R · 不单独评摘要质量
消融佐证:只换摘要器(执行策略不动)SWE 就差 6.5 分(49.0→55.5)——摘要决定后续动作能看到什么,是性能关键的决策,所以非训不可。

rollout 侧吐出的不再是一条完整轨迹,而是被压缩切开的变长多段(exec + summary)。训练时要把它们重新看成同一条逻辑轨迹——否则段数、段长的乱结构会在两个地方作妖,各配一个修正:

  • 长度不均 → token-level loss。若按"段"平均 loss,题A 被切 3 段就在统计里算 3 份,压缩多的题被凭空放大权重。改成所有段的 token 倒进一个大池子、除以 token 总数 $|M|$,切几段都不影响权重。
① 一条 rollout 被压缩切成 3 段(任务成功,R = 1) Prompt Exec 段1 1000 tok(真输出) Summary 200 tok(真输出) Exec 段2 800 tok(真输出) R = 1 任务级奖励 ↑ 撞到 T_comp,压缩触发点(早晚只是位置巧合) 总生成 token:1000 + 200 + 800 = 2000 个,全部进 loss(含 summary token)。 ② R 摊到每一个 token → 每 token 一个 advantage  R = 1 段1 · 1000 个 token Summary · 200 段2 · 800 个 token ··· ··· ··· 每 token 一个 Â:先段内 GAE,再按"后面还剩多少 token"打折 (γλ)^(N>s) 段2 后剩 0 → 不打折 | Summary 后剩 800 → ×(γλ)^800 | 段1 后剩 1000 → 打折最狠 关键:R 变成 2000 个 Â。切几段完全不影响 token 总数——还没有任何"重复"。 ③ 归一化:分母用"段"还是"token",差别就在这 段级平均 ✗ 错 第一步:段内先平均(÷ n_s) 段1→1份 Sum→1份 段2→1份 第二步:段间再平均(÷ K = 3) 同一个 R 被折算成 3 份进 batch ÷n_s 把每段压成"1 份", 段数 K 直接变成 R 的份数。 没压缩的 rollout 只占 1 份 → 压缩越多,R 灌进梯度越多次  (这就是"重复计入") 某 token 权重 = 1 /(K · n_s) 短段 token 还被 1/n_s 额外放大 token 级平均 ✓ 对 没有"段内平均",没有"÷K"。 2000 个 token 直接进同一个池子 M: 全 batch 混一起 求和后除以 token 总数 |M|: 每 token 权重 = 1 / |M|(全一样) 段数 K 根本不出现在公式里 切成 3 段还是不切,都是那 2000 个 token,R 传导 2000 份、再 ÷|M|。 → "重复"没被减掉,是被拿掉了 长任务 token 多、占份多是真实输出量, 本来就该多占,不是偏差。

换分母 = 把"按被切几刀计费"改成"按实际写了多少字计费"。消融去掉它掉分最狠:66.8 → 60.0

  • 跨段信用错乱 → cross-trajectory GAE。每段末尾都挂着同一个终局奖励,靠前的段会被误以为"马上到终点"。按"这段后面还剩多少 token"打个折扣,把它到真正终点的时间距离恢复回来。
分段单独算 GAE 的问题:前两段会误以为"自己已经快到终点了"。 ✗ 错误世界:每段各自算时,把自己的段尾当成"终点"(3 段各看各的) σ₁ Exec · 1000 tok 本段视角:到这就当结束 R′₁ 段尾折扣=×1(假装到终点·伪) σ₂ Sum · 200 到这就当结束 R′₂ ×1(伪终点) σ₃ Exec · 800 tok ✓ 本段正确:段尾就是真终点 R ×1(真终点·N>₃=0,本就正确) 问题:前两段并没真结束,只是"局部看来像结束"——段尾被当成 ×1(假装到终点),local GAE 以此为锚点算。 于是 σ₁、σ₂ 段的 advantage 幅度被夸大(|A| 虚高,以为"马上拿到奖励")。 真正丢掉的信息:把三段拼回一整条,这些段尾离真终点其实还很远—— σ₁ · 1000 σ₂ · 200 σ₃ · 800 R σ₁ 段尾 → 真终点:还差 1000 token σ₂ 段尾 → 还差 800 σ₃ 段尾=0 ↓ 修正:把"还差多少 token 才到真终点"补回来 — 给每段整体乘 (γλ)^N>s ↓ ✓ 修正世界:按"离真终点还多远"把每段压回正确位置(只有一个真终点 R) σ₁ Exec · 1000 σ₂ · 200 σ₃ Exec · 800 R 整段补乘"剩余 1000" ×(γλ)^1000 整段补乘"剩余 800" ×(γλ)^800 剩余 0,不修正 ×(γλ)⁰=×1 离真终点越远的段,额外折扣越狠((γλ)^1000 趋近 0)→ 前段 advantage 的幅度被收缩回真实大小,不再虚高。 Âs,i = (γλ)N>s · Aloc s,i 一句话:段内先照常算 local GAE,段外再按"这段后面还剩多少 token 才到真终点"整体压一遍。N>s = 后续所有段的 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}$ = 它在拼接后完整轨迹里到终点的真实距离。

轨迹在 rollout 侧压缩、切段、拿到终局奖励后,就该送去训练了。但"怎么送"又踩中"慢 / 异步"这条线:要高吞吐就得异步,而异步和 GRPO 的组采样天生打架——下图三条时间线对比:

rollout 生成中 GPU 空转干等 开训 横轴=时间 → ✗ 同步 RL:采完整批才训 短轨迹早跑完,干等最慢那条 开训 ← 大片 GPU 空转 ✓ 异步 + 单轨迹:谁跑完先训谁 一到就消费,无同步屏障,GPU 不空闲 吞吐拉满 ✓ ✗ GRPO 拖后腿:G 条凑齐才能训 组同步屏障 = 又回到等最慢那条 G 条凑不齐就等 → 屏障 开训 空转又回来了 另一坑:真实 online 环境常常每题只给 1 条反馈 → 根本凑不出一组,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 主干上,机制天然对得上。注意:这个"咬合"是基于两篇机制兼容的合理重构,论文并未同框描述二者如何拼接(详见 ⑥)。

轨迹进了 learner,接下来这三招(DIS / 价值三件套 / Skip-Obs)都是为了让这条异步来的单轨迹能稳定地学DIS · 先解决"重要性采样怎么算"这一环。标准做法要拿旧策略 $\pi_{old}$ 当基准,可异步下一条轨迹是好几个版本接力采出来的、$\pi_{old}$ 根本说不清。

① Direct:丢掉 π_old,直接用 π_rollout 日志当基准 trainer 生成途中不断把新权重推给 rollout 引擎,一条轨迹就这样被接力采出来 ↓ token 1–1000 · v5 采 1001–2500 · v6 2501–4000 · v7 4001–5000 · v8 → π_old 碎成一串,要还原它得逐版本重跑推理(≈4× 推理成本,昂贵) DIS 的解法:π_rollout 是每个 token 生成当下就记进日志的 v5 吐 · logπ✔ v6 吐 · logπ✔ v7 吐 · logπ✔ v8 吐 · logπ✔ 比率 r = exp(logπθ − logπ_rollout),天然逐 token 对齐 · 白嫖日志,省掉旧策略推理

光换基准还不够:用 $\pi_{rollout}$ 近似会引入一点可控的 off-policy 偏差,DIS 的另一半用双侧硬掩码兜住。它跟标准 PPO 的 clip 看着像、干的却相反,下图对着比一遍。

DIS 的另一半:越界处理 mask(归零)vs PPO clip(夹边界) 这张图画的是校准函数 f(x):把每个 token 的原始比率 x 映射成它实际生效的比率——灰底=越界区(下界 0.7、上界 6.0) 标准 PPO — clip(夹边界,留堂限分) ↑ f(x) 有效比率 原始比率 x 窄窗(≈0.2) 压平到上界 梯度=0 ✗ 单侧生效 · 未夹的大比率原样带偏 SAO — mask(归零,越界除名) ↑ f(x) 有效比率 原始比率 x 0.7 6.0 宽窗(ε_h=5.0) 窗内原样 f(x)=x 断崖归零 ↘ 这些红点= 旧引擎采的 发散 token 越界→归零 整条除名
信任域宽到离谱——比 PPO 的 ≈0.2 大一个量级,还随任务调(数学 0.3 / 5.0,SWE-Bench 0.8 / 3.0)。之所以敢开这么宽,全靠越界的极端值被归零删干净,留下的正常区间才放得开。

价值三件套 · 把 critic 训好

单轨迹每题只有一条样本、没有组内比较来平均噪声,方差全靠一个估得准的 critic 压——三件套(地基→追赶→稳梯度)就是"怎么把它训好":

目标:一个估得又快又准的 critic V_φ → advantage 才可信 → 压住单轨迹高方差 Trick 1 · K=2 快更(critic 追上 policy) policy ×1 critic ×2 critic 追上 policy 再打分 → value 估准、advantage 基线可信 Trick 2 · 冻结注意力(稳梯度) value 模型 V_φ 分两部分,只训一半: Attention 层 🔒 冻结 梯度 norm 大 MoE 投影层 ✓ 训练 梯度稳 管「稳」:把梯度 norm 大的注意力冻住,只更新稳的 MoE Trick 3 · Scaling Value Pretraining(地基) RL 开跑前用大规模语料把 V_φ 训到"心里有谱",解决冷启动——不让第 0 步 critic 乱打分把策略带崩 才追得动 冻它才有意义
三招都只为一件事:把 critic 估准。单轨迹没有组内比较来平均噪声,advantage 可不可信全押在这个 critic 上——所以 SAO 把工程投入几乎全砸在它身上(值不值,看 ⑥ 的消融)。

还有第四招 Skip-Observation GAE——为什么光把环境反馈 token 挡出 loss 还不够,得连 GAE 递推也一起躲开它,下图拆开看:

为什么"loss-mask 掉环境 token"还不够 · δ 里的 V(x_next) 到底指向谁 一条 agentic 轨迹铺开:我的动作 → 环境反馈 → 我的下一个动作 a(i,N) o(i,0) 环境 o_i a(i+1,0) 我生成的 环境吐的(我不生成) loss-mask 只把环境 token 挡出 policy 梯度,管不到下面的 GAE ✗ 标准 GAE:δ 的 V(x_next) 落到紧邻的 o(i,0)(递推还会继续穿过 o(i,1)…整段 observation) δ(a(i,N)) = r + γ·V(o(i,0)) − V(a(i,N)) a(i,N) o(i,0) → critic 被迫给环境 token 估值=瞎猜外部状态、灌噪声 ✓ Skip-Obs:V(x_next) 越过整段 observation,指向 a(i+1,0) δ(a(i,N)) = r + γ·V(a(i+1,0)) − V(a(i,N)) → GAE 递推不碰任何环境 token a(i,N) 环境 o_i(跳过) a(i+1,0) ✓ 越过 o_i,只依赖模型自己生成的 token
关键区分: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 之外。

⑥ 证据与边界:哪些被证明了,哪些是推断

标题承诺 GLM-5.2,但两篇报告的实验其实来自不同模型、不同任务——诚实地分清楚

证据一:压缩机制有效(CompactionRL)

模型 GLM-4.5-Air / GLM-4.7-Flash,任务是编码/终端类:

SWE-BENCH VERIFIED
59.8 → 66.8
+7.0 · GLM-4.5-Air 开压缩训练
TERMINAL-BENCH 2.0
13.4 → 20.2
+6.8 · GLM-4.7-Flash(30B)

支撑"可训练压缩有效"这一环(第②③章)。前提:增益只在"开压缩评测"下成立,关掉压缩单窗口反而掉分(train-test mismatch);消融去掉 token-level loss 掉到 60.0。

证据二:异步 single-rollout 稳定(SAO)

模型是 Qwen3-30B-A3B(注意:不是 GLM),任务是数学推理:

AIME 2025
84.2 → 97.3
30B 模型 · 反超 GPT-5 High(94.6)
稳定训练步数
1000+ 步
SAO 稳训;vanilla GRPO ~160 步崩

支撑"异步单轨迹能长稳训、参数化 critic 不可省"这一环(第④⑤章)。消融:换滑窗均值 baseline 掉 17.5 分(97.3→79.8),证明参数化 value 不能用简单统计量替代。

⚠ 关于"两者在 GLM-5.2 上组合使用"——分三级说清楚:
论文明说的(事实):两篇各自在摘要里声明"已部署到训练 GLM-5.2(750B-A40B) 的 RL 产线",且共享同一套底座(单轨迹 + critic PPO、同一团队)。同样是事实的还有:两篇从头到尾没有同框描述过二者如何拼接——这是可查证的缺失,不是推断。
本文的合理推断:开头那张"端到端系统图"、以及"逻辑轨迹被 SAO 接住"的接口讲法,都是基于两套机制兼容做的重构,方便理解,但论文里并没有这张图、也没有这段接口描述。
无法证实的:GLM-5.2(750B) 本身的任何实验数字(两篇合计零条 750B 结果);SAO 在 GLM 系模型上的效果(其全部实验在 Qwen3-30B)。

一句话记住这条主线

GLM-5.2 的长程 agentic RL,不是发明新目标函数,而是认清 GRPO 组采样在"要压缩 + 要异步"场景下的结构性不匹配,退回 single-rollout critic PPO,再把它工程化训好

压缩(CompactionRL)把一次超长交互变成一条分段的逻辑轨迹并修好分段信用;异步 single-rollout(SAO)接住这条轨迹、修好策略滞后与价值估计——前一步的输出正是后一步的输入,它们不是两篇并排的论文,而是同一条流水线的两段。

GLM-5.2 · RL for Long-Horizon Tasks · 教学拆解 · 数字来自 CompactionRL / SAO 两篇技术报告