青稞AI技术交流群:大模型RL框架与开源项目解读

本文基于Agentic RL训练框架的开发实践,整理了在框架搭建过程中遇到的核心问题与对应的解决方案思路,从Rollout模块的演进、两种架构的对比到核心组件的设计细节逐一展开讨论。
从LLM Rollout到Agentic Rollout的演进
强化学习(RL)的训练流程通常围绕数据生成与模型迭代两个核心环节交替展开:首先,Rollout模块基于当前训练好的策略执行任务,生成交互轨迹与输出结果,并通过评测机制计算对应奖励;随后,训练模块利用采集到的轨迹数据更新模型参数,再将更新后的参数同步回Rollout模块,用于下一轮的数据生成,由此形成完整的训练闭环。
对于面向推理任务的LLM RL来说,一次Rollout流程相对简单:系统向模型发送提示词,获取模型输出后直接计算奖励,属于单轮的交互过程。
而Agentic RL的场景则更为复杂。以代码修复任务为例,智能体需要完成读取仓库文件、分析错误信息、修改代码、测试验证等一系列步骤,这要求智能体多次调用大模型,并通过工具调用执行文件操作、运行命令、安装依赖等操作。因此,Agent的执行过程需要多轮模型调用与工具交互,这也对Rollout模块提出了更高的要求:不仅需要支持模型推理,还要能够管理智能体及其运行环境,并完整采集执行过程中产生的多轮交互轨迹。
耦合式架构的实现与局限
1. 实现原理
早期实现Agentic RL最直接的方式,是在Rollout模块中直接内置Agent Loop逻辑,模拟真实智能体的执行流程。以下是核心的伪代码实现:
messages = [task_prompt]
while True:
# 调用模型并记录本轮交互
response = llm_server.generate(messages)
trajectory.record(messages, response)
# 模型给出最终结果,任务结束
if response.is_final_answer():
final_answer = response.content
break
# 执行工具,并将结果加入下一轮上下文
tool_result = env.execute(response.tool_call)</code></pre>
在这种耦合式的实现中,整个Agent Loop由Rollout模块直接驱动,模型输入、输出以及工具调用都可以在执行过程中被直接记录。任务完成后,系统会按照执行顺序将交互数据整理为完整的轨迹,并结合评测结果生成训练样本。因此,Agent执行、模型推理与轨迹采集都位于同一个控制流程中。
2. 架构优势
智能体行为易于控制与调试
由于Agent Loop完全由Rollout模块实现,智能体的执行细节对开发者完全透明。Rollout可以统一管控模型调用、工具执行与任务终止流程,还可以根据训练需求灵活添加上下文压缩、子Agent调用、轮次限制等干预逻辑。
轨迹采集简单高效
模型与工具的调用都发生在Rollout内部,每一轮交互都可以在执行时直接记录。这种实现方式成本低,且能保证采集到的轨迹与实际执行过程完全一致。
3. 架构局限
扩展性较差
不同的智能体框架通常存在差异,比如系统提示词配置、上下文管理策略、工具调用逻辑等都可能不同。为了接入RL训练,Rollout模块需要针对每种智能体单独适配,每新增一种智能体都需要重新开发适配代码,维护成本较高。
无法适配黑盒智能体
对于像Claude Code这类不开源的黑盒智能体,只能通过外部接口提交任务并获取最终结果,无法获取其内部执行逻辑,因此无法完全复刻其运行流程,导致训练模拟的逻辑与实际使用存在偏差,最终影响训练效果。
解耦式架构的设计思路与核心组件
耦合式架构的核心问题在于轨迹采集依赖Rollout模块对智能体具体逻辑的模拟,这直接限制了系统的扩展性与黑盒智能体的接入能力。解耦式架构则采用了完全不同的设计思路:让智能体保持原生运行状态,Rollout模块不再实现具体的Agent Loop逻辑,而是从外部管理智能体的生命周期,并在智能体与大模型的通信链路上采集交互轨迹。
这种设计下,Rollout模块不需要理解智能体内部如何维护状态、调用工具,只需要关注智能体的启动与管理、模型交互的轨迹采集,以及任务完成后训练样本的生成。不过,要落地解耦式架构,需要解决三个核心问题:如何运行和管理智能体、如何采集完整的交互轨迹、如何协调一次完整的Rollout流程。
针对这三个问题,我们的实现引入了三个核心组件:Controller、Runtime Manager与Gateway,分别负责不同的核心功能。
1. 总体架构设计
- Controller:负责Rollout任务的统一编排,接收上游训练系统下发的任务,协调Runtime Manager与Gateway的执行流程,跟踪任务状态,并在任务结束后汇总智能体执行结果、模型轨迹与评测信息。
- Runtime Manager:负责智能体的运行时管理,包括创建隔离环境、准备任务文件与依赖、启动和停止智能体,以及回收运行日志与输出产物。
- Gateway:作为智能体与大模型服务之间的代理服务,负责轨迹采集,在转发模型请求的同时记录请求、响应等信息,完整采集多轮交互轨迹。
- 智能体:在隔离环境中运行的智能体框架,根据任务需求向Gateway发送推理请求。
- 大模型服务:负责实际的模型推理,接收来自Gateway的请求并生成对应回复。
整体流程为:Controller接收任务后,通过Runtime Manager创建运行环境并启动智能体,同时在Gateway中建立模型交互会话;智能体运行期间通过Gateway访问大模型服务,Gateway负责记录完整的交互轨迹;任务结束后,Controller汇总智能体执行结果、模型轨迹与评测信息,最终生成训练样本。
2. Agentic Rollout完整执行流程
一次完整的Agentic Rollout可以分为三个核心阶段:资源创建与初始化、智能体执行与轨迹采集、结果汇总与资源释放。
资源创建与初始化
Controller接收上游训练系统下发的任务,任务信息包括本次任务的提示词、智能体配置、评测规则等。首先,Controller会请求Gateway创建独立的Session,Session用于关联本次任务的轨迹数据与状态,每个任务使用唯一的Session ID进行标识,Gateway会返回Session ID与模型接入地址,后续智能体的所有模型请求都将通过Session ID关联到本次任务。随后,Controller请求Runtime Manager为本次任务创建独立的沙盒环境,为智能体提供隔离的文件系统与执行环境,Runtime Manager会根据任务配置初始化环境,包括写入任务文件、准备代码仓库、安装依赖,并向沙盒环境注入任务提示词、Session ID与Gateway地址等必要信息。
智能体执行与轨迹采集
环境准备完成后,智能体进程会在沙盒环境中启动。当智能体调用大模型时,请求会先发送到Gateway,Gateway根据Session ID将请求归入对应的Session,记录模型输入后转发至大模型服务。模型返回结果后,Gateway会保存响应内容、工具调用信息与Token等轨迹数据,再将响应返回给智能体。这一过程会循环多次,同一Session下的连续请求与响应共同构成本次任务的完整轨迹。由于轨迹采集发生在模型通信链路上,Gateway不需要理解智能体的内部状态维护逻辑,也不依赖智能体的具体实现。
结果汇总与资源释放
智能体任务结束后,Runtime Manager会根据进程状态回收执行结果,包括错误信息、运行日志、修改后的环境数据等。随后,Controller会从Gateway获取对应Session下的完整轨迹,检查轨迹信息是否完整并过滤失败的轨迹。之后系统会按照任务定义的规则进行评测,生成奖励等训练信号。最终,Controller会将任务输入、交互轨迹、智能体执行结果与评测结果对齐,转换为上游训练模块所需的训练样本。完成数据回收后,系统会关闭本次任务对应的Session,并释放沙盒环境资源。至此,智能体执行、模型推理与轨迹采集被完全解耦到不同模块,通过统一的Session ID关联到同一个任务,完成一次完整的Agentic Rollout闭环。
Gateway模块的核心设计细节
1. 协议归一化处理
不同的智能体框架通常通过不同的模型API发起请求,常见的协议格式包括OpenAI Chat Completions、OpenAI Responses与Anthropic Messages,三者的核心差异如下表所示:
协议类型
典型适配框架
上下文组织方式
系统提示词配置
工具调用实现
工具结果关联方式
OpenAI Chat Completions
LangChain ChatOpenAI
通过messages数组管理完整对话历史
通过developer或system角色的消息放入messages数组
在Assistant回复消息中通过tool_calls数组声明工具调用
使用role为tool的消息,通过tool_call_id关联工具调用与结果
OpenAI Responses
Codex CLI
通过instructions和input组织上下文,所有交互内容统一封装为Item
通常作为顶层instructions配置
工具调用作为独立的function_call Item出现在output中
使用function_call_output Item,通过call_id关联工具调用与结果
Anthropic Messages
Anthropic官方SDK
通过messages数组管理对话,系统提示词单独配置
通过system参数单独传递,或放入messages的system角色消息
工具调用通过content中的tool_use对象实现
使用tool_result消息类型,通过id关联工具调用与结果
为了复用同一套请求处理与模型调用逻辑,Gateway在入口处添加了协议适配器,将不同协议格式的请求统一转换为标准的OpenAI Chat Completions格式,后续模块只需要面向这一种格式进行处理,实现了协议与业务逻辑的解耦。
除了请求协议的转换,适配器还需要处理响应阶段的流式协议封装。由于Gateway向大模型服务转发请求时通常采用非流式模式,但部分智能体框架仅支持流式响应,如果直接返回完整结果可能导致客户端解析失败。因此,适配器会将大模型服务返回的完整结果重新封装为对应协议可消费的SSE事件流,包括文本增量、工具调用增量与结束事件等。
2. 请求场景分类管理
在实际任务执行过程中,Gateway接收到的模型请求并非全部来自主智能体循环,还可能包括上下文压缩、子Agent调用、会话摘要、心跳检测等不同场景。不同场景的请求轨迹训练价值存在明显差异:主智能体循环与子Agent调用通常包含完整的推理决策与执行过程,训练价值较高;而会话摘要、心跳检测等请求并非训练数据的核心内容,甚至可能干扰训练效果。
因此,训练框架需要具备请求场景识别与轨迹分类能力,实现差异化处理。对于部分支持自定义请求URL的智能体框架,可以通过请求入口直接识别场景类型;对于Claude Code、Codex CLI等智能体,则需要结合API路径、请求头、模型标识与请求内容特征进行场景分类,比如Codex的普通模型调用与上下文压缩分别使用/responses与/responses/compact路径,部分内部请求会携带特定的请求头标识。
3. 轨迹重建逻辑
主智能体循环通常会向Gateway发送多次请求,为了获取完整的执行轨迹,Gateway需要识别请求之间的延续关系,将整个执行过程整合为一条完整轨迹。理想情况下,每一轮请求携带的消息历史会随着执行过程持续增长,后一轮请求的消息数组始终以前一轮的完整消息数组为前缀,因此可以自然地归并为一条完整轨迹。
但在实际执行中,当上下文长度接近模型窗口上限时,智能体通常会对历史消息进行压缩,此时新请求中将不再保留此前的完整消息序列,原有的前缀关系会被打断,完整的执行轨迹会被拆分为多段。虽然多段轨迹在消息序列上并不连续,但实际上属于同一次智能体执行过程,如果只保留其中一段,将会丢失部分关键信息。因此,框架需要具备同时保存多段轨迹并识别其关联关系的能力。
我们的实现方案是:为每个Session保存历史请求对应的完整轨迹,当新请求到达时,通过前缀匹配判断是否为已有轨迹的延续:如果当前消息序列以前一轮的完整消息序列为前缀,则将新增消息追加到原轨迹;否则,将其保存为一条新轨迹。不过在实际运行中,严格的字符串前缀匹配并不完全适用,部分智能体框架在发送请求前可能会对消息进行脱敏或注入额外内容,导致语义相同的内容在字符层面存在差异,此时需要根据具体场景放宽匹配条件。
4. Token一致性保障
通常所说的轨迹指的是以明文形式存储的消息数组,但对于大模型推理与训练来说,仅保存消息数组是不够的,还需要同步保存对应的Token ID序列。这是因为将Token序列解码为文本后,再通过分词器重新编码,不一定能够还原出原始的Token序列,比如中文场景下,多个单字Token可能会被合并为一个多字Token。
这种差异会带来两个核心问题:一是降低推理阶段的KV缓存复用率,Prefix Cache依赖Token序列进行匹配,一旦重新分词后的Token与原始序列不一致,从首个差异位置开始,原有的KV缓存将无法继续复用,降低推理效率;二是影响训练稳定性与数据对齐,实践中发现,如果训练阶段使用的Token ID与推理生成时的Token ID不一致,可能会影响训练稳定性,此外训练过程中可能用到的推理生成的log probability、路由专家等数据都与具体的Token位置绑定,如果仅保存消息数组,训练时根据文本重新分词会导致这些信息发生错位。
因此,在Gateway中,对于每条轨迹,除了保存消息数组外,还需要保存对应的Token ID序列。Gateway需要使用Token ID向大模型服务发送请求,获取生成的Token ID并保存,再将其转换为文本回复给智能体,这要求Gateway具备分词与反分词的能力,实现文本与Token之间的转换。需要注意的是,保存的消息数组与Token ID序列解码后的文本内容可能不完全一致,比如智能体不需要推理内容来执行工具,模型输出中的思考过程可能不会在智能体与Gateway之间传输,但这部分内容可能对训练有价值,因此仍会保留在Token ID序列中。
Runtime Manager的设计与扩展
1. 抽象分层设计
Runtime Manager需要同时适配两个维度的差异:一是不同智能体框架的差异,比如Codex、Claude Code、Hermes等智能体在配置文件、启动命令、环境变量等方面都存在不同;二是不同运行环境的差异,智能体可能运行在远端沙盒、Docker容器或本地进程中,不同环境的操作接口也存在差异,比如沙盒通常使用E2B SDK进行控制,Docker容器则使用docker命令进行管理。
为了灵活适配这些差异,Runtime Manager将智能体适配与运行环境拆分为两个相互独立的抽象层:
- Agent Harness:用于描述如何准备、启动与解析某一种特定的智能体框架。
- Sandbox Backend:用于描述如何创建与操作某一种特定的运行环境。
两者由Runtime Manager进行组合,通过这种分层设计,新增一种智能体只需要实现对应的Agent Harness,新增一种运行环境只需要实现对应的Sandbox Backend,无需修改另一维度的适配逻辑。以下是Runtime Manager编排一次智能体任务的核心流程:
# 根据配置创建 Agent 适配器和运行环境
harness = create_harness(config.agent)
sandbox = create_sandbox(config.runtime)
try:
# 在指定运行环境中完成 Agent 的初始化与配置
harness.setup(context, sandbox)
# 启动 Agent,并等待任务执行完成
result = harness.run(context, sandbox)
return result
finally:
# 无论任务成功、失败还是被取消,都执行资源清理
harness.cleanup(context, sandbox)
sandbox.destroy(context)
2. 可扩展的Hook机制
在实际运行中,不同的任务场景通常需要插入不同的处理逻辑,比如任务完成后收集智能体框架日志用于问题分析、在环境中执行脚本进行评估打分、或者进行指标统计等。这些流程往往与业务强绑定,因此我们设计了一套可扩展的Hook机制,在智能体的生命周期中提供标准化的扩展点,将各项附加能力拆分为独立、可插拔的Hook组件。
用户可以根据具体场景选择、组合与实现不同的Hook,多个Hook会按照配置顺序依次执行,从而在不修改Runtime Manager核心逻辑的前提下实现自定义扩展流程。简化的核心逻辑如下:
# 根据配置创建 Agent、运行环境和 Hook
harness = create_harness(config.agent)
sandbox = create_sandbox(config.runtime)
hooks = create_hooks(config.hooks)
try:
# 准备 Agent 运行环境
harness.setup(context, sandbox)
# 在 Agent 启动前执行扩展逻辑
hooks.before_run(context, sandbox)
# 启动 Agent,并等待任务执行完成
result = harness.run(context, sandbox)
# 在 Agent 完成后执行产物收集、评估、上传等后处理逻辑
hooks.after_run(context, sandbox, result)
return result
except Exception as error:
# 执行异常处理 Hook,例如收集错误日志和上报运行状态
hooks.on_error(context, sandbox, error)
raise
finally:
# 无论任务成功、失败还是被取消,都执行资源清理
harness.cleanup(context, sandbox)
sandbox.destroy(context)
Controller模块的核心功能
Controller模块属于纯粹的逻辑协调组件,主要负责接收任务、协调执行顺序等,完成一次完整的Rollout流程,核心功能可以分为三个部分:
- 任务编排:为每次任务执行请求创建独立的上下文,比如创建Session、沙盒环境等,并根据资源情况进行调度,保证不同任务之间相互隔离。
- 流程协调:依次协调Session创建、环境准备、智能体启动、轨迹回收与样本生成,并通过Session ID关联智能体实例与对应的Session。
- 异常处理与结果汇总:当任务出现异常时终止后续流程并释放资源;任务完成后汇总执行结果、轨迹数据与奖励计算逻辑,生成最终的训练样本。
系统的扩展性优化
1. Gateway模块的扩展性
正如前文所述,Gateway并非简单的模型请求转发服务,每次请求到达后还需要完成轨迹匹配、分词、反分词与轨迹数据整理等工作,在多模态场景下还会涉及图片处理与数据转换,这些操作大多属于CPU密集型任务。当大量智能体同时连接到单个Gateway进程时,相关计算会集中在同一个进程中,造成CPU瓶颈,限制整个系统的吞吐能力。
因此,我们采用多进程扩展的方式优化Gateway的性能:将不同的Session分配给不同的Gateway进程实例,每个实例独立完成请求处理与轨迹管理,从而分散CPU压力;同一个Session始终由同一个Gateway进程管理,保证轨迹状态的一致性。
2. Runtime Manager的扩展性
在实践中,Runtime Manager通过E2B SDK与沙盒进行通信,完成沙盒环境的创建、配置与销毁。每个智能体任务可能会产生多次沙盒调用,在高并发场景下,单个进程的E2B SDK需要同时维护大量的HTTP请求与连接,当并发规模超过1000时,容易出现请求超时、连接不稳定等问题,导致智能体任务失败。
我们的解决方案是将Runtime Manager扩展为多进程架构,每个进程使用独立的E2B SDK,将不同的智能体任务分配给不同的Runtime Manager实例,从而分散单个SDK的并发压力,提升智能体任务的稳定性。
总结与参考项目
以上就是我们在Agentic RL训练框架开发过程中的实践与思考。在开发过程中,我们参考了多个优秀的RL框架与智能体框架的设计,在此感谢这些项目的贡献:
- verl:https://github.com/verl-project/verl
- slime:https://github.com/THUDM/slime
- ProRL-Agent-Server:https://github.com/NVIDIA-NeMo/ProRL-Agent-Server
- openclaw:https://github.com/openclaw/openclaw
- hermes-agent:https://github.com/nousresearch/hermes-agent
- codex:https://github.com/openai/codex
- learn-claude-code:https://github.com/shareAI-lab/learn-claude-code


