找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1677

积分

0

好友

217

主题
发表于 昨天 06:36 | 查看: 3| 回复: 0

春节假期期间,我调研了当前 强化学习 (RL) 智能体场景下对基础设施的一些新兴需求。核心内容主要来源于各大厂近期发布的技术文章。有意思的是,除了最后一篇Seer的文章,其余三篇都是在春节期间发布的,看来大模型领域真是卷得一刻不停。

ForgeRL 系统架构图,展示了数据缓冲区、SGLang路由与服务器集群的协同

01 ForgeRL:大规模原生Agent RL系统

原文链接:Forge:大规模原生 Agent RL 系统

Agent框架实现

首先是将 Agent 独立为一个单独的 Server 模块,这是一个比较直观的抽象。

原本的数据流为:

RLFramework(Verl/Slime) -> RolloutEngine => [AsyncBuffer] -> Trainer

增加一个中间层后变为:

RLFramework(Verl/Slime) -> [AgentServer <-> RolloutEngine] => [AsyncBuffer] -> Trainer

整体架构如下:

ForgeRL 整体架构,分为AGENT、MIDDLEWARE、ENGINES三层

对于AgentServer,实际上又有两种情况:

  • 黑盒Agent:例如,如果想专门训练ClaudeCode+LLM的表现,那么在AgentServer的沙盒中启动一个ClaudeCode实例。ClaudeCode与RolloutEngine不断交互产生轨迹(trajectory),然后存入AsyncBuffer,后续就是常规的RL流程:计算Reward -> 计算优势函数 -> 计算Loss。
  • 白盒Agent:这一部分起初让人费解。核心背景在于多轮对话场景下,上下文(Context)过长时需要一些手段来清除部分内容(即上下文管理,Context Management)。

例如,在DeepSeek V32的技术报告中提到,对于思考(Thinking)模型,一旦用户新消息到达,就会清除之前的Thinking内容以避免上下文过长。

多轮对话与工具调用流程图,展示了Thinking与Tool call的交替过程

而对于搜索类Agent(如BrowseComp),目前几家主流模型(ClaudeCode opus4.5/DS v32/Kimi K2.5/GLM 50)都采用了discard-all策略。即当token使用量超过阈值的80%时,重置整个上下文窗口。ClaudeCode提供的API大致如下:

new_context_tool工具的API文档说明

在ForgeRL中,将上下文管理(Context Management)建模为Agent的一个动作(action),在训练中显式告知模型上下文的变化情况。这样,模型在训练阶段就能感知到CM的变化,进而在CM实际发生时更加关注那些对状态至关重要的Token(State-critical Token)。

02 RL调度策略

目前主流的RL框架基本都采用异步实现,因此必然会产生离策略(off-policy)的问题。这里的权衡在于:

  • 如果只取最新鲜的数据训练,丢弃旧版本数据,会导致训练样本更多地偏向“快而简单”的样本。
  • 如果只取最旧的数据训练,由于Rollout的长尾效应,会导致系统吞吐量下降。

但具体到调度策略的设计,没有银弹。ForgeRL提出了一种基于滑动窗口的算法:在窗口内可以任意获取轨迹进行训练,但必须等待窗口内最旧的数据完成后,窗口才会向前推进。

具体算法如下,原文讲解得非常详细:

基于可见窗口机制的训练调度器工作原理示意图

03 前缀树合并(Prefix Tree Merging)

对于基于组的强化学习(Group-Based RL,如GRPO)来说,一大特点是对于一个提示词(Prompt),会生成多个补全(Completions,例如20个)。这些补全大多拥有相同的前缀,在推理时可以借助SGLang的PrefixCache能力尽可能复用KV缓存。

这项工作的核心是,在Megatron训练时也采用前缀合并的思路。具体实现是先将多个Completions组织成树状结构,然后借助MagiAttention来实现注意力计算。实际上,MagiAttention本身是为CP并行设计的,但它提供了AttentionMask语义(可以控制每个序列的可见范围),从而能够实现树注意力(TreeAttention)的计算。

蚂蚁的AReal团队也提出了一种类似的TreeAttention方案,使用深度优先搜索(DFS)来计算注意力。详细内容可见论文:AREAL-DTA:Dynamic Tree Attention for Efficient Reinforcement Learning of Large Language Models。

Completions与Prefix Tree Merge的结构对比图

(1) 推理加速

