2026/8/28 17:34:38

Agentic RL后训练动态资源分配:Libra如何提升集群吞吐

Agentic RL后训练动态资源分配:Libra如何提升集群吞吐 在大模型后训练进入 Agent 阶段之后资源分配从“怎么把模型训练完”变成了“怎么让多个训练任务在一个集群里都按时跑完”。Agentic RL 后训练和普通 SFT 最大的区别是 workload 不稳定策略模型要反复调用工具、查询知识库、和环境交互轨迹长度从几百 token 到几万 token 都可能出现。同一个任务在 rollout 阶段可能只需要少量 GPU在 trainer 阶段又突然需要整卡。如果按传统方式给每个任务固定分 8 卡、固定显存额度集群会出现排队区堆满任务运行区却大量 GPU 空转的情况。香港中文大学和恒生大学提出的 Libra正是把焦点放在后训练资源分配上公开描述中吞吐最高提升 3 倍。下文不会逐条复述论文公式而是把“为什么要动态分配、资源分配器怎么设计、怎么用一个最小模拟器验证效果”讲清楚。1. 先理解 Agentic RL 后训练为什么这么不稳定1.1 后训练不再只是把文本样本喂进去传统 SFT 的后训练过程是准备一批指令和回答计算交叉熵损失更新模型参数。每个 batch 的输入输出长度基本可控显存占用、计算时间、数据读取速度都相对平稳资源分配可以按“一个 job 固定占几卡”来规划。Agentic RL 后训练不同。模型不再是简单生成一段文本而是在一个回合制环境里反复决策它要决定调用哪个工具读取工具返回结果再决定下一步动作。每一步都会产生新的文本和状态整条轨迹的长度由环境反馈决定。比如一个 agent 在调用搜索接口时返回 8000 token在下一轮又只返回 50 token同一个 job 内的显存需求和推理延迟会出现很大毛刺。训练目标也不再是直接对文本求损失而是把采样到的轨迹放入策略梯度目标里更新。为了算 advantage还需要 critic 或 reward model 对每个状态打分如果使用 PPO 这类方法还要维护旧策略、做 clip、做 GAE。这些环节都会额外消耗显存、CPU 和内存而且消耗量跟着轨迹内容变化。1.2 四个环节的资源画像会按分钟级变化在一个典型的 Agentic RL 后训练循环里至少有四个环节在抢资源而它们各自的资源敏感度差异很大。环节主要资源波动来源最容易出现的问题轨迹采样 rolloutGPU/CPU 推理、KV cache、网络工具调用次数、环境响应长度、并发度GPU 空转或推理排队奖励/验证模型GPU/CPU 推理外部服务延迟、评估 prompt 长度单个慢请求拖慢整批结果策略更新GPU 计算、显存带宽batch 内轨迹长度方差、是否用梯度检查点显存超限或训练吞吐下降经验缓存 replay bufferCPU 内存、磁盘长轨迹积压、日志量内存被打满、日志写入慢因此只看 GPU 利用率是不够的。一个 job 可能 GPU 利用率很低但 CPU 上已经有几千条 rollout 在排队另一个 job 可能 GPU 计算很忙但显存因为某一条超长轨迹突然被打爆。资源调度必须能同时感知这些维度否则任何单一指标都会误导决策。1.3 多任务并发时静态配额会放大不确定性单任务训练就算波动大只要资源够也能靠排队慢慢跑完。真正让问题恶化的是多个训练 job 共享同一个集群。假设集群有 8 张卡Job A 正在做 Agentic RL 后训练当前处于 rollout 阶段8 张卡里实际只有 1 张卡在做推理其余 7 张处于空闲或低负载。Job B 在后面对列里等着申请 8 张卡。静态调度下Job B 必须等到 Job A 整体结束才能获得资源即使 Job A 在未来五分钟内都用不到那 7 张卡它们也不能转给 Job B。结果就是集群总 GPU 利用率不高任务排队时间却很长。Libra 这类资源分配方案要解决的就是这种“固定配额和实际需求错配”的问题。它把后训练 job 当作一个动态负载而不是一个提交后就不会变化的黑盒。2. Libra 的资源分配核心把任务当成动态可调度负载2.1 静态预留为什么在 Agentic RL 上失灵静态预留最典型的做法是每个 job 申请固定数量的 GPU、固定显存、固定 CPU调度器只在开始时刻做一次决策之后不再调整。这种模型适合计算量均匀的批处理任务。它的优点是简单、可预期、不容易互相影响。但 Agentic RL 后训练有两个特征会破坏这个假设需求随时间变化rollout 和 training 交替出现工具调用和长文本又会引入尖峰。需求随内容变化同样一个模型在不同环境、不同用户 prompt 下产生的轨迹长度差异很大。于是固定 8 卡的 job 可能在 60% 时间内只用到 2 卡却把另外 6 卡锁死其他 job 想用却申请不到。这属于典型的资源碎片化也会让队列里的任务越积越多。2.2 动态调度器只需要四个核心组件Libra 这类方法的具体实现可以各有不同但核心思路通常是四件事观测、预测、规划、反馈。观测实时采集每个 job 的 GPU 使用率、显存水位、CPU 排队长度、rollout 平均步数、tool call 次数。预测根据当前轨迹和历史窗口预测下一个时间段内这个 job 对资源的需求。规划在总资源有限、每个 job 有最小配额和最大配额约束下把空闲资源动态分配给需求较高的 job。反馈执行新分配方案后继续观测指标如果发现任务被频繁抢占或队列延迟变大就回调参数。可以把这四个组件理解成一个闭环控制。调度器不是每秒钟都去抢资源而是每隔一个固定周期做一次再平衡。这个周期不能太短否则调度本身会成为瓶颈也不能太长否则无法跟上工具调用带来的负载尖峰。2.3 GPU 卡之外还要分配显存、CPU、内存和网络很多集群调度器只认“卡数”这在实际 Agentic RL 后训练里远远不够。资源维度单位典型瓶颈不纳入调度的后果GPU 算力TFLOPS/SM 占用策略更新和 rollout 推理算力不足训练步数变慢GPU 显存GBKV cache、模型权重、梯度显存超限job OOMCPU 核数vCPUrollout 数据预处理、tokenizerCPU 排队GPU 空转主机内存GBreplay buffer、采样结果暂存内存不足进程被杀网络带宽GB/s模型并行通信、工具调用通信开销拖慢训练实际调度中最好把每个 job 抽象成一组多维资源需求例如gpu: 8, gpu_mem: 80Gi, cpu: 32, mem: 128Gi而不是只写gpu: 8。否则会出现显存明明够但卡数被人占满导致小显存任务无法调度或者 CPU 已经排队到几千条但 GPU 还空在那里等数据。2.4 如何理解“吞吐最高提升 3 倍”“吞吐提升 3 倍”在标题里是一个很引人注意的数字但要正确理解它的适用范围。它通常不是指所有任务、所有集群配置下都能提升 3 倍而是指在资源竞争明显、job 之间需求互补、静态分配造成大量空闲的场景下动态分配让集群整体完成工作量更快。吞吐指标本身也分很多种单位时间完成的训练 step 数。单位时间完成的 rollout 轨迹数。单位时间完成的 job 总数。单位时间完成的有效训练量例如处理了多少 token。不同指标下的提升幅度会不一样。因此在对比方案时先明确“吞吐”到底指什么再看这个指标是否和业务目标一致。Libra 的价值不在于把单卡算力提升而在于把集群空闲资源利用起来减少排队和等待。3. 用最小模拟器验证“固定分卡”和“动态分配”的差距3.1 最小可行模型需求曲线加工作量为了理解资源分配策略不一定要先搭大规模集群。可以用一个 Python 模拟器建模每个 job 有一条需求曲线表示每个时间片内它能用掉多少资源还有一个总工作量表示完成它需要消耗多少资源单位。调度器在每时刻决定给每个 job 分配多少资源实际推进量是分配量和需求量的较小值。下面的模型只用于说明思路不包含显存、带宽、抢占成本。要放到真实集群需要再叠加这些约束。from dataclasses import dataclass dataclass class Job: name: str demand_curve: list[float] total_work: float JOBS [ Job(tool-call-heavy, [0.2, 0.1, 0.9, 0.9, 0.9, 0.1], 2.6), Job(long-context, [0.8, 0.8, 0.5, 0.5], 2.0), Job(rag-light, [0.5, 0.5, 0.3, 0.3, 0.3], 1.7), Job(interleaved, [0.4, 0.9, 0.1, 0.9, 0.4], 2.4), ]这里的demand_curve是每个时间片的最大可用资源取值在 0 到 1 之间。total_work是完成该 job 需要的总资源单位。如果 job 没完成曲线会循环复用用来模拟周期性出现的 rollout 高峰。3.2 静态平分调度静态调度的规则最简单不管需求是多少始终给每个 job 平均分配资源。def static_equal(demands): n len(demands) return [1.0 / n] * n这种策略的问题是当一个 job 的需求低于平均份额时多出来的份额不会被其他 job 使用当一个 job 的需求远高于平均份额时它又会因为拿不到更多资源而迟迟无法完成。在真实集群里这就对应着 GPU 空转和任务排队并存。3.3 按需求动态分配调度动态调度可以先保证