1. verl 实验性功能到底解决什么问题:异步 RL 训练与 Agent Loop 的真实场景
如果你正在用 verl 跑 PPO 类强化学习训练,大概率遇到过这样的场景:rollout 阶段推理 GPU 满载、训练 GPU 空转;等训练开始时,推理侧又闲下来。同步训练把采样和更新严格串行,硬件利用率天然上不去。verl 的 experimental 模块就是冲着这类瓶颈去的,它把前沿探索集中在一个独立命名空间里,和主流程稳定的同步 PPO 分开维护。
这个模块主要覆盖五个方向:Fully Async Policy 全异步策略训练、One-Step Off-Policy 单步离策略、Agent Loop 智能体循环、Reward Loop 奖励循环、Teacher Loop 教师循环,再加上底层的 Separation 资源分离模式。它们共同回答一个问题——如何打破 rollout 与 training 的同步耦合,让大规模 LLM 强化学习跑得更满、更灵活。
这篇文章面向需要搭建可复现 RL 训练流程的开发者。我会把异步训练的关键配置项、Agent Loop 的接入方式、以及怎么通过日志和指标确认流程按预期运行讲清楚。适合已经跑通过 verl 基础 PPO、想进一步压榨吞吐或接入多轮工具交互的人。读完后你应该能独立配出一份可运行的异步训练配置,并知道去哪里看它是否真的异步起来了。
需要说明的是,experimental 模块的成熟度参差不齐。Agent Loop 和 Reward Loop 相对完整,Fully Async 和 Teacher Loop 还在打磨。所以下面的配置我会标注哪些是稳定可用的,哪些需要你多验证几轮。
2. TaoToken 前置准备:模型接入与 API Key 配置
异步 RL 训练和 Agent Loop 都离不开稳定的模型推理服务。verl 本身负责训练编排,但 rollout 阶段调用的 LLM 推理端点、Agent Loop 里工具调用背后的模型、以及 Teacher Loop 的教师模型,都需要一个统一的接入层。我这边习惯用 TaoToken 来做这层接入,它把多家模型的调用收敛成一套 OpenAI 兼容接口,配置一次就能在训练脚本里复用。
先说清楚它是什么、能做什么。TaoToken 提供 OpenAI 兼容的 API 网关,你可以用同一个 Base URL 和 API Key 访问不同模型,适合在 RL 训练这种需要频繁切换模型、批量发请求的场景里减少配置成本。对 verl 来说,最直接的价值是:rollout 的推理服务、Agent Loop 的工具调用模型、Reward Loop 里的生成式奖励模型,都可以指向同一个端点,省去为每个组件单独维护鉴权和地址。
适合谁:正在搭 RL 训练流水线、需要多模型对比、或者想让 Agent Loop 里的模型调用统一管理的开发者。如果你只是跑单机小规模实验,也可以先用它把流程跑通,再决定要不要换成自建推理服务。
接入前你需要准备两样东西:API Key 和 Base URL。API Key 在控制台的 API Keys 页面创建,地址是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。Base URL 统一用 https://taotoken.net/api ,注意这个地址不带任何查询参数,直接填进配置即可。
创建 Key 的步骤不复杂:登录后进入 API Keys 页面,点新建,给 Key 起个能区分的名字(比如 verl-rollout、verl-teacher),复制保存。Key 只在创建时完整显示一次,丢了就得重建。建议按用途分开建 Key,这样后面看用量和排障时能快速定位是哪个组件在发请求。
模型选择上,rollout 阶段通常用生成能力强的模型,Agent Loop 里的工具调用模型需要较好的指令遵循,Teacher Loop 的教师模型一般选比学生更强的版本。具体选哪个模型 ID,可以在模型对话页面先试跑几条请求确认效果,地址是 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。确认没问题后再写进训练配置。
有一点要提醒:TaoToken 是接入层,不替代你的训练框架。verl 负责采样、更新、资源调度,TaoToken 负责把模型请求稳定地送出去。两者职责分清,排障时才知道该看哪边。
3. 可复制配置:异步训练与 Agent Loop 的完整参数
这一节是重点,我给出可以直接抄的配置片段。verl 的 experimental 功能主要通过 YAML 配置和入口脚本组合来启用,下面按 Fully Async、Agent Loop、Separation 三块拆开讲。
先看 Fully Async Policy 的核心配置。它把 rollout 和 training 解耦成两个独立的 Ray Actor,中间用消息队列连接。关键参数集中在 trainer 和 rollouter 两段:
# fully_async_ppo.yaml trainer: class: FullyAsyncTrainer ppo_mini_batch_size: 64 require_batches: 2 # 每次训练需要的最小批次数 trigger_parameter_sync_step: 4 # 每隔多少步同步一次参数到 rollouter use_trainer_do_validate: true # 混合验证模式 rollouter: class: FullyAsyncRollouter max_queue_size: 512 # 消息队列容量,满时丢弃最旧样本 partial_rollout: true # 推理中断时自动恢复生成 message_queue: class: MessageQueue max_queue_size: 512require_batches和trigger_parameter_sync_step是两个最影响行为的参数。前者决定训练器每次攒够多少批才更新,后者决定参数多久同步回 rollouter。同步太频繁会削弱异步收益,太稀疏又会让样本过旧,我一般从 4 起步,看 stale_trajectory_processed 指标再调。
Agent Loop 的接入配置长这样。它把单轮生成扩展成多轮工具交互,核心是注册一个 agent loop 实现并指定工具解析器:
# agent_loop_config.yaml agent_loop: name: tool_agent # 对应 @register("tool_agent") max_user_turns: 3 max_assistant_turns: 5 max_parallel_calls: 2 tool_selection: per_sample # 支持 per-sample 工具过滤 tool_parser: name: function_call_parser stop_token_ids: [128009] # 工具调用 token 处停止生成 llm_client: base_url: "https://taotoken.net/api" api_key: "${TAOTOKEN_API_KEY}" model: "your-rollout-model-id"注意response_mask机制:LLM 生成的 token 标 1,工具响应的 token 标 0,训练时只对 1 的部分算损失。这个在配置里不用手动设,Agent Loop 会自动处理,但你要理解它,否则看日志时会困惑为什么 response 长度和实际生成对不上。
Separation 模式的配置负责资源隔离,是异步训练的基础:
# separation_config.yaml resource_pool: trainer_pool: num_gpus: 8 rollout_pool: num_gpus: 8 actor_worker: class: DetachActorWorker # 支持 save/restore 到 CPU strategy: fsdp2 # 可选 fsdp / fsdp2 / veOmni / megatronDetachActorWorker的 save/restore 机制是异步训练能跑起来的关键:训练完成后把参数卸载到 CPU,推理时再加载回推理引擎,这样训练和推理的 GPU 才能真正分开。
如果你用 Claude Code 或类似工具做配置管理,可以把上面这些片段放进项目目录,用环境变量注入 Key。三件套要写全:Base URL 用https://taotoken.net/api,Key 从环境变量读,Model ID 填你确认过的模型。缺任何一个都会在启动时报鉴权或路由错误。
4. 验证请求与成功结果:日志和指标怎么看
配置写完不代表跑对了。异步训练最容易出的问题是"看起来在跑,其实没异步起来",所以验证环节必须盯紧几个指标。
先做一次最小验证请求,确认模型端点通。用 curl 打一条 chat completion:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "your-rollout-model-id", "messages": [{"role": "user", "content": "ping"}], "max_tokens": 8 }'返回里有choices[0].message.content就说明接入层没问题。这一步能排掉大部分鉴权和地址错误。
然后启动异步训练,重点看这几类日志。第一类是消息队列统计:total_produced、total_consumed、dropped_samples。健康状态下 produced 和 consumed 应该持续增长且差距不大,dropped_samples 偶尔有是正常的(队列满时丢最旧样本),但如果一直涨说明消费跟不上生产,要么调大 max_queue_size,要么检查训练侧是不是卡住了。
第二类是样本新鲜度指标。stale_trajectory_processed统计因参数版本过旧被丢弃的样本数。这个值应该保持在总样本的较低比例,如果它占比很高,说明trigger_parameter_sync_step设太大了,rollouter 产出的样本在训练器消费时已经过期。
第三类是 GPU 利用率。异步训练跑起来后,训练和推理两侧的 GPU 应该都在持续工作,而不是交替空闲。你可以用 nvidia-smi 或训练框架自带的监控看。如果还是交替满载,多半是参数同步或队列配置没生效。
Agent Loop 的验证看AgentLoopMetrics:generate_sequences、tool_calls、compute_score、num_preempted。多轮交互正常时,tool_calls 应该大于 0,num_turns 反映实际轮次。如果 tool_calls 一直是 0,检查工具解析器的 stop_token_ids 是否和模型匹配,以及工具注册是否生效。
成功的结果长这样:训练日志里 global_steps 稳步推进,消息队列 produced/consumed 同步增长,stale 比例可控,Agent Loop 的 tool_calls 有非零值,验证集指标(ValidationGenerationsLogger 记录的生成结果)符合预期。这几条同时满足,基本可以确认流程按预期运行。
5. 本篇常见错误排查:401、local proxy failed、reading choices、OAuth
异步训练和 Agent Loop 的报错有几类高频的,我按实际遇到的顺序列出来,对照着查。
401 Unauthorized。最常见,基本是 Key 问题。检查三件事:环境变量TAOTOKEN_API_KEY是否真的注入到训练进程(Ray Actor 有时拿不到父进程的环境变量,需要在启动脚本里显式传);Key 是否被复制时带了空格或换行;Base URL 是否写成了带路径的形式。Base URL 必须是https://taotoken.net/api,不要自己拼/v1,客户端库通常会补。
local proxy failed / connection refused。这类报错说明请求根本没发出去,或者被本地网络配置拦了。先确认训练节点能直连taotoken.net,用 curl 测一次。如果 curl 通但训练脚本不通,检查是不是 Ray Actor 运行在隔离的网络命名空间里,或者有本地代理配置干扰。注意不要引入任何网络代理工具,直接走正常网络出口即可。
reading choices 相关报错,比如KeyError: 'choices'或list index out of range。这通常是响应体结构和预期不符。可能原因:模型 ID 填错导致返回了错误结构;请求被限流返回了非标准响应;或者 Agent Loop 里工具解析器把非生成内容也当成了 response。排查时先把原始响应打出来看,确认choices字段存在且非空。如果是限流,LimitedRewardManager 的 AsyncTokenBucket 配置可以帮你控制请求速率。
OAuth / 鉴权流程报错。如果你在 Agent Loop 里接了需要 OAuth 的外部工具,报错往往出在 token 刷新环节。检查 OAuth 配置里的 client_id、回调地址、token 有效期。这类问题不在 TaoToken 侧,而在工具集成侧,需要单独看工具的鉴权日志。
参数同步失败。异步训练特有的问题。如果 rollouter 拿不到新参数,检查trigger_parameter_sync_step是否被设成了 0 或负数,以及 DetachActorWorker 的 save/restore 是否正常。日志里搜 "parameter sync" 或 "param_version",看 current_param_version 有没有推进。
Agent Loop 卡住不结束。多轮交互如果 max_user_turns 和 max_assistant_turns 设得过大,或者工具一直返回需要继续调用的结果,循环可能不终止。检查状态机是否走到了 TERMINATED,以及工具响应是否触发了终止条件。
排障时记住一个原则:先确认接入层通(curl 测端点),再确认训练框架配置对(看队列和参数同步日志),最后看业务逻辑(Agent Loop 的工具调用和奖励计算)。分层排查比一股脑看日志快得多。
6. 语义一致 CTA:把配置落到你的训练流程里
上面这套配置和排查方法,核心是把异步训练和 Agent Loop 真正跑起来。如果你还没建 Key,先去 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 创建一个,按用途分开命名,方便后面看用量。
接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有完整的接口说明和参数列表,配置时对照着看能少踩坑。想先验证模型效果再写进训练脚本,可以用模型对话页面 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 试跑几条请求。
如果你打算长期跑编码类或 Agent 类训练任务,Coding Plan 会更划算,地址是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。它适合需要持续调用、批量发请求的场景,比按次计费更可控。
最后给个实操建议:先把 Fully Async 的配置跑通,确认消息队列和参数同步正常,再往上叠 Agent Loop。一次改太多变量,出问题时很难定位是哪一层引起的。我试过同时开异步和 Agent Loop,结果卡在工具解析器上排查了半天,后来拆开一步步验证才找到原因。分层推进,每层都确认指标正常,是最省时间的做法。