这一篇对应 veRL 视频:“从原理层面理解训练参数,PPO & GRPO,batch size,kl & entropy”(BV1DZL1zNEN2)。

写这篇的目标不是“照着参数表翻译”,而是把你在 verl(veRL)里最常改、最容易踩坑的配置项,和它背后的算法对象一一对齐:你改的到底是“采样分布/有效 batch/更新步长/探索强度/保守性”,还是只是在调一个看起来像超参的数字。

我不会把视频内容当成“圣经”。这类讲解里最容易出错的两件事是:

  1. 把经验区间说成普适真理(例如某个指标“应该在 0.1-0.4”)。
  2. 把实现细节当成算法本身(例如 KL 的某种近似估计,被误当成 KL 的定义)。

所以本文会更强调“你应该如何用日志闭环验证”,而不是“照着配方抄参数”。一句话:参数之间强耦合,你改一个数字,往往同时改了“有效学习率、更新步长、数据复用程度和探索强度”。

系列导航:

延伸阅读(更偏算法本体而不是参数):


0. 资料对齐(视频 + 本地仓库)

  • 视频:BV1DZL1zNEN2
  • 配套仓库(你本地路径):
    • /Users/wangpeng/Downloads/modern_genai_bilibili-main/agentic_rl/verl
  • 本文主要引用的笔记(按重要程度):
    • agentic_rl/verl/objectives/objectives_loss.ipynb:PPO / KL / entropy / clip fraction / 训练时该看什么曲线
    • agentic_rl/verl/训练及调参经验/config-perf-tuning.ipynb:mini/micro batch 切分、dynamic batch、loss scale
    • agentic_rl/verl/objectives/agg_loss.ipynbloss_agg_mode 为什么会改变“有效 batch”与长度偏置
    • agentic_rl/verl/objectives/review_grpo.ipynb:PPO vs GRPO 直觉与 DeepSeek 风格细节(adv 标准化 / off-policy masking)
    • agentic_rl/verl/objectives/grpo_gspo.ipynb:GRPO/GSPO 的“序列 vs token”缩放因子(更偏进阶,可选)
    • agentic_rl/verl/训练及调参经验/sft.ipynbmicro_batch_size_per_gpu 才是显存真开关、长序列的 sequence parallel

verl 配置文档入口(笔记里引用的链接):


0.1 先建立一个“不会被参数名带偏”的心智模型

verl 的参数很多,但绝大多数可以映射回 4 个“你真正关心的杠杆”。我建议你以后看任何 RLHF/RLVR 框架,都先按这个分类理解:

  1. 采样分布(Sampling Distribution):你让旧策略怎么生成数据。
  2. 数据复用强度(Data Reuse / Off-policy Drift):同一批 rollout 你要训练几轮,以及每轮切多细。
  3. 更新幅度(Update Magnitude / Trust Region):你允许策略一步走多远。
  4. 探索强度(Exploration / Diversity):你是否允许模型持续探索,而不是快速塌缩到单一模板。

同一个“症状”(比如 reward 不涨)可以来自这四类中的任意一种原因。把它们混在一起调,最常见的结果是:短期变好,长期崩;或者“看起来更稳了”,其实只是探索被掐死。

1. 你在 verl 里训练 PPO/GRPO,本质做的是哪 4 件事

把实现细节都抹掉,verl 的 RL4LLM(PPO/GRPO)训练闭环可以压缩成 4 步:

  1. Rollout(采样):用旧策略 $\pi_{\theta_{\text{old}}}$ 生成 response(或者每个 prompt 生成一组 response)。
  2. 打分(reward / verifier):得到每条轨迹的 reward(verifiable reward / RM / rubric / verifier 都属于这一步)。
  3. 信用分配(advantages)
    • PPO:靠 critic/GAE 把序列回报分解成 token 级 advantage;
    • GRPO:不用 critic,直接在“同一 prompt 的一组样本”内部做 baseline(group mean / std)。
  4. 优化(update):对同一批 rollout 数据做若干个 epoch,按 mini-batch/micro-batch 切分,梯度累积,然后更新 actor(以及 PPO 的 critic)。

所以当你看到“训练不稳定/显存爆/速度慢/指标不涨”,不要先猜玄学超参。先问自己它属于哪一步:

  • rollout 太慢:推理引擎与 batching 问题;
  • reward 噪声大:评测/奖励设计问题;
  • advantage 质量差:baseline/标准化/critic 学不动;
  • update 太激进:batch/epoch/LR/clip/KL/entropy 的耦合问题。

