Chapter 13

源码导读:调用栈与文件地图

📌 所有路径基于 verl-project/verl · commit 9c38b8bb1876 。 目标:读完之后你 给 verl 提 PR 时知道改哪个文件 。

13.1 顶层目录地图

verl/
├── trainer/                              训练入口 + Controller 算法主控
│   ├── main_ppo.py                       ★ 默认启动入口(async 风格)
│   ├── main_ppo_sync.py                  ★ 同步 PPO 入口(TransferQueue / agent loop)
│   ├── main_eval.py                      离线评测入口
│   ├── main_generation_server.py         独立 generation server
│   ├── sft_trainer.py / sft_trainer_ray.py  SFT 单进程 / Ray 入口
│   ├── ppo/
│   │   ├── ray_trainer.py                ★ RayPPOTrainer.fit() 主循环(1769 行)
│   │   ├── core_algos.py                 ★ 算法核心(13 个 advantage / 11 个 policy loss)
│   │   ├── metric_utils.py               metrics 计算与聚合
│   │   ├── utils.py                      Role enum / need_reference_policy
│   │   └── ...
│   ├── distillation/                     在线蒸馏 loss
│   ├── config/                           ★ Hydra YAML 层叠(algorithm/actor/critic/ref/rollout/...)
│   ├── constants_ppo.py                  PPO_RAY_RUNTIME_ENV
│   └── runtime_env.yaml                  Ray runtime 默认环境变量
├── single_controller/                    Controller + WorkerGroup 抽象
│   ├── base/                             平台无关基类
│   │   ├── worker.py                     Worker 基类、@register 装饰器
│   │   ├── worker_group.py               WorkerGroup / NaiveWorkerGroup / ResourcePool / ClassWithInitArgs
│   │   └── decorator.py                  Dispatch 枚举(8 种)+ dispatch/collect 函数
│   └── ray/                              Ray 实现(RayResourcePool / RayWorkerGroup / func_generator)
├── workers/                              Worker 实现
│   ├── engine/                           训练引擎抽象(EngineRegistry)
│   │   ├── base.py                       BaseEngine / EngineRegistry
│   │   ├── fsdp/                         FSDPEngine / FSDPEngineWithLMHead
│   │   ├── megatron/                     MegatronEngine{,WithLMHead,WithValueHead}
│   │   ├── torchtitan/                   TorchTitanEngine
│   │   ├── veomni/                       VeOmniEngine
│   │   ├── automodel/                    AutomodelEngine(HF 直跑 / vexact)
│   │   └── mindspeed/                    华为昇腾
│   ├── rollout/                          采样后端
│   │   ├── base.py                       BaseRollout + _ROLLOUT_REGISTRY
│   │   ├── vllm_rollout/                 vLLM ServerAdapter + BucketedWeightSender
│   │   ├── sglang_rollout/               SGLang ServerAdapter + PD 分离
│   │   └── trtllm_rollout/               TensorRT-LLM ServerAdapter
│   ├── reward_manager/                   reward 计算
│   │   ├── abstract.py                   AbstractRewardManager
│   │   ├── registry.py                   REWARD_MANAGER_REGISTRY
│   │   ├── naive.py / batch.py / dapo.py / prime.py
│   ├── engine_workers.py                 ★ TrainingWorker / ActorRolloutRefWorker
│   ├── utils/losses.py                   ppo_loss / value_loss
│   └── config/                           ★ ActorConfig / CriticConfig / RolloutConfig / EngineConfig
├── protocol.py                           ★ DataProto / DataProtoFuture / BatchData
├── base_config.py                        BaseConfig (frozen dataclass)
├── models/                               transformer 模型实现(多模态 / Megatron 包装)
├── checkpoint_engine/                    多后端 checkpoint(nccl / hccl / kimi / mooncake / nixl)
├── model_merger/                         训完导出 HF 格式
├── third_party/                          第三方集成(vLLM patch 等)
├── tools/                                工具框架(BaseTool / tool_registry)
├── utils/                                通用工具 + reward_score + zero_to_fp32 等
├── experimental/                         2024-2026 新特性温床
│   ├── agent_loop/                       多轮工具调用 agent
│   ├── fully_async_policy/               2025-08 全异步训练
│   ├── one_step_off_policy/              一步 off-policy
│   ├── transfer_queue/                   零拷贝零 padding 数据通道
│   ├── reward_loop/                      在线 reward model
│   ├── teacher_loop/                     在线蒸馏 teacher
│   └── separation/                       架构变体
└── recipe/                               子模块:业界 recipe 集合(独立仓 verl-recipe)

13.2 五条主线代码