这部分原文写得很清楚,在此直接摘录核心观点。

关于动态多标记预测(Dynamic MTP),此前与MMX合作过一些项目,比较了解。目前的最新进展是,对于多层MTP,现有的Eagle算法不能直接支持,需要采用一种叫做Vanilla的变种算法。这一块在SGLang中已由liangsheng大佬实现#15207,但目前是每层MTP单独运行一个CUDA Graph。可以优化为将多层MTP融合到一个CUDA Graph中,阿里云的同学正在支持,估计年后不久会有PR。

  • 动态MTP:首先引入MTP进行推理加速。同时,为了保证训练过程中草案模型(draft model)的高接受率,通过Top-K KL Loss在RL过程中持续训练独立的MTP头部,使其与RL策略保持对齐。
  • Rollout侧的PD分离:PD分离可以消除MoE调度中的PD干扰,为每个实例提供独立的并行和生成策略。这样能在最大化吞吐量的同时,优化长尾样本的延迟,防止极端样本阻塞FIFO调度器,并带来较高的离策略性(off-policy)。
  • 全局L3 KV缓存池:在多轮和超长上下文的Agent场景下,请求间拥有极高的共享前缀比例。但局部KV缓存受容量限制,无法达到满意的前缀缓存命中率。甚至在RL批次极大时,会发生大量因驱逐导致的重计算。因此,需要支持全局的L3 KV缓存。同时,Forge还通过调度器成本感知(scheduler cost-aware)的调度机制,权衡排队延迟和缓存传输时间来动态路由请求,在不使实例超载的前提下最大化缓存局部性。

(2) 复杂奖励函数

由于Agentic RL基本都是多轮调用,因此只对最终结果进行奖励(稀疏奖励)容易让模型养成偷工减料的习惯。需要设计一种密集奖励(dense reward)机制,对每一轮调用都进行打分。

这对基础设施来说倒没什么难度,大概只需要在RewardServer中实现一个新类即可。但在算法侧如何设计可能比较讲究。MMX的原文如下,讲得也比较直白。

为了解决超长轨迹的信用分配问题并确保稳定,他们设计了一个由三部分组成的复合奖励:

  • 过程奖励(Process Reward):监督Agent的中间行为(如惩罚语言混合或特定的工具调用错误),提供密集反馈,而不只依赖最终结果。
  • 任务完成时间奖励:将相对完成时间作为奖励信号。因为真实延迟不仅取决于Token生成,还受工具执行和子Agent调用影响。这能激励Agent主动利用并行策略、选择最短的执行路径来加速任务。
  • 用于降低方差的后续奖励(Reward-to-Go):长周期任务的稀疏奖励容易引发高梯度方差。使用Reward-to-Go来标准化回报,大幅提高了信用分配的精度,稳定了优化过程。

04 ROLL:Agentic RL训练实践经验

原文标题:苦涩的教训!ROLL 团队分享:Agentic RL 训练中的实践经验。

(1) Agent环境实现

与ForgeRL类似,ROLL框架也实现了独立的AgentServer:采用Rock作为沙盒,启动内置的iFlow Cli作为Agent实现与模型的交互。

ROLL训练框架与ROCK执行引擎的架构图

(2) Agent环境处理

在Agentic RL训练时,由于Agent经常会留下中间产物(如临时文件),这些产物可能会间接提示模型,因此需要严格的环境管理。否则会导致模型经常“偷懒”,甚至会直接读取或修改测试脚本(例如在观测结果中看到测试脚本调用次数显著上升)。

训练过程中Top 10命令使用次数随训练步数的变化折线图

  • 防止资源泄露与污染:ROLL进行严格的环境清理。
    • 在rollout前主动清理环境初始化或Agent安装过程中产生的中间文件。
    • 测试文件仅在最终评估阶段上传,与训练阶段严格隔离。
  • 在环境中主动引入多样性
    • 不同版本的软件包。
    • 不同镜像源。
    • 不同的环境配置细节。
  • 需要有意扰动甚至部分破坏环境:例如移除某个预装依赖或切换到不可用的镜像源。

(3) 训练数据处理

这部分讲的是数据处理问题。ROLL团队发现大量测试数据存在假阳性(false positive)问题:要么数据不完整,要么本身就是错误数据。因此在数据清洗阶段就引入了一个“LLM-as-judge”验证模块,让多个LLM审查每一组测试数据,只有通过验证的实例才会进入RL训练池。