1.1 “On-policy” 在 LLM-RL 里经常是一个误导词

很多人听到 PPO/GRPO 是 on-policy,就默认“只要是 on-policy 就稳”。但在工程里你几乎必然会做两件事,它们会把你推向 轻度 off-policy

  1. 一次 rollout 很贵,所以你会对同一批数据做多个 epoch 更新(ppo_epochs > 1)。
  2. 一次 rollout 的 batch 很大,所以你会切成很多 mini/micro 去更新。

当你跑到第 2/3 个 epoch 时,当前策略已经不是产生数据的那一个策略了。于是你会看到经典组合:

  • clip fraction 上升(大量 ratio 被裁到边界);
  • approx_kl(old,new) 上升(更新跨得更大);
  • reward 未必更好(甚至变差),因为你可能在反复榨同一批数据,过拟合到了某些 reward hacking 模式。

所以我更推荐你把 ppo_epochs 当成“数据复用强度旋钮”,而不是“训练更充分”的旋钮。更稳的调参顺序是:

  1. 先用较小 ppo_epochs 跑通(例如 1-2),把 KL/entropy/clipfrac 的形态看明白。
  2. 只有当你确认更新非常保守(KL 很低、clipfrac 接近 0、reward 上不去)且 rollout 成本极高时,再考虑增加 epoch。

2. Batch size:为什么你觉得改了 batch,结果像改了 learning rate

2.1 三个名字:Global / Mini / Micro(以及 GA)

从“优化器每次 step 用了多少样本”这个角度,你只需要记住三层:

  • Global batch(全局 batch):一次参数更新(optimizer step)看到的样本总量。它决定梯度噪声大小与“每步更新有多稳”。
  • Micro-batch(微 batch):单卡一次 forward/backward 放得下的样本量。它几乎直接决定峰值显存。
  • Gradient Accumulation(梯度累积,GA):当 global > micro_total 时,用多次 micro forward/backward 累积梯度,再做一次 optimizer step。

在 verl 的笔记里也强调过一句很工程的话:

真正影响显存的常常是 micro_batch_size_per_gpu,而不是 train_batch_size