主线核心文件
启动入口 trainer/main_ppo.py
控制主循环 trainer/ppo/ray_trainer.py
算法核心 trainer/ppo/core_algos.py
Worker 实现 workers/actor/dp_actor.pyworkers/rollout/vllm_rollout.py
RPC 框架 single_controller/ray/base.py

13.3 从命令行到训练循环的调用栈

flowchart TB CLI["python -m verl.trainer.main_ppo <hydra overrides>"] CLI --> Mn["main_ppo.py · main()"] Mn --> Hyd["Hydra 解析 → 渲染 config"] Hyd --> Ray["ray.init() / ray.attach()"] Ray --> RT["trainer/ppo/ray_trainer.py
RayPPOTrainer(config)"] RT --> WG1["init_workers():
建 ActorRolloutRefWorkerGroup"] RT --> WG2["建 CriticWorkerGroup(PPO 时)"] RT --> WG3["建 RewardModelWorkerGroup(model reward 时)"] WG1 --> Init["WorkerGroup.init_model()
每个 worker 加载模型"] RT --> Fit["trainer.fit()"] Fit --> Loop["for epoch / for batch:
generate → reward → advantage → update"]

13.4 ray_trainer.py:算法主循环

这是 verl 最重要的一个文件。它的 fit() 函数大约 200-300 行,是算法工程师工作的主战场

# 伪代码(接近真实结构)
def fit(self):
    for epoch in range(total_epochs):
        for batch in self.train_dataloader:
            # 1. rollout:actor 生成 response
            gen_output = self.actor_rollout_wg.generate_sequences(batch)

            # 2. reference logp
            ref_log_prob = self.actor_rollout_wg.compute_ref_log_prob(gen_output)

            # 3. critic value(PPO only)
            if self.use_critic:
                values = self.critic_wg.compute_values(gen_output)

            # 4. reward
            reward_tensor = self.reward_fn(gen_output)

            # 5. advantage(grpo / gae / rloo / ...)
            gen_output = compute_advantage(
                gen_output,
                adv_estimator=self.config.algorithm.adv_estimator,
                ...
            )

            # 6. 训练 actor
            for ppo_epoch in range(ppo_epochs):
                self.actor_rollout_wg.update_actor(gen_output)

            # 7. 训练 critic
            if self.use_critic:
                self.critic_wg.update_critic(gen_output)

            # 8. 保存 / 评测
            if step % save_freq == 0:
                self._save_checkpoint(...)
            if step % test_freq == 0:
                self._evaluate(...)

整个 RLHF 训练循环就这 8 步。改算法 = 改这 8 步中的某一两步。

13.5 single_controller/:RPC 透明化的实现

文件说明
base/worker.py 所有 Worker 的基类,含 @register 装饰器
base/decorator.py @register(dispatch_mode=...) 的实现
base/dispatch.py 5 种 dispatch_mode(DP/All-to-all/...)
ray/base.py RayWorkerGroup + RayResourcePool
ray/megatron.py Megatron 的特殊 ResourcePool 打包

13.6 workers/actor/dp_actor.py:训练 actor 主循环

FSDP actor 的 update_actor() 大致结构:

def update_actor(self, data: DataProto) -> DataProto:
    self._enter_train_mode()        # weight resharding 回训练布局

    for micro_batch in chunked(data):
        # 前向:算 new logp
        logits = self.model(micro_batch.input_ids)
        log_prob = compute_log_prob(logits, micro_batch.labels)

        # ratio
        ratio = (log_prob - micro_batch.old_log_prob).exp()

        # policy loss(PPO clip)
        policy_loss = -torch.min(
            ratio * micro_batch.advantages,
            ratio.clamp(1-eps, 1+eps) * micro_batch.advantages,
        ).mean()

        # KL loss(可选)
        kl_loss = compute_kl_loss(log_prob, micro_batch.ref_log_prob)

        loss = policy_loss + self.kl_loss_coef * kl_loss

        loss.backward()

    self.optimizer.step()

读懂这个函数 = 读懂 verl 训练核心。

13.7 workers/rollout/vllm_rollout.py:采样后端封装

方法做什么
__init__ 启动 vLLM LLM engine
generate_sequences() 从 DataProto 取 prompt → vLLM 生成 → 包成 DataProto
sync_model_weights() actor 权重 → vLLM 权重布局
offload_model_weights()rollout 完,把 vLLM 内存释放

新接入一个采样后端 = 模仿这个文件写一个新的 SGLangRolloutMyRollout

13.8 workers/sharding_manager/:训推权重对接

文件说明
fsdp_vllm.py FSDP ↔ vLLM TP 布局变换
megatron_vllm.py Megatron ↔ vLLM 变换(含 EP)
fsdp_sglang.py FSDP ↔ SGLang

遇到新模型架构 rollout 报形状错,多半要在这里加 special case。

13.9 trainer/ppo/core_algos.py:算法核心

函数说明
compute_gae_advantage_return GAE 经典实现
compute_grpo_outcome_advantage GRPO 组内归一
compute_rloo_outcome_advantage RLOO leave-one-out
compute_reinforce_plus_plus_outcome_advantageREINFORCE++
compute_policy_loss PPO clip policy loss
kl_penalty kl / abs / mse / low_var_kl / full

新算法 = 在这里加一个 compute_my_advantage + dispatch 一行 case。

13.10 阅读路线总结

  1. 跑通 ch03 单机 GRPO 例子:先有手感;
  2. trainer/ppo/ray_trainer.py · fit():理解主循环 8 步;
  3. trainer/ppo/core_algos.py:理解算法侧;
  4. single_controller/ray/base.py:理解 RPC 透明化;
  5. workers/actor/dp_actor.py:理解训练实现;
  6. workers/rollout/vllm_rollout.py:理解推理对接;
  7. 看一遍 workers/sharding_manager/fsdp_vllm.py:理解 hybrid engine 的"魔法"。

13.11 想给 verl 提 PR?从这些地方入手

改动类型对应文件
新 advantage 算法 trainer/ppo/core_algos.py 加函数 + dispatch
新 reward 函数 utils/reward_score/<new>.py + 注册到 __init__.py
新采样后端 workers/rollout/<new>_rollout.py
新训练后端 workers/engine/<new>_engine.py + 对应 actor/critic 实现
支持新模型架构 workers/sharding_manager/ 加变换规则
新 worker 角色(如 evaluator) 新建 workers/<new>_worker.py
新数据集预处理 examples/data_preprocess/<dataset>.py
新配置字段 trainer/config/ YAML + dataclass

13.12 延伸阅读

资源用途
GitHub verl-project/verl 主仓库
GitHub verl-project/verl-recipe R1 / DAPO 等业界 recipe
GitHub verl-project/verl-omni 扩散与多模态后训练
GitHub verl-project/vexact "零失配" rollout
verl.readthedocs.io官方文档
deepwiki.com/verl-project/verlAI 生成的代码 wiki
HybridFlow 论文理论基础
本目录 OpenRLHF 那本书对比另一种 RLHF 框架设计
本目录 TRL 那本书对比单机风格 RLHF

13.13 RayPPOTrainer.fit() 真实代码地图

fit() 的伪代码 13.4 已经给过,下表是真实 line 锚点。基于 verl/trainer/ppo/ray_trainer.py(commit 9c38b8bb1876):

行号marker做什么
11361fit() 入口 整个 PPO 训练循环
21466"gen" async_rollout_manager.generate_sequences(),rollout
31517"reward" _compute_reward_colocate()extract_reward(batch)
41532–1541"old_log_prob" bypass mode 复用 rollout logp;decoupled 调 _compute_old_log_prob()
51577"RefPolicy" _compute_ref_log_prob()(如果 use_reference_policy)
61583"values" _compute_values()(如果 use_critic,即 GAE)
71587"adv" compute_advantage(),按 13 种 estimator 之一
81599"apply_kl_penalty" 如果 algorithm.use_kl_in_reward=True
91615rollout IS 矫正 decoupled mode 应用 importance ratio
101624compute_advantage 实际调度 dispatch 到具体 estimator 函数
111636"update_critic" critic mini-batch 更新(如果 use_critic)
121642critic warmup warmup 阶段只更新 critic 不动 actor
13~1660"update_actor" actor mini-batch 更新
141669–1670checkpoint 触发 步数到 / ESI 即将到期
151674checkpoint_manager.update_weights()actor 权重同步到 rollout
161711–1746metrics 聚合 compute_data_metrics / timing_metrics / throughout_metrics / variance_proxy_metrics / spec_decode_metrics

13.14 ActorRolloutRefWorker.update_weights() 完整路径

ch05.10 介绍过 update_weights 的两种 mode。源码在 verl/workers/engine_workers.py:659–738,按行号展开 naive 模式 5 步:

动作
689判定 mode:naive vs 其他(checkpoint-engine 路径)
699rollout.resume(tags=["weights"]) —— 唤醒 vLLM/SGLang server,腾出权重空间
703–704actor.engine.get_per_tensor_param(layered_summon, base_sync_done) 生成 (name, tensor) 流
709LoRA 时 sleep_level=1(base 留 GPU),否则 =2
721rollout.update_weights(per_tensor_param, peft_config=..., global_steps=...)
734rollout.release() —— 释放 rollout 端的临时内存

对应到 vLLM ServerAdapter(vllm_rollout.py):

动作
61ServerAdapter 类定义
85–88计算 replica_rank / rollout_rank
91–95sleep_level 决策
108ZMQ IPC handle 协商
119_execute_method() —— Ray RPC dispatcher
150–162resume() / release() 走 collective_rpc 到 vllm server
165–192update_weights() 用 BucketedWeightSender 流式发权重

13.15 EngineRegistry 与所有训练后端的"找类"

"我用 strategy: fsdp,最后实例化的到底是哪个类?"答:

EngineRegistry._registry[model_type][backend][device]

常见组合的实际类(来自 verl/workers/engine/__init__.py:14–65):

用户角色strategy实际实例
actor fsdp FSDPEngineWithLMHead
actor megatron MegatronEngineWithLMHead
critic fsdp FSDPEngineWithValueHead(若存在;否则 base FSDPEngine 加 value head wrapper)
critic megatron MegatronEngineWithValueHead
ref fsdp / megatron 同 actor 但 forward_only=True
actor torchtitan TorchTitanEngine
actor veomni VeOmniEngine(多模态)
actor automodel AutomodelEngine(vexact / 调试)

13.16 跟着测试读 API

verl 自带丰富 unit tests,是"我想看这个 feature 怎么用最小化"的最快入口。下表是几份高密度的:

测试文件示范了什么
tests/test_protocol.py DataProto chunk / concat / select_idxs 全套用法
tests/single_controller/test_worker_group.py WorkerGroup / Dispatch 各模式
tests/single_controller/test_register_decorator.py @register 装饰器各 dispatch_mode 的真实行为
tests/workers/engine/test_fsdp_engine.py FSDPEngine 构造 + train/eval batch 最小例
tests/workers/rollout/test_vllm_rollout.py vLLM rollout 单独跑(绕过整个 trainer)
tests/workers/reward_manager/test_naive_reward.py 规则 reward 的样本流
tests/trainer/ppo/test_core_algos.py 13 种 advantage estimator 的数值正确性
tests/tools/test_base_tool.py BaseTool 接口

跑测试的标准命令:cd repos/verl && pytest tests/test_protocol.py -xv-x 在第一个失败处停,-v 看每条 test 名字。

13.17 阅读路线(升级版)

整合本书所有锚点,给一个目标导向的 7 天阅读路线:

目标主要文件
D1跑通最小 GRPO ch03 步骤 + examples/grpo_trainer/run_qwen3_4b_fsdp.sh
D2理解 controller 装配 main_ppo.py:39 / 52 / 111 (TaskRunner)
D3啃完主循环 ray_trainer.py:1361 fit() + 13.13 line 表
D4RPC 透明化机制 single_controller/base/decorator.py:26 + ray/base.py:48 func_generator
D5worker 内的实际计算 workers/engine_workers.py:76 TrainingWorker / 434 ActorRolloutRefWorker
D6训推权重桥接 workers/engine/base.py:150 get_per_tensor_param + workers/rollout/vllm_rollout/vllm_rollout.py:165 update_weights
D7算法细节 + 新特性 trainer/ppo/core_algos.py 13 个 estimator + 11 个 loss;扫一眼 experimental/

跑完这 7 天,verl 90% 的代码不再陌生。剩下 10%(多模态 / VLA / verl-omni)是 niche,到需要时再读对应子目录。

13.18 想给 verl 提 PR?升级版索引

改动类型对应文件 / 注册点
新 advantage estimator trainer/ppo/core_algos.py + @register_adv_est("name")
新 policy loss core_algos.py + @register_policy_loss("name")
新 reward manager workers/reward_manager/<name>.py + @register("name")
新 reward 规则 utils/reward_score/<name>.py + 在 __init__.pydata_source 映射
新 rollout 后端 workers/rollout/<name>_rollout/ + 在 rollout/base.py:83 _ROLLOUT_REGISTRY 注册
新 training 后端 workers/engine/<name>/ + 在 engine/__init__.py 导出 + EngineRegistry.register
新 worker 角色 workers/<new>_worker.py + trainer/ppo/utils.py Role enum
新工具 tools/<name>_tool.py 继承 BaseTool
新模型支持(kernel inject) third_party/vllm/third_party/sglang/ 加 patch
新算法 recipe verl-recipe 独立仓而非主仓
恭喜 到这里你已经读完了 verl 中文 tutorial 的全部 13 章。 这本书在 commit 9c38b8bb1876 上是精确的;verl 高速演进, 关注 verl-project 组织下的新仓库(recipe / vexact / verl-omni)来跟进前沿。