具体会做两种检查:

  • Ground-truth验证:如果标准答案(golden solution)无法通过全部测试,则丢弃该实例。
  • No-op验证:如果在不执行任何有效操作的情况下也能通过测试,则丢弃该实例。

(4) 分块MDP(Chunked MDP)

对于GRPO,它是在Token级别做重要性采样;对于GSPO,是在序列(Sequence)级别。这里提出了一个Chunked MDP的概念,即将从一次环境交互到下一次环境交互之间的连续片段称为一个“块”(Chunk),在块级别计算奖励和重要性采样。

05 ThunderAgent:简单、快速、程序感知的Agent推理系统

春节期间看到Kang Hao在各大群里宣传他们的工作,同时我自己正好在关注这部分,索性拜读一番。

ThunderAgent解决问题的思路非常直观:遇事不决,加一层。ThunderAgent就是在AgentServerRolloutEngine之间加了一个代理层。

ThunderAgent系统架构流程图,包含Global Waiting Queue与多个后端

(1) 程序抽象(Program Abstraction)

目前AgentServerRollout基本都将每轮交互看作是独立的推理任务,这样做的坏处是无法实时追踪每个Agent任务的实时状态(例如已经消耗了多少Token)。

例如:一个编码智能体(SWE-Agent)正在修复GitHub上的Bug,其交互流程大致如下:

传统请求感知系统(SGLang):
┌─────────────────────────────────────────────────────┐
│  Step 1: 推理请求 ──→ vLLM(无状态,独立处理)        │
│  Step 2: 工具执行(编译器)──→ Kubernetes(无状态)    │
│  Step 3: 推理请求 ──→ vLLM(不知道Step1的存在)       │
│  Step 4: 工具执行(测试器)──→ Kubernetes(不知道历史)│
└─────────────────────────────────────────────────────┘

而引入独立的ThunderAgent层后,就可以完成对Agent任务抽象的流程管理,例如:

┌─────────────────────────────────────────────────────┐
│  P = ⟨ID="bug_fix_001",                             │
│        c=15000 tokens,   ← KV缓存占用               │
│        T={Docker, Bash}, ← 工具资源                  │
│        L=backend_2,      ← 绑定GPU节点               │
│        τ=Reasoning,      ← 当前推理阶段              │
│        s=Active⟩         ← 调度状态                  │
│                                                     │
│  Step 1 → Step 2 → Step 3 → Step 4                 │
│     ↑___________同一个程序P持续存在__________↑       │
└─────────────────────────────────────────────────────┘

(2) 状态感知暂停(State-Aware Pausing)

这里首先对任务的开销进行了建模,总成本近似为以下几项之和:

Cost_total ≈ Cost_decode + Cost_prefill + Cost_recompute + Cost_unused + Cost_caching

系统的优化目标主要是减小后三项,即Cost_recomputeCost_unusedCost_caching的开销。

任务总成本分解公式

当KVCache接近阈值时,由于ThunderAgent可以捕获程序的运行状态,因此引入了两个新的操作:

  • 暂停(Pause):暂停一个程序的执行,并释放其KVCache。
  • 恢复(Restore):恢复一个程序的执行。

有了这两种状态,ThunderAgent会基于周期性检测机制,当显存水位较高时暂停一部分程序的执行,而当显存水位降低时恢复程序执行。那么到底应该驱逐哪些程序呢?论文给了一个结论:优先驱逐占用KV Cache最小的程序,因为注意力计算的时间复杂度与序列长度的平方相关。

注意力计算复杂度公式 N²,及不等式 A²+B²<(A+B)²

(3) 工具资源管理(Tool Resource Management)

这里要解决的是Agent任务运行完成后环境被污染的问题。有两项设计:

  • 基于钩子的垃圾回收(Hook-based garbage collection):既然能够检测任务状态,那么就在任务达到终止(Terminated)状态时对环境资源进行清理。
  • 异步环境准备(Asynchronous environment preparation):环境初始化的延迟(例如安装Docker)可能成为瓶颈。为解决此问题,ThunderAgent监控全局队列,如果发现高优先级程序接近恢复阈值,就提前准备(Prepare)初始化环境。

06 Kimi-Seer:面向快速同步LLM强化学习的在线上下文学习

原文标题:Seer: Online Context Learning for Fast Synchronous LLM Reinforcement Learning。