(见 agentic_rl/verl/训练及调参经验/sft.ipynb

2.2 PPO update 阶段:mini-batch 和 micro-batch 怎么对应

config-perf-tuning.ipynb 把 actor 更新阶段的逻辑写得很直白:

1
2
3
4
5
6
7
for _ in range(self.config.ppo_epochs):
for batch_idx, mini_batch in enumerate(mini_batches):
if self.config.use_dynamic_bsz:
micro_batches, _ = prepare_dynamic_batch(mini_batch, max_token_len=max_token_len)
else:
self.gradient_accumulation = self.config.ppo_mini_batch_size // self.config.ppo_micro_batch_size_per_gpu
micro_batches = mini_batch.split(self.config.ppo_micro_batch_size_per_gpu)

你可以直接把它翻译成一句话:

  • ppo_mini_batch_size 决定“这一次 update 的统计单位有多大”(更像优化器视角的 batch)。
  • ppo_micro_batch_size_per_gpu 决定“单卡一次能塞多少”(更像显存视角的 batch)。
  • 两者的比值决定 GA 次数:mini // micro

这也是为什么你会感觉“改 batch 像改了学习率”:

  • 在很多实现里,loss 默认是 mean reduction;
  • 当你改变 micro/mini 的切分方式时,梯度尺度往往也被改变了;
  • 于是相当于你隐式改变了“每次 step 的有效步长”。

这里补一个更“从原理出发”的说法:

在 SGD 视角下,batch size 决定的是梯度噪声大小与统计稳定性。你一旦改变 loss 的归一化方式(mean/sum、token/seq 聚合),batch size 的含义就会变形,进而等价成“有效学习率”的变化。

2.3 Dynamic batch:按 token budget 切分,比按样本数切分更靠谱

LLM-RL 最大的工程痛点是序列长度差异极大:

  • 固定样本数切 micro-batch:长序列 batch OOM,短序列 batch 显存空闲,吞吐不稳;
  • 所以 verl 提供 use_dynamic_bsz=True:不是“每个 micro-batch 固定 K 条样本”,而是“每个 micro-batch 固定 token 上限”。

在笔记里,这个 token 上限被写成:

  • ppo_max_token_len_per_gpu * ulysses_sequence_parallel_size

这意味着:序列并行(SP)更多是为了省显存,不是为了加速;它把“每卡 token budget”摊到更多并行里。

2.4 Dynamic batch 必须配套一个东西:loss scale(否则梯度有偏)

Dynamic batch 里每个 micro-batch 的样本数 $|b_j|$ 不再相同。为了让最终梯度等价于“对整个 mini-batch 的平均”,verl 会对每次 micro 的 loss 乘一个 scale factor(笔记里给了对应代码解释):

  • loss_scale_factor = response_mask.shape[0] / self.config.ppo_mini_batch_size

直觉上它做的是:这个 micro-batch 占整个 mini-batch 的比例是多少,就让它贡献相应比例的梯度。

如果你只开了 dynamic batch,但忽略了 scale,你训练出来的结果很可能“看似能跑,指标却怪”,因为长短样本被系统性重加权了。

2.5 你真正的 compute 单位是 token,而不是 sample

在 LLM-RL 场景,“同样的 32 条样本”可能是完全不同的算力消耗与优化难度:长 CoT 的 token 数、激活保存、通信量都更大,也更容易触发 OOM 或梯度不稳定。

所以我建议你在日志/实验记录里把 batch 写成两行:

  1. num_samples(样本条数)
  2. num_tokens(响应 token 总数,或平均 token)

你会发现很多“看似随机”的不稳定,其实和 token 分布的长尾强相关。


3. PPO vs GRPO:同样是“policy gradient”,参数含义完全不同

3.1 PPO:更精细,但你必须养得起 critic

PPO 的核心对象是 token 级别的 advantage $\hat A_t$,常见形式是 GAE:

$$\hat A_t = \delta_t + (\gamma\lambda)\delta_{t+1} + (\gamma\lambda)^2\delta_{t+2} + \cdots$$

所以 PPO 的“稳定”来自两个东西:

  • critic 给 baseline:降低方差;
  • clip / KL trust region:限制一步更新幅度。

代价也很明显:

  • 你要训练/维护 critic(更多显存、更多 compute、更多不稳定来源)。

我个人判断 “PPO 值不值得上” 的标准是:你是否真的需要 token-level credit assignment。

  • 如果你的 reward 基本只在序列末端出现(典型 RLHF/RLVR),critic 很容易学成“差不多的常数”,advantage 质量不高,PPO 的复杂度未必值得。
  • 如果你的 reward 可以拆成比较细的过程信号(例如 tool 选择、检索覆盖、证据一致性等中间指标),token-level baseline 才更可能带来稳定收益。

3.2 GRPO:没有 critic,你把 baseline 放进“同组样本”

GRPO 的典型设定是:对同一个 prompt 采样一组 completion(组大小记作 $G$),得到 reward ${R_{i,1}, \dots, R_{i,G}}$,然后用组内统计量构造 advantage。

你可以把最常见的版本记成:

$$\hat A_{i,j} = \frac{R_{i,j} - \text{mean}(\mathbf R_i)}{\text{std}(\mathbf R_i) + \epsilon}$$

笔记里也提到 DeepSeek 风格的一种简化:不除以 std,只做中心化:

$$\hat A_{i,j} = R_{i,j} - \text{mean}(\mathbf R_i)$$

所以 GRPO 里你改的“batch size”经常有两层含义:

  • 你每个 prompt 采样的组大小 $G$(决定 baseline 质量与方差);
  • 你一次 update 看到多少个 prompt(决定全局梯度噪声)。

如果你把 PPO 的直觉(“batch 越大越稳”)直接搬到 GRPO 上,经常会误判,因为 GRPO 的关键瓶颈是“组内差异”是否足够大、baseline 是否有意义。

这里我补一个常被忽略的现实:组内差异很多时候来自采样温度,而不是来自模型能力

如果你把 temperature/top_p 调得很保守,组内样本高度同质,你就会看到:

  • reward std 很小;
  • advantage std 很小(甚至接近 0);
  • 训练看起来“很稳”,但 reward 也不涨,因为你几乎没探索到新的轨迹。

反过来,如果你把采样调得太散,组内差异很大但正确率很低,你会得到高方差 advantage,训练会非常抖。

所以 GRPO 最核心的调参,往往不是“更大 batch”,而是 “合理的 group size + 合理的采样多样性”

3.3 一个很容易被忽略的参数:loss 聚合方式(loss_agg_mode

agg_loss.ipynb 讲了一个非常重要但常被忽略的问题:你怎么把 token loss 聚合成一个标量,会系统性改变训练偏好。

例如 sample-level 的 seq-mean-token-mean,会把每个样本先除以序列长度;这在长 CoT 场景可能导致:

  • 长而正确的解被“平均”掉,梯度缩小;
  • 短而凑巧的解反而更占优势(长度偏置)。

这不是“理论洁癖”,而是会直接体现在你训练出来的输出风格上(短/长、是否愿意展开推理)。

verl 文档也明确提到:

  • 原始 GRPO 常用 sample-level;
  • DrGRPO / DAPO 等会改成更 token-level 的聚合来提升稳定性与减少长度偏置。

我建议你把 loss_agg_mode 当成“优化目标定义的一部分”,而不是“实现细节”:

  • 你的 reward 如果本质是 sequence-level(例如 pass/fail),你用 per-token mean 去归一化,就相当于把目标改成了“单位 token 的平均收益最大化”,这会系统性偏向短输出。
  • 如果你希望鼓励长 CoT 并且 reward 更像“总回报”,token-mean 往往更贴近直觉。

这个问题没有绝对对错,只有“你想要的行为”。


4. KL 与 Entropy:一个管“别飘”,一个管“别死”

4.1 KL:你需要先分清两种 KL

在 LLM-RL 里常出现两类 KL(含义完全不同):

  1. PPO 的 KL(new vs old):衡量你这次更新跨得有多大,常配合 clip fraction 一起看,属于稳定性监控。
  2. RLHF/RLVR 的 KL(policy vs ref):把策略约束在 reference(通常是 SFT/base)附近,防止语言漂移、reward hacking、分布坍缩。

为了避免“大家都叫 KL,但每个人脑子里不是同一个东西”,我更喜欢把它们写成公式(LLM 里 action 就是 token):

  • PPO 的 ratio 与 KL(old vs new)
    • $$r_t(\theta)=\exp(\log \pi_\theta(a_t|s_t) - \log \pi_{\theta_{\text{old}}}(a_t|s_t))$$
    • $$\widehat{\mathrm{KL}}(\pi_{\theta_{\text{old}}}|\pi_\theta)\approx \mathbb{E}_{a\sim \pi_{\theta_{\text{old}}}}[\log\pi_{\theta_{\text{old}}}(a|s)-\log\pi_\theta(a|s)]$$
  • 对 reference 的 KL(policy vs ref)
    • $$\mathrm{KL}(\pi_\theta|\pi_{\text{ref}})=\mathbb{E}_{a\sim \pi_\theta}[\log\pi_\theta(a|s)-\log\pi_{\text{ref}}(a|s)]$$

一个很重要但常被忽略的点:KL 的方向会改变偏好。在 RLHF/RLVR 里常用的 $\mathrm{KL}(\pi_\theta|\pi_{\text{ref}})$ 更像“mode-seeking”的约束,它天然会惩罚跑到 ref 低概率区域的行为,所以你在很多任务里会看到分布被削尖、输出多样性下降。这不是“实现问题”,而是 KL 方向本身带来的效应。

objectives_loss.ipynb 里给的实现提示很直接:最终 policy loss 往往是“PG surrogate + KL loss”:

1
policy_loss = policy_loss + kl_loss * self.config.kl_loss_coef

你调 kl_loss_coef 的直觉可以粗暴理解为:

  • 越大:越保守,越像 SFT,越不容易 reward hacking,但也越难把 reward 推上去;
  • 越小:越激进,更容易“学会取悦 reward”,同时也更容易跑飞。

4.2 为什么“KL + entropy”经常要一起调

objectives_loss.ipynb 里还强调了 entropy(探索强度):

  • entropy_coeff 默认可能是 0;
  • 但一旦 reward 非常稀疏或你的策略容易塌缩(只会输出一种模板),entropy 可能是救命的。

entropy 的计算在笔记里也给了很清楚的 token 级公式(这里用直觉版):

$$H(\pi(\cdot)) = -\sum_v p(v)\log p(v)$$

判读上你只需要记住:

  • entropy 低到贴地:模型输出非常确定,探索不足,容易 mode collapse;
  • entropy 持续很高:策略没收敛,可能 reward 信号太弱或 update 被各种约束压没。

4.3.1 entropy bonus 不是“补丁”,更像“探索预算”

一个常见误区是:模型塌缩了,就把 entropy_coeff 加大。

entropy bonus 的副作用很明确:它会持续奖励“分布更平”。如果你的 reward 信号本身很弱,entropy 可能会把你推向“随机但多样”的区域,reward 更难学。

更好的策略通常是:

  1. 先把 reward 设计到足够可学习(区分度、噪声、可验证性)。
  2. 再用 entropy 做“探索预算”,并考虑 schedule(前期大、后期小),而不是一把梭。

4.3 训练时你该盯的 4 条曲线(比“loss 变没变小”重要)

结合 objectives_loss.ipynb 的建议,训练中优先看这几类:

  1. reward / task metric curve(主指标):别盯 policy loss 的绝对值。
  2. KL(对 ref + 对 old):是否偏离过大、是否长期贴 0(步子太小)。
  3. clip fraction:它是个很敏感的“步子是否过猛”的指标,但没有普适黄金区间。长期过高通常意味着更新过猛(LR 大、epoch 多、优势尺度过大),长期贴 0 通常意味着更新过保守或 advantage 接近 0。
  4. actor entropy:探索是否快速坍缩。

如果你愿意再加一条,盯 adv/std 或者 GRPO 的 reward std,能很快看出“baseline 是否还有意义”。

4.4 别把 KL 系数当成常数“背配方”,更建议用闭环控制思路

不同实现里 KL 的估计方式(token/seq 聚合、近似形式)并不完全一致,所以你不能把某个固定系数当成可迁移经验。

比起死守一个系数,我更建议你用控制思路:

  1. 设一个你能接受的 KL 目标区间(对 ref 或对 old,看你要控制什么)。
  2. 观察 KL 与 reward 的耦合:KL 上去 reward 不涨,通常是更新在学坏;KL 很低 reward 不涨,通常是更新太保守或 reward 太弱。
  3. kl_loss_coef 变成“为了把 KL 拉回目标区间”的旋钮,而不是“一个固定常数”。

4.5 采样温度 vs entropy bonus:别把同一件事调两次

temperature/top_p 是 采样分布层面的探索;entropy bonus 是 优化目标层面的探索。它们经常被同时调,导致你以为自己在“调探索”,实际上是在两处重复施力。

一个更可诊断的做法是:

  1. 先固定采样温度,用 entropy bonus 调出一个不会塌缩的训练。
  2. 再把 entropy bonus 固定住,用采样温度去控制“你愿意花多少算力去探索”。

4.6 两个常见误读(我建议你别照搬任何人的结论)

  1. “clip fraction 0.1-0.4 是黄金区间”:这只是常见经验,不是定理。clipfrac 的合理范围依赖于 clip epsilon、advantage 尺度、epoch 数、以及你到底在算 token-level 还是 seq-level。
  2. “policy_loss 的正负代表训练好坏”:policy loss 是 surrogate,且数据分布在变。你更应该看 reward、KL、entropy、ratio 分布形态,以及“指标是否可复现”。

5. 你要把 verl 用在 agentic RL / deep research,上来就该怎么改参数

把“深研究 agent”抽象成一个 RL 任务,最常见的失败模式是:reward 难、噪声大、探索空间巨大。

因此一个更稳的起手式通常是:

  1. 先把 rollout 推理打通并做吞吐优化(你已经有 vLLM 那篇)。
  2. 在 update 侧优先保证“不会跑飞”:KL/clip/epoch 不要太激进。
  3. 在 reward/advantage 侧优先保证“有有效差异”:GRPO 的组内方差、或者 verifier 的区分能力。

这里我再补一个更“像研究”的建议:用最小实验矩阵把耦合拆开。例如固定三样只动一样:

  • 固定 sampling(temperature/group size),只调 update(epochs/clip/KL);
  • 固定 update,只调 sampling;
  • 固定两者,只改 reward/advantage(baseline/标准化/长度归一化)。

这样你才知道“系统到底在优化什么”,也更容易判断视频里的说法是否适用于你的任务。


6. verl/veRL 框架层面的关键点(比单个超参更重要)

这一节不是“照视频”,而是我认为 verl 真正强的地方:它把 rollout 的系统问题(长尾、并发、训练-推理资源争用、tokenization 一致性)当成一等公民处理。对 agentic RL 来说,这往往比你选 PPO 还是 GRPO 更关键。

6.1 为什么 agent rollout 倾向 async:长尾(straggler)会吃掉你的 GPU

agent 任务里不同样本完成 rollout 的时间差距极大:简单样本一轮对话结束,复杂样本可能多轮推理 + 多次工具调用 + 报错重试。如果用同步 batching,整批会被最慢样本拖死,GPU 大量空转。

verl 的 AgentLoop 体系用 async 并发把“快任务”和“慢任务”交织起来,本质是在解决这个系统瓶颈(见 agentic_rl/verl/agent/agent_loop_details.ipynb)。

6.2 token-in/token-out:训练-推理一致性不是细节,是收敛性问题

agentic_rl/verl/tokenizer/encode-decode.ipynb 里强调:encode(messages) 并不等价于 prompt_ids ⊕ response_ids,decode 再 encode 也可能不可逆。在 RL 训练里,这会导致 trajectory 偏离策略分布,严重时 PPO 甚至不收敛。

因此在多轮 agent rollout 里,verl 更倾向 token-in/token-out:用 token ids 作为接口,避免 chat template 反复 encode/decode 的不一致。

6.3 tool 返回的 token 不参与 policy gradient(response_mask)

AgentLoop 里常见做法是维护 response_mask

  • LLM 生成的 token:mask=1(参与 loss)
  • Tool 返回的 token:mask=0(不参与 loss)

这不是“实现偏好”,而是你必须明确:你究竟在优化 LLM 的什么行为。把工具输出当作 action token 来回传梯度,会把“环境反应”错当成“策略行为”,梯度变得没有意义。

6.4 Hybrid 模式:同一批 GPU 在训练和推理之间分时复用

verl 里有 wake_up/sleep 机制,用于在 rollout(vLLM/TP,占 KV cache)和 train(FSDP,占梯度/优化器状态)之间切换资源。这类机制是否好用,取决于你的集群形态:

  • 推理训练分离(standalone):架构更简单;
  • 同卡复用(hybrid):吞吐可能更高,但工程复杂度与系统抖动也更高,调参必须把“系统因素”算进去。

7. 一个更靠谱的“调参诊断表”:用现象定位到旋钮

下面这张表不是配方,是把“症状 -> 可能原因 -> 优先改什么”串起来。你不需要完全照做,但建议你每次改参数都能回答:我是在动哪类旋钮(采样/复用/更新/探索),我预期哪个日志指标会怎么变。

  1. reward 完全不涨,KL(old,new) 很低,clipfrac 接近 0,entropy 也不高
    可能原因:更新太保守或 advantage 接近 0(组内样本太同质 / reward 无区分度)。
    优先动作:降低 kl_loss_coef 或稍增 LR/clip epsilon;提高采样多样性(temperature/top_p 或 group size);检查 reward std / adv std。
  2. reward 短期上涨后崩,KL(ref) 快速上升,entropy 下降很快
    可能原因:在学 reward hacking 或走出 ref 分布太快。
    优先动作:增大 kl_loss_coef;减少 ppo_epochs;降低 LR;检查 reward 设计是否过度偏向格式/捷径。
  3. clipfrac 很高但 KL(old,new) 不高
    可能原因:同一批数据被多 epoch 复用,ratio 被推到 clip 边界,但整体 KL 还没很大(或被聚合方式稀释)。
    优先动作:先减 ppo_epochs;再看是否需要增大 ppo_mini_batch_size 或调整 loss_agg_mode;检查 advantage 尺度是否过大(reward scale / 标准化)。
  4. GRPO 训练“很稳但没学到”,reward std 很小
    可能原因:采样太确定,组内没有有效对比;或者 reward 太粗。
    优先动作:提高采样温度或 group size;把 reward 拆成更可学习的分项(哪怕是 proxy);必要时引入 verifier/自验证提升区分度。
  5. 一切看起来都正常,但换成多轮 agent 就完全不收敛
    可能原因:tokenization/trajectory 不一致(chat template encode/decode),或者 tool 输出误入梯度。
    优先动作:确保 token-in/token-out;确认 response_mask 把 tool token 排除;优先把单轮收敛后再扩到多轮。

如果你后续希望我把“verl 的 agent loop + rollout + reward manager + objective”按代码路径拆成能复现的工程笔记,我建议下一篇从这些 notebook 开始:

  • agentic_rl/verl/agent/agent_loop_arch.ipynb
  • agentic_rl/verl/agent/agent_loop_details.ipynb

它们会把“训练循环怎么把 agent 的 tool 交互排除在梯度外、怎么做 async rollout、怎么做 continuous batching”讲得更像一套系统,而不是零散参数。