1. 为什么 Agentic RL 训练总卡在“等”上如果你最近在跑 Agentic 场景的强化学习后训练大概率遇到过这种画面8 张卡里 6 张在等 Rollout 生成Trainer 干瞪眼一条超长样本把整个 step 拖到超时好不容易跑起来一个 Rollout 实例 OOM整个任务从头再来。这不是你配置写错了而是传统 RL 训练框架的结构性问题——Rollout 和 Train 绑在同一组 GPU 上串行执行谁慢谁说了算。小红书 AI 平台团队开源的 Relax就是冲着这个结构性问题来的。它是一个为全模态数据、Agentic 工作流和大规模异步训练协同设计的现代 RL 训练引擎核心思路很直接把 Rollout 和 Train 拆成两个独立服务用数据总线连接让推理生成和梯度更新真正并行跑起来。官方实测全异步 Off-Policy 模式相比共卡 On-Policy 吞吐提升 76%相比 veRL 的全异步实现提升 20%。这篇文章面向的是已经在跑或准备跑 Agentic RL 训练的工程师我会交付一套可复制的 Relax 启动配置骨架、TaoToken 统一 Key 接入 settings.json 的示例以及吞吐对比验证的具体动作。你可以在自己的 H800 或 A800 集群上按步骤复现不需要从头理解整个架构。先说清楚 Relax 解决的三层问题这样你配置的时候知道每个参数在干什么。第一层是数据异构多模态的图片、视频、音频原始数据体积大CPU 预处理开销高编码后 token 爆炸传统并行策略很难高效协同。第二层是系统脆弱上千卡长时训练OOM 和 NCCL 超时是常态传统方案缺乏分钟级故障恢复和单角色弹性伸缩。第三层是角色耦合Colocate 方案下各角色共享 GPU 只能串行Trainer 等最慢的 Rollout现有全异步方案虽然拆了组但缺少细粒度流水线调度。Relax 用一套协同设计把这三个问题一起解决而不是分别打补丁。2. TaoToken 前置统一 Key 接入 settings.json在开始配 Relax 之前先把模型调用的 Key 管理理顺。Agentic RL 训练里Rollout 阶段要调模型生成、Reward 阶段可能要用 LLM-as-Judge 做打分、工具调用可能涉及外部模型服务如果每个环节各配一套 Key调试和轮换都是灾难。我的做法是用 TaoToken 做统一入口一个 Key 覆盖对话、编码、Agent 多种调用场景。TaoToken 的官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 注意 API 地址不带 UTM 参数配置的时候别把跟踪参数写进去。你需要先去控制台创建 API Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 创建完在 API Keys 页面复制页面地址 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。拿到 Key 之后在项目根目录建一个 settings.json把模型调用统一收口到这里。下面是我实际用的骨架你可以直接复制改{ llm: { provider: taotoken, base_url: https://taotoken.net/api, api_key: sk-你的TaoToken密钥, default_model: claude-sonnet-4-20250514, timeout_seconds: 120, max_retries: 3 }, rollout: { engine: sglang, model_path: /data/models/Qwen3-4B, tp_size: 2, max_new_tokens: 4096 }, reward: { judge_model: claude-sonnet-4-20250514, use_llm_judge: true } }这里有几个点要注意。base_url 必须写 https://taotoken.net/api 不要加任何查询参数否则部分 SDK 会解析异常。api_key 建议用环境变量注入settings.json 里只留占位符避免提交到仓库。default_model 按你实际要调的模型填Agentic 场景下多轮推理建议选上下文窗口大的。timeout_seconds 给到 120 是因为 Agentic 多轮交互单次可能跑很久太短会频繁触发重试。如果你用的是 Claude Code 做辅助编码TaoToken 也支持 Anthropic 兼容接入配置文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite ClaudeCodeAnthropic 的接入说明在 https://taotoken.net/doc/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode-anthropicutm_campaignrewrite 。这样你在写 Relax 配置脚本的时候可以让编码助手直接读你的 settings.json 结构减少手写 YAML 的出错率。3. 可复制的 Relax 启动配置骨架Relax 的架构是 Ray Serve Megatron SGLang 的组合Rollout 服务用 SGLang 做推理生成Train 服务用 Megatron 做梯度更新中间通过 TransferQueue 数据总线连接。配置的核心是把每个角色拆成独立的 Ray Serve 服务各自有独立的资源配额和故障域。先装依赖。Relax 基于 Ray 生态建议用 Python 3.10 以上pip install ray[serve] sglang megatron-core transferqueue pip install relax-rl如果你的集群有 InfiniBand确认 NCCL 走 IB 而不是 TCPexport NCCL_IB_DISABLE0 export NCCL_IB_GID_INDEX3 export NCCL_SOCKET_IFNAMEib0 export NCCL_DEBUGWARN接下来是 Relax 的启动配置。我把它拆成三个部分集群资源声明、Rollout 服务配置、Train 服务配置。先看集群资源声明这是 Ray 的初始化部分import ray from ray import serve ray.init( addressauto, runtime_env{ env_vars: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: ${TAOTOKEN_API_KEY}, NCCL_IB_DISABLE: 0, NCCL_SOCKET_IFNAME: ib0, } } ) serve.start(http_options{host: 0.0.0.0, port: 8000})Rollout 服务用 SGLang 引擎配置重点是 tp_size 和 micro batch 的切分粒度。Relax 把全局 batch 切成 micro batch 做流水线256 条的全局 batch 切成 32 条一组每组生成完立即写入 TransferQueuefrom relax import RolloutService, RolloutConfig rollout_config RolloutConfig( enginesglang, model_path/data/models/Qwen3-4B, tp_size2, dp_size4, micro_batch_size32, global_batch_size256, max_new_tokens4096, temperature0.7, top_p0.95, partial_rolloutTrue, partial_rollout_timeout300, enable_processor_poolTrue, processor_workers8, ) rollout_service RolloutService.options( num_replicas4, ray_actor_options{num_gpus: 2} ).bind(rollout_config)Train 服务用 Megatron 后端关键是和 Rollout 服务的资源隔离。Train 服务不直接引用 Rollout 的实例全部通过 TransferQueue 通信from relax import TrainService, TrainConfig train_config TrainConfig( backendmegatron, model_path/data/models/Qwen3-4B, tp_size2, pp_size1, dp_size4, micro_batch_size8, global_batch_size256, learning_rate1e-6, kl_coef0.01, clip_range0.2, advantage_estimatorgrpo, use_r3True, r3_replay_modeasync, ) train_service TrainService.options( num_replicas1, ray_actor_options{num_gpus: 8} ).bind(train_config)TransferQueue 数据总线是连接两个服务的核心配置里要指定存储路径和异步读写参数from relax import TransferQueueConfig tq_config TransferQueueConfig( storage_path/dev/shm/relax_tq, max_size_gb64, async_putTrue, async_getTrue, prefetch_batches4, enable_multimodalTrue, )把三个服务串起来启动from relax import RelaxPipeline pipeline RelaxPipeline( rolloutrollout_service, traintrain_service, transfer_queuetq_config, checkpoint_dir/data/checkpoints/relax_qwen3_4b, dcs_enabledTrue, dcs_topology_awareTrue, elastic_rolloutTrue, min_rollout_replicas2, max_rollout_replicas8, ) pipeline.run()这套配置的核心逻辑是Rollout 用 4 个副本各 2 张卡做推理Train 用 8 张卡做梯度更新两边通过 TransferQueue 异步读写。micro_batch_size32 是 Rollout 侧的切分粒度Train 侧 micro_batch_size8 是训练切分粒度两者不需要一致TransferQueue 会做缓冲。partial_rolloutTrue 开启部分回收超时样本已生成的部分直接复用不白等也不白扔。4. 验证请求与吞吐对比动作配置写完先做一次最小验证确认 Rollout 和 Train 能正常通信。用一个简单的数学推理任务跑通链路from relax import RelaxClient client RelaxClient(http://localhost:8000) # 提交一个验证任务 task client.submit( prompt计算 23 * 47 的结果并解释计算过程。, reward_fnmath_verify, max_turns3, ) result client.wait(task.id, timeout600) print(f生成结果: {result.output}) print(fReward: {result.reward}) print(fRollout 耗时: {result.rollout_time_ms}ms) print(fTrain 耗时: {result.train_time_ms}ms)如果链路通了你会看到 Rollout 和 Train 的耗时是重叠的而不是串行相加。接下来做吞吐对比验证这是复现 76% 提升的关键动作。对比实验分三组Colocate On-Policy、Async On-Policy、Async Off-Policy。用同一份 DAPO-MATH-17k 数据集Qwen3-4B 模型16 张 H800。Colocate 模式把 Rollout 和 Train 绑在同一组卡上# Colocate 模式启动 python -m relax.launch \ --mode colocate \ --model /data/models/Qwen3-4B \ --dataset /data/datasets/DAPO-MATH-17k \ --gpus 16 \ --global_batch_size 256 \ --micro_batch_size 32 \ --steps 100Async Off-Policy 模式用上面的分离配置Rollout 和 Train 各占独立 GPU# Async Off-Policy 模式启动 python -m relax.launch \ --mode async_off_policy \ --model /data/models/Qwen3-4B \ --dataset /data/datasets/DAPO-MATH-17k \ --rollout_gpus 8 \ --train_gpus 8 \ --global_batch_size 256 \ --micro_batch_size 32 \ --partial_rollout \ --steps 100跑完之后对比 steps/hour 指标。官方数据是 Async Off-Policy 达到 28.7 steps/hourColocate 是 16.3 steps/hour提升 76%veRL 全异步是 23.9 steps/hourRelax 比它快 20%。你在自己环境跑的时候重点看三个指标steps/hour、单 step 的 wall-clock 时间、以及长尾样本对整体 step 的拖累程度。验证收敛质量同样重要。按 wall-clock time 看三种模式最终收敛到相同 reward 水平但 Async Off-Policy 达到同等 reward 的 wall-clock 时间比 Colocate 缩短 43%。你可以在训练日志里记录每 10 个 step 的平均 reward画一条 reward vs wall-clock 的曲线确认没有因为异步导致质量下降。如果你在验证过程中需要快速调模型做对比测试可以用 TaoToken 的模型对话入口 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 直接对比不同模型在相同 prompt 下的输出差异不用改训练代码。5. 本篇常见错排查配置 Relax 的过程中有几个报错出现频率很高我按实际踩坑顺序列出来。第一个是 TransferQueue 连接超时。报错长这样TransferQueueTimeoutError: Failed to connect to storage at /dev/shm/relax_tq after 30s。原因通常是 /dev/shm 空间不够或者多个 Ray actor 挂载的路径不一致。检查df -h /dev/shm如果小于 64GB把 storage_path 改到实际数据盘比如/data/relax_tq。另外确认所有节点的 Ray runtime_env 里路径一致跨节点训练时 /dev/shm 是各自独立的必须用共享存储。第二个是 NCCL 通信卡住。现象是 Rollout 和 Train 都启动了但 step 一直不推进日志停在NCCL INFO Bootstrap。这通常是 IB 网卡配置问题。先确认ibstat能看到活动端口然后检查 NCCL_SOCKET_IFNAME 是否写对用ip addr看实际网卡名。如果集群没有 IB把 NCCL_IB_DISABLE1走 TCP 也能跑但吞吐会降。还有一个隐蔽原因不同角色的 CUDA_VISIBLE_DEVICES 有重叠导致 NCCL 初始化时 rank 冲突检查 Ray actor 的 num_gpus 分配是否和物理卡一一对应。第三个是 R3 路由回放导致 log probs mismatch。报错是RoutingMismatchError: expert routing mismatch between rollout and training。Relax 的 R3 机制在 Rollout 时记录路由决策Training 时原样回放但如果你的模型是 MoE 且用了自定义路由需要确认 r3_replay_mode 设为 async。同步模式下 R3 开销会加到关键路径上官方数据是 veRL 的 R3 开销 32%Relax 异步模式只有 1.9%。检查配置里use_r3True和r3_replay_modeasync同时生效。第四个是 partial rollout 回收的数据格式错误。报错PartialRolloutFormatError: incomplete sequence missing finish_reason。这是因为超时样本的已生成部分没有正确的结束标记下游 Advantage 计算时解析失败。解决办法是在 RolloutConfig 里设partial_rollout_pad_token|endoftext|让回收的部分自动补结束符。另外确认 partial_rollout_timeout 不要设太短300 秒是 Agentic 多轮场景的合理值太短会导致大量样本被截断。第五个是 DCS 权重同步失败。报错DCSSyncError: rank mapping mismatch for TP2 PP1。DCS 做拓扑感知的权重传输如果 Rollout 和 Train 的 TP/PP 配置不一致rank 映射会对不上。检查两边 tp_size 和 pp_size 是否匹配如果不匹配DCS 需要显式配置 rank_mapping。弹性扩缩场景下新加入的 Rollout 实例会经过 PENDING→CREATING→HEALTH_CHECKING→WEIGHT_SYNCING→READY→ACTIVE 六个阶段任何一步超时都会回滚看日志确认卡在哪一步。如果你在接入 TaoToken 做 Reward 打分时遇到 401 或 429先确认 API Key 有没有过期然后看 settings.json 里的 base_url 是不是写成了带 UTM 的地址。正确的 API 端点是 https://taotoken.net/api 不带任何查询参数。429 通常是并发太高Reward 服务里加个信号量限流或者把 max_retries 调到 5。6. 长期编码与 Agent 场景的接入建议Relax 的服务化架构对 Agentic 场景是天然适配的。自定义 Rollout 可以写成可插拔服务Agent 要调工具、查数据库、跑沙箱挂上去就行多轮状态管理在服务化架构下就是服务间的消息传递Reward 可以按需组合 Rule-based、LLM-as-Judge、自定义函数。如果你打算长期跑 Agentic RL 训练建议把模型调用统一收口到 TaoToken用 Coding Plan 管理编码和 Agent 场景的调用配额入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。这样 Rollout 生成、Reward 打分、工具调用三条链路共用一个 Key轮换和监控都省事。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有各语言的 SDK 示例和错误码说明。最后说一个实际经验Relax 的 micro batch 流水线粒度不要设得太细。我试过把 global_batch_size256 切成 8 条一组的 micro batch结果 TransferQueue 的元数据开销反而拖慢了整体吞吐。32 条一组是官方验证过的平衡点长尾样本的影响被限制在单个 micro batch 内同时流水线调度开销可控。如果你的 Agentic 任务单条样本特别长可以适当放大到 64但不要超过 global_batch_size 的四分之一。