首先需要说明,Seer这篇论文的研究场景主要是同步训练下的优化。但我看到的几个RL案例基本上都跑的是异步训练,所以其适用性还有待考证。

分段Rollout(Divided Rollout)

背景:如前文所述,对于GRPO这类基于组的RL算法,它会对同一个Prompt生成N个响应。但常见的调度算法会把N个响应的生成调度到同一台实例上执行,这会带来两个问题:

  1. 由于Rollout的长尾问题严重,会造成实例间负载不均衡。
  2. 单实例的KVCache可能爆满,触发抢占。

因此,论文中提出将请求切割为“块”(Chunk)粒度,每个Chunk为8K长度。同时,KV Cache需要缓存到共享存储(如Mooncake)中,这样在实例间迁移时无需重新进行预填充(Prefill)。

传统方式:
Group → [req1, req2, ..., req8] → 绑定到Instance A,跑完为止
                                         ↑
                                   长的拖死短的,无法迁移

Seer的方式:
Group → req1 → chunk1(8K) → chunk2(8K) → chunk3(8K) → ...
          ↑              ↑              ↑
       调度到A         调度到B         调度回A(按负载动态选)

上下文感知调度(Context-Aware Scheduling)

要解决负载不均衡,本质上需要知道哪些请求是长尾请求,也就是最好能预知响应的长度。因此,一个直观的想法是对于GRPO任务:

  • 优先选择第一条请求作为推测请求(speculative request)来优先调度。
  • 该请求调度完成后,以其长度作为组内其他响应长度的估计。
  • 按照长任务优先的策略调度剩余的请求。

大致流程为:

阶段1:Length Filtering(长度过滤)
  └─ 用SFS(Shortest First)调度所有Speculative Requests
     → 短的很快完成,长的暴露为长尾候选
阶段2:Length Estimation Update(长度估计更新)
  └─ Context Manager记录每个Group已完成请求的最大生成长度
     → 作为该Group预期长度的在线估计
阶段3:Approximate LFS调度
  └─ 对剩余请求按预测长度降序调度
     → 长任务优先,与短任务并行执行,填满批次

自适应分组推测解码(Adaptive Grouped Speculative Decoding)

分布式推测解码架构图,包含Global Grouped Draft Server

对于采用草案模型(Draft-Model)做推测解码(Speculative Decoding)的方式,经常会由于RL在目标模型(Target Model)和草案模型之间的权重更新不同步,导致草案模型的接受率降低。

传统的N-Gram算法受限于单机执行,无法充分利用GRPO算法的特点。因此,这里提出了一种类似“分布式N-Gram”的思路,即将不同组(Group)内的响应产生的Token一起存入一颗分布式的压缩后缀树中,这样能够匹配的信息更多。

大致流程为:

┌──────────────────────────────────────────────────────────────┐
│  Step 1: 异步Append(各实例独立)                             │
│  Instance_A 生成了 req_0 的新token: [tok_a, tok_b, tok_c]   │
│  Step 2: 全局聚合(DGDS Server端)                           │
│                                                              │
│  收到来自不同实例的更新:                                      │
│  G1/req_0: [tok_a, tok_b, tok_c, ...]  ─→ ┐                │
│  G1/req_1: [tok_x, tok_y, tok_z, ...]  ─→ ├→ Group G1's CST│
│  G1/req_2: [tok_p, tok_q, tok_r, ...]  ─→ ┘                │
│                                                              │
│  Step 3: 周期性Fetch(各实例拉取)                            │
│  Instance_A 只拉取自己正在处理的group的CST                   │
│  支持增量同步:只传上次fetch之后的新增内容                     │
└──────────────────────────────────────────────────────────────┘

作者:attack204,已获作者授权发布
来源:https://zhuanlan.zhihu.com/p/2007250216227729670


本文梳理了近期各大厂在 Agentic RL 系统优化上的前沿思考,从架构抽象、调度策略到推理加速,展现了人工智能基础设施领域快速迭代的活力。对于构建高吞吐、低延迟的分布式系统,这些实践经验具有重要参考价值。更多关于后端架构与系统设计的深度讨论,欢迎在云栈社区交流分享。




上一篇:AI推理革命:专用芯片成本骤降10倍,GPU会步矿机后尘吗?
下一篇:OpenClaw技能进阶指南:50个精选Skills释放AIGC Agent生产力
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-25 09:11 , Processed in 1.731876 second(s), 45 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表