首页 > 基础资料 博客日记
【Agentic RL / 强化学习框架】Uni-Agent 深度技术分析(1)--- 总体
2026-06-09 20:30:01基础资料围观6次
【Agentic RL / 强化学习框架】Uni-Agent 深度技术分析(1)--- 总体
- 【Agentic RL / 强化学习框架】Uni-Agent 深度技术分析(1)--- 总体
0x00 概要
本系列目的是通过解析两个强化学习框架(Uni-Agent,Miles)来梳理Agentic RL。
Uni-Agent 是 verl 社区推出的一个面向 Agent RL 的训练/推理一体框架,以同一套交互栈同时支撑大规模 Agent 推理和 RL 训练。它在 verl(字节跳动开源的 LLM RL 训练框架)之上构建 Agent 抽象层(Model/Tool/Env 三层),将 sandbox 执行委托给 SWE-ReX。
项目的核心主张可以概括为 "推理 = 训练,同一入口"——把"Agent 如何与环境交互并产出 token-level 训练数据"封装成一套可复用、可扩展、可训练的工程系统。无论你是跑 1000 个并行的 SWE-Bench 推理任务,还是进行 GRPO/GSPO 强化学习训练,底层走的是同一套 AgentInteraction 交互循环。这种设计使得从"模型推理验证"到"大规模 RL 训练"的切换成本趋近于零——只需换一个 YAML 配置,不需要重写任何交互逻辑。
注,因为:
-
可能本文参考的verl版本不够新,且 verl 社区本身就在突飞猛进,因此可能某些对verl的认知不够准确
-
本文为“从代码反推设计理念” & 时间仓促
所以本文肯定存在很多错误,还请读者不吝指出存在的问题,谢谢。
0x01 基本功能
1.1 竞品对比与定位
在正式进入架构分析之前,我们先回答一个关键问题:Uni-Agent 在现有的 Agent 框架生态中处于什么位置?
1.1.1 三者定位
Agent 抽象层次
高 ▲
│ ┌──────────────┐
│ │ OpenHands │ 完整 Agent 应用
│ └──────────────┘
│
│
│ ┌──────────────┐ ┌──────────────────────────────┐
│ │ SWE-Agent │ │ Uni-Agent │
│ │ (学术 Agent) │ │ 推理+训练一体化框架 │
│ └──────────────┘ │ 统一交互栈,大规模并发, │
│ │ RL 训练原生支持 │
│ └──────────────┬───────────────┘
│ │
│ ┌──────────────▼──────────────┐
│ │ verl (训练引擎) │
│ │ AgentLoopBase/Manager │
│ │ FullyAsyncRollouter │
│ └─────────────────────────────┘
└──────────────────────────────────────────────────────► 训练能力
从上图可以看出,OpenHands 定位在"完整产品"层,关注的是开箱即用的 Agent 体验;SWE-Agent 定位在"学术研究"层,侧重单任务的推理表现。而 Uni-Agent 选择了第三条路:向下集成 verl 的训练能力,向上提供可编程的 Agent 抽象。这一定位使得它成为目前唯一将 Agent 交互栈与 RL 训练引擎深度集成的开源框架。这一点也在其 README 中可以看到。
# Uni-Agent: Build, Run, and Train Agents at Scale
Uni-Agent is a unified framework for general agents at scale.
- **All-in-one stack:** one framework for building, running, and training agents.
- **Unified agent interface:** unified abstractions for diverse and complex real-world agent scenarios.
The long-term vision is to build the backend infrastructure for next-generation agents across both inference and training, enabling them to perceive, act, and explore complex real-world tasks.
1.1.2 七维度对比表
| 维度 | Uni-Agent | OpenHands | SWE-Agent |
|---|---|---|---|
| 核心定位 | Agent 推理+训练一体化 | 全栈 AI 软件工程师产品 | 学术研究 Agent |
| 训练能力 | 原生 GRPO/GSPO RL,Fully Async + Partial Rollout | 无 | 无 |
| 交互模式 | ReAct Loop + Tool-as-Bash + 双 parser | Agent-Controller 事件驱动 | ReAct Loop + 自定义 DSL |
| 工具机制 | 6 种内置 tool,脚本上传到容器 | 丰富 tool 生态 | 3 种 tool |
| 环境管理 | 4 种部署 discriminated union + SWE-ReX | Docker 为主 | Docker 容器 |
| 并发规模 | 1000+ 并行,Ray + asyncio | 单实例为主 | 单实例 |
| 社区生态 | 新生项目(2026),tool 生态早期 | 成熟社区 | 学术社区 |
| 底层依赖 | verl(训练) + SWE-ReX(sandbox) | 自研 | Docker |
1.2 Uni-Agent 架构全景
因为 Uni-Agent 深度依赖 verl,下文的一些架构图会连带着 verl 一起展示,以完整呈现二者的协作关系。
1.2.1 全景图

从全景图中可以清晰地看到 verl 和 Uni-Agent 的分工:
- verl 负责"怎么训练"(调度、算法、参数同步)。
- Uni-Agent 负责"训练什么"(交互逻辑、环境管理、奖励计算)。
二者的分界线是 AgentLoopBase.run() 这一个方法签名——verl 通过 hydra.utils.instantiate 创建 UniAgentLoop 实例,调用 run() 获取 AgentLoopOutput,然后将其喂入 PPO/GRPO 训练管线。
1.2.2 代码组织
Uni-Agent VeRL
───────────────────────────────── ───────────────────────────────────────
uni_agent/ verl/verl-main/verl/
├── agent_loop.py ├── experimental/agent_loop/
│ └─ UniAgentLoop(AgentLoopBase) │ ├── agent_loop.py
├── interaction/ │ │ ├── AgentLoopBase (ABC)
│ ├── interaction.py │ │ ├── AgentLoopOutput (BaseModel)
│ │ └─ AgentInteraction │ │ ├── AgentLoopWorker
│ ├── model.py │ │ ├── AgentLoopManager
│ │ ├─ AgentChatModel │ │ └── _agent_loop_registry
│ │ └─ OpenAICompatibleChatModel│ │ ├── tool_agent_loop.py
│ ├── env.py │ │ │ └─ ToolAgentLoop (verl内置)
│ │ └─ AgentEnv │ │ ├── tool_parser.py
│ ├── tools_manager.py │ │ │ └─ ToolParser, FunctionCall
│ │ └─ ToolsManager │ │ └── utils.py
│ └── tool_parser.py │ ├── fully_async_policy/
│ ├─ XMLToolParser │ │ ├── fully_async_main.py
│ └─ HermesToolParser │ │ ├── fully_async_rollouter.py
├── deployment/ │ │ └── fully_async_trainer.py
│ ├── config.py │ ├── tools/
│ │ └─ DeployConfig (union) │ │ ├── schemas.py
│ ├── local/, host/, modal/, │ │ │ ├── OpenAIFunctionToolCall
│ │ vefaas/ │ │ │ ├── ToolResponse
│ └── remote_runtime.py │ │ │ └── ...
├── tools/ │ │ └── tool_registry.py
│ ├── base.py │ └── utils/
│ │ └─ AbstractTool │ ├── chat_template.py
│ ├── registry.py │ │ ├── apply_chat_template()
│ ├── execute_bash/ │ │ └── initialize_system_prompt()
│ ├── str_replace_editor/ │ ├── tokenizer.py
│ ├── finish/, submit/ │ │ └── normalize_token_ids()
│ ├── search/, search_arxiv/ │ └── profiler.py
└── reward/ │ └── simple_timer()
├── base.py └── workers/
│ └─ AbstractRewardSpec └── rollout/
├── registry.py └── replica.py
├── swe_bench.py └── TokenOutput
├── swe_rebench.py
├── r2e_gym.py
└── search.py
1.2.3 DeployConfig 四后端
deployment/config.py 使用 Pydantic v2 discriminated union 实现编译时类型安全 + 运行时自动反序列化:
| 后端 | 类型标记 | 底层 Runtime | 适用场景 |
|---|---|---|---|
| Local | type: "local" |
Apptainer/Docker/Podman 容器 | 本地开发、单机训练 |
| Host | type: "host" |
主机直接执行(无容器) | 搜索等轻量任务 |
| Modal | type: "modal" |
Modal serverless sandbox | 云端弹性扩缩容 |
| veFaaS | type: "vefaas" |
字节跳动内部 FaaS | 字节内部大规模部署 |
值得注意的是,RemoteRuntime(deployment/remote_runtime.py)是独立于四种后端之外的 HTTP 客户端层,所有后端都通过它与 SWE-ReX server 通信。它不在 DeployConfig 的 discriminated union 内,而是各后端的共享基础设施——这种设计意味着添加新后端时,只需新增一个 Config 类和一个 Deployment 类,无需修改 RemoteRuntime。
0x02 Agentic RL 的难点
在深入 Uni-Agent 的设计之前,我们需要先理解它要解决什么问题。Agentic RL 与传统 RLHF 的差异不是量变,而是质变。
2.1 Agentic RL vs 传统 RLHF
传统 RLHF 训练的是一问一答的对话模型:给定一个 prompt,模型生成一段 response,奖励模型打分。这是一个"单轮、短文本、无环境交互"的场景。
π_θ(y | x) → reward_model(x, y) → PPO update
单轮文本生成 单标量奖励 一次参数更新
Agentic RL 则完全不同:它训练的是一个"与环境持续交互"的 Agent 模型。模型在真实环境中(代码仓库、终端、浏览器)通过多步交互完成复杂任务,训练目标是让模型学会更好的决策策略——不仅包括"说什么",更包括"何时采取什么行动"。
π_θ(a_t | h_t) → env.step(a_t) → o_{t+1} → ... → terminal reward
多轮决策 多步环境交互 稀疏、延迟奖励
两者的本质区别在于:Agentic RL 的 trajectory 是多轮交互序列,包含交替的 model 输出和 environment observation token;奖励信号是稀疏且延迟的(任务完成才给分),而非每步都有即时 reward;token 序列需要精确区分 model 生成段(参与 loss)和 tool 响应段(不参与 loss)。
2.2 Agentic RL 六大核心需求
从上述差异出发,可以归纳出 Agentic RL 框架必须满足的六项核心需求:
| # | 需求 | 说明 | Uni-Agent 实现 |
|---|---|---|---|
| 1 | 多轮交互 | Agent 与环境进行多步 ReAct 循环;模型需要多次推理,每次根据环境反馈调整行动 | AgentInteraction.run() |
| 2 | Token 级对齐/归因 | prompt/response/tool observation 的 token mask 精确对齐;RL 训练需要知道每个 token 的 log probability 的梯度归属 | rollout_cache["response_mask"] |
| 3 | 异步大规模并发 | 训练需要大量轨迹数据;上千个 agent 同时与环境交互 | asyncio.Semaphore + Ray worker pool |
| 4 | 环境多样性 | 支持不同 sandbox 后端;每个 Agent 需要独立的 sandbox,执行命令并获取结果 | 4 种 DeployConfig discriminated union |
| 5 | 异构奖励信号 | 不同 benchmark 的评估逻辑;不同任务有不同的评估方式 | 4 种 AbstractRewardSpec 实现 |
| 6 | 训练-推理统一 | 同一套代码跑推理和训练,避免训练/推理 gap | AgentChatModel + OpenAICompatibleChatModel 共享 AgentInteraction |
2.3 Agentic RL 十大难点
这六项需求背后是一系列工程和算法层面的难题。我们将它们归纳为十个主要难点,逐一分析 Uni-Agent 的应对策略。
难点 1: Token 对齐
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ prompt [MASK=0] │ response₁ [MASK=1] │ obs₁ [MASK=0] │ response₂ [MASK=1] │ ... |
└─────────────────────────────────────────────────────────────────────────────────────┘
核心问题:每次追加 tool response 时,应该重新 tokenize 整个对话历史,还是增量 tokenize?如果增量 tokenize,如何保证 token 序列格式与 model 训练时的格式完全一致?
Uni-Agent 的方案是增量 tokenize + message_boundary_tokens 探测,采用的差分对比法来发现 tokenizer 在消息边界插入的特殊 token。
难点 2: Token 流的混合属性
Agent 的 response 不再是纯模型输出,而是模型输出与环境反馈的交织:
Token 序列: [模型思考][模型动作][环境观察][模型思考][模型动作][环境观察]...
梯度归属: [需要] [需要] [不需要] [需要] [需要] [不需要]
这要求框架精确标记哪些 token 参与 RL 梯度计算(模型的选择),哪些只是上下文(环境反馈)。Uni-Agent 通过 response_mask(1=参与/0=不参与)来实现这一区分。
难点 3: 交互时间极长且方差巨大
传统 RLHF 中一个 rollout 约 2 秒(生成 200 tokens),但在 Agentic RL 中,一个 rollout 可能耗时 30 秒到 60 分钟(100 步交互 + 环境执行)。Batch 内样本的执行时间差异可能超过 10 倍,训练被最慢的 Agent 阻塞(长尾效应),GPU 在等待环境执行时处于空闲状态。
难点 4: 训练-推理 Gap
推理时你可能用 OpenAI API 调用模型,但训练时 verl 需要 token IDs + log probs 来计算 PPO loss。如何保证同一套交互代码在两种不同的数据格式下都能工作?
Uni-Agent 的方案是双 Model 后端共享 AgentInteraction。
难点 5: 环境资源的管理与隔离
每个 Agent 需要独立的执行环境。1000 个 Agent = 1000 个容器(内存、CPU、磁盘)。环境可能崩溃、超时、资源耗尽。不同任务可能需要不同的 Docker 镜像。容器的生命周期需要与 rollout 同步管理。Uni-Agent 通过分层错误处理 + mask_abnormal_exit_traj + timeout_budget + 探活机制来应对。
难点 6: 大规模并发稳定性
上千个 sandbox 同时运行会引发资源争抢和端口耗尽。Uni-Agent 的方案是 Semaphore 限流 + Ray worker pool + per-worker 隔离。
难点 7: Off-policy 与 Staleness(参数过期)
为了提高 GPU 利用率,推理和训练需要异步执行(Fully Async 模式下 rollout worker 和 training worker 异步运行)。但这导致 Agent 使用的模型参数可能已过期,Importance sampling ratio 的估计变得不准确,训练稳定性下降。Uni-Agent 借助 verl 的 staleness_threshold 三层防御来应对。
难点 8: Partial Rollout
慢 trajectory 不应阻塞整个 batch。verl 的 partial_rollout=True 允许部分完成的 rollout 先参与训练,Uni-Agent 对此完全透明——FullyAsyncLLMServerClient 在底层处理断点续传。
难点 9: Reward 的计算复杂性
传统 RLHF 的 reward 计算是毫秒级的(调用一次 reward model),但 Agentic RL 的 reward 计算需要在环境中跑测试套件、执行代码、对比结果,耗时为分钟级。Reward 计算本身需要环境支持,且必须在 Agent 交互完成后、环境关闭前完成。不同 benchmark 的评估逻辑完全不同。
难点 10: Credit Assignment 困难
一个 Agent 执行了 100 步,最终 reward 是 0(没解决 bug)。哪一步是关键错误?传统 RL 只有 trajectory-level 的标量 reward,无法精确归因到具体的 step 或 action。Token-level 的梯度无法区分"好的思考"和"差的行动"。这是当前整个 Agentic RL 领域尚未解决的开放问题。
0x03 verl 的能力与系统性不足
理解了 Agentic RL 要解决的难点之后,我们需要审视 Uni-Agent 的底层依赖——verl——能提供什么,不能提供什么。这决定了 Uni-Agent 必须自己构建哪些能力。
3.1 设计范式差异
verl 的定位是通用 RL 训练框架:它擅长分布式训练、GRPO/GSPO、模型并行、异步调度与推理引擎集成,但并不内建 Agent 与环境交互本身。Uni-Agent 则把注意力放在多轮交互、工具调用、环境生命周期、奖励评估时序和 token-level 轨迹构建上。两者的关系可以概括为:verl 提供骨架,Uni-Agent 负责把骨架变成可运行、可训练、可扩展的 Agent 系统。
3.1.1 本质矛盾
verl 的核心假设源自 RLHF 场景,为"单轮对话 RLHF"而生:
| 维度 | RLHF | Agentic RL |
|---|---|---|
| 交互轮数 | 1 轮 | 10-500 轮 |
| Response 时间 | 秒级 | 分钟到小时级 |
| Response 长度 | 几百 tokens | 万~十万 tokens |
| 环境依赖 | 无 | 容器 / 沙箱 / 云 |
| 失败模式 | 只有生成问题 | 环境崩溃、超时、格式错误、资源耗尽 |
| Reward 计算 | 调 reward model | 在环境中执行验证 |
| 数据组成 | 纯模型输出 | 模型输出 + 环境反馈混合 |
可以看到,verl 的设计假设在 Agent 场景下被系统性打破——几乎每一个维度都发生了质变。这不是 verl 的缺陷,而是"单轮场景"和"多轮场景"之间的根本差异。
3.1.2 分工边界
因此,Uni-Agent 与 verl 的分工非常清晰:
- verl = 训练引擎("怎么训练"):分布式调度、GPU 管理、PPO/GRPO 算法、Fully Async 策略
- Uni-Agent = Agent 抽象栈("训练什么"):多轮交互逻辑、Tool 执行、Sandbox 管理、Reward 计算
分界线是 AgentLoopBase.run() + AgentLoopOutput——一个方法签名,一个输出结构。verl 提供 RL 训练引擎,Uni-Agent 提供 Agent 行为层(环境 + 交互 + 工具 + 奖励 + 胶水),两者通过 AgentLoopOutput 协议连接。

3.1.3 详细分工表
| 关注点 | verl 负责 | Uni-Agent 负责 | 边界机制 |
|---|---|---|---|
| Agent 实例创建 | hydra.utils.instantiate |
YAML 中 _target_ |
_agent_loop_registry |
| LLM 推理 | LLMServerClient.generate() → TokenOutput |
封装为 model.query() |
self.server_manager |
| Tokenizer | AutoTokenizer 加载 + 注入 |
tokenize/decode/boundary探测 | self.tokenizer |
| RL 算法 | PPO/GRPO/GSPO loss + optimizer | 不感知 | AgentLoopOutput |
| Fully Async | FullyAsyncRollouter 全模块 |
不感知(训练脚本配置) | verl 内部 |
| Partial Rollout | FullyAsyncLLMServerClient resume |
透传 max_global_steps |
TokenOutput.extra_fields |
| 多轮交互逻辑 | 不提供 (ToolAgentLoop 是另一实现) |
AgentInteraction.run() |
run() 内部 |
| Tool Schema | verl.tools.schemas 数据类 |
6 种 tool + bash 脚本 | 使用 verl 数据类 |
| Tool 执行(沙箱) | 不提供 | ToolsManager + AgentEnv |
纯 Uni |
| Sandbox 管理 | 不提供 | 4 种 DeployConfig |
纯 Uni |
| Token 对齐 | AgentLoopOutput.response_mask |
rollout_cache 增量 + boundary |
AgentLoopOutput |
| Reward 计算 | reward_score 字段消费 |
AbstractRewardSpec 4 种 |
reward_score 字段 |
| Worker 调度 | AgentLoopManager Ray pool |
不感知 | verl 内部 |
| 数据批次 | DataProto 协议 |
填充 raw_prompt + tools_kwargs |
kwargs 透传 |
| Padding | _agent_loop_postprocess() |
不感知 | verl 内部 |
| 异常分类 | 不提供 | 9 种 exit_reason + 5 层错误 | 纯 Uni |
| Dashboard | wandb/tensorboard | dashboard/ Web UI |
各自独立 |
| Tool Parse | verl 内置 ToolParser |
XMLToolParser+HermesToolParser |
Uni 使用自己的 parser |
3.2 verl 为 Agentic RL 做了什么
尽管 verl 的设计范式源自 RLHF,但它确实为 Agentic RL 提供了关键的专用能力。这些能力可以分为四个维度:
| 训练架构 | |
|---|---|
| • Fully Async Pipeline | rollout/train 解耦,最大化 GPU 利用率 |
| • Partial Rollout | 不等最慢轨迹,快速利用已完成数据 |
| • Parameter Sync | 定期同步 + staleness 控制 |
| 算法 / 损失 | |
|---|---|
| • GSPO | 序列级 clip,适应长轨迹 |
| • Token-Mean Loss | 按 token 加权,公平对待不同长度轨迹 |
| • Dynamic Batch Size | 按 token 总量组批,平衡 GPU 负载 |
| • DAPO Reward Manager | overlong buffer + 长度惩罚 |
| • N-Response/Prompt | Group 内多采样 + advantage normalization |
| 推理引擎优化 | |
|---|---|
| • Multi-Turn Mode | 推理引擎感知多轮模式 |
| • Chunked Prefill | 超长 context 分块处理,防 OOM |
| • Free Cache Engine | rollout 后释放显存给训练 |
| • Preemption Tracking | 监控请求抢占,优化并发 |
| • Routed Experts | MoE expert 路由追踪 |
| Staleness 控制 | |
|---|---|
| • staleness_threshold | 数据过旧直接丢弃 |
| • clip_ratio (极小值) | 限制 importance sampling 偏差 |
| • trigger_sync_step | 控制参数传播频率 |
这些功能对应的实体如下表:
| # | 功能 | Uni-Agent 使用方式 |
|---|---|---|
| 1 | AgentLoopBase ABC |
继承,实现 run() |
| 2 | AgentLoopOutput |
填充 9 字段 |
| 3 | AgentLoopMetrics |
填充 metrics |
| 4 | num_turns 字段 |
填充 turn 数 |
| 5 | extra_fields 双端通道 |
注入 traj_masked,exit_reason |
| 6 | multi_turn rollout 配置 |
enable=True |
| 7 | max_parallel_calls |
设为 1 |
| 8 | FullyAsyncRollouter |
直接使用 |
| 9 | FullyAsyncLLMServerClient |
直接使用 |
| 10 | staleness_threshold |
训练脚本配置 |
| 11 | partial_rollout |
训练脚本配置 |
| 12 | trigger_parameter_sync_step |
训练脚本配置 |
| 13 | verl.tools.schemas |
使用 ToolCall 等 |
| 14 | ToolResponse (多模态) |
不直接使用 |
| 15 | apply_chat_template() |
使用(带 tools=) |
| 16 | normalize_token_ids() |
5 处调用 |
| 17 | AgentLoopManager |
训练+推理都使用 |
| 18 | _agent_loop_registry |
通过 config_path 注入 |
3.3 verl 的不足
在充分肯定 verl 提供的能力之后,我们需要诚实地审视它的不足。verl 的"留白"正是 Uni-Agent 存在的理由。
3.3.1 verl 没做什么(= Uni-Agent 自己构建)
几个主要缺失能力举例如下:
| 缺失能力 | 为什么 Agentic RL 需要 | Uni-Agent 如何补充 |
|---|---|---|
| 多轮 ReAct 循环逻辑 | verl 的 ToolAgentLoop 是状态机,Uni 需要更灵活的设计 | AgentInteraction.run() while 循环 |
| Sandbox/容器管理 | verl 不管理执行环境 | AgentEnv + 4 种 DeployConfig |
| Tool 的 bash 执行 | verl tool 是 Python 函数/类,无法在容器内隔离 | Tool-as-Bash-Script + 容器上传 |
| Agent 异常分类 | verl 不知道 bash 超时 vs 格式错误 | 9 种 exit_reason + 5 层错误处理 |
| 双 Model 后端 | verl 只提供 LLMServerClient | AgentChatModel(训练)+ OpenAICompatible(推理) |
| SWE 特定 reward | verl 的 reward 是通用的 | AbstractRewardSpec + swe_bench/r2e_gym |
| 实时 Dashboard | verl 只有 wandb/tensorboard | dashboard/ Web UI |
3.3.2 具体不足分析
以下我们对 verl 的十项不足进行逐项深入分析。这些分析的目的不是批评 verl,而是帮助理解 Uni-Agent 的设计动机。
不足 1: 没有 Multi-Turn 交互的一等公民支持
verl 通过 AgentLoopBase.run() 提供了一个空接口——它的态度是"用户自己搞定多轮交互"。multi_turn.enable=True 只是一个开关,告诉引擎"这个 request 会多次追加 tokens",但不提供循环框架。Token 流的 prompt/response 分割完全由用户负责。这意味着每个 Agent RL 项目都要从头实现 multi-turn 循环,没有标准的 "step → observe → append" 模式可复用。
Uni-Agent 的补充方式:AgentInteraction 类 + rollout_cache 增量维护。
不足 2: 不理解"环境"概念
verl 的世界观里只有 Model(Actor/Critic/Ref),没有 Environment 抽象——它不知道需要启动容器、管理资源、安装工具。没有环境生命周期管理(start/run/close),也没有环境并发控制。verl 的 AgentLoopManager 只管"给 worker 派活",不管 worker 上跑了多少容器。1000 个 rollout 同时调用 env.start() 会导致资源爆炸。
Uni-Agent 的补充方式:AgentEnv + Deployment 多后端 + asyncio.Semaphore。
不足 3: Rollout 时间分布假设不成立
verl 的 batch 机制(包括 asyncio.gather 等待全部完成的模式)假设所有 rollout 时间接近。但在 Agent RL 中,某些 bug 简单(3 步 submit,30 秒),某些 bug 复杂(跑满 100 步,30 分钟),batch 被最慢的 rollout 阻塞。虽然 partial_rollout=True 在 async mode 中部分解决了这个问题,但 Sync mode 中无解——必须等全部完成。
更深层的问题是:Agent 的执行时间不可预测(取决于任务难度 + 模型能力),传统 RL 的"固定步数"假设完全不适用。
不足 4: Token Credit Assignment 不够精细
verl 的 response_mask 只有 0/1 两档,能区分模型输出和环境输出,但不能区分 thought 和 action(都是 mask=1),不能提供 per-step reward(只有一个标量 reward_score),不能做时间衰减(所有 mask=1 的 token 权重相同)。GRPO 的 advantage 是 trajectory-level 的标量,所有 mask=1 的 token 共享同一个 advantage,无法告诉模型"你的第 3 步是关键错误"。
verl 目前的 workaround 是通过 loss_agg_mode="token-mean" 让不同长度的轨迹贡献正规化,但本质上还是 trajectory-level reward。
不足 5: 不支持 Action-Level Policy Optimization
verl 的 GRPO/PPO 工作在 token-level(loss = Σ_t [mask[t] * ratio[t] * advantage]),但 Agent 的决策单元不是 token,而是 action(一个 tool call 可能包含 50+ tokens)。Token-level ratio 不等于 action-level ratio;一个 action 中的 token 共享相同的"意图",但 log prob 分别计算;如果模型生成了正确的 function name 但参数有 typo,所有 token 被同样惩罚。
理想的 action-level 方案是将一个 action 中所有 token 的 logprob 求和作为一个整体来计算 ratio,但 verl 目前不支持。
不足 6: 推理引擎不为 Multi-Turn 优化
verl 的推理引擎(vLLM/SGLang)每次 generate() 调用本质上是一个独立请求,每次都传完整序列。100 步交互 = 100 次 prefill。即使有 prefix caching,cache miss 时也需要完整重算,且没有"session"概念——引擎不知道同一个 Agent 的多次 generate 是相关的。
理想的方案是 session-based generation:创建 session → 每步只传增量 → append observation,但 verl 的接口没有显式利用这种模式。
不足 7: 训练数据不均匀
verl 的 DataLoader 假设样本大小大致均匀,但 Agent 轨迹长度差异极大(2K ~ 128K tokens)。GPU 内存由最大的 sample 决定,这浪费了大量 padding。Dynamic batch size 部分缓解,但需要 per-sample padding,极长轨迹可能 OOM,但是,截断则意味着模型永远不会对长轨迹中后期的 token 计算梯度。
不足 8: 无法优雅处理 Rollout 失败
verl 的 AgentLoopManager 期望每个 rollout 都返回 AgentLoopOutput。如果失败,需要用户自行构造"empty output"(如 Uni-Agent 的 _build_empty_agent_output),没有"重试"机制,没有"替代 sample"机制。失败的 rollout 产生 garbage data(全 0 的 token ids + mask),占用了 batch 位置但不产生训练信号,高失败率 = 低训练效率。
不足 9: 参数同步对 Agent Rollout 的干扰
Fully Async 模式下,parameter sync 需要推理引擎加载新参数。如果 sync 恰好发生在 Agent 交互中间——Agent_A 正在第 50 步,推理引擎暂停 10-30 秒加载新参数,Agent_A 的环境一直在等(浪费容器时间),恢复后 Agent_A 继续第 51 步时用的是新参数。
结果:同一个 rollout 中前 50 步用参数 θ₀,后 50 步用参数 θ₁,response_logprobs 是混合的,importance sampling ratio 计算不准确。这是 async Agent RL 的固有困境,verl 对此无解。
不足 10: 缺少 Agent 特有的 Metrics
verl 的 training logger 跟踪的是通用 RL 指标(reward_score mean/std、response_length、KL divergence、loss),但 Agent RL 需要更细粒度的指标:per-task success rate、average number of turns、exit reason distribution、environment setup time vs interaction time、tool call distribution、format error rate 等。Uni-Agent 通过 extra_fields 传递了部分信息,但 verl 的 logger 不会自动展示这些,需要用户在 wandb/tensorboard 中 custom track。
3.4 小结
回顾整个分析,我们可以清晰地看到 verl 的设计范式和 Agent RL 需求之间的根本张力:
verl 的设计范式(RLHF-centric):
- Rollout 是短的、确定性时间的
- 所有 token 都是模型生成的
- 不需要外部环境
- Reward 由 reward model 即时返回
- 失败是少见的
- Batch 内样本大致等长
- 一次 generate 调用 = 完整 response
Agent RL 的实际需求:
- Rollout 时间跨度大(30 秒~1 小时)
- Token 流是模型 + 环境交织的
- 需要管理容器 / 云环境的生命周期
- Reward 需要在环境中执行验证(耗时)
- 失败是常态(环境崩溃、超时、格式错误)
- 轨迹长度差异 >10x
- 需要 100+ 次 generate 调用 per rollout
verl 选择的策略是务实的:提供最小化的扩展接口(AgentLoopBase),让上层框架(如 Uni-Agent)处理所有 Agent 特有的复杂性。这让 verl 保持通用性,让专业框架处理专业问题。
verl vs Uni-Agent 能力矩阵如下:

0x04 Uni-Agent 设计理念与核心功能
4.1 核心设计哲学
在理解了 verl 的边界之后,我们可以深入 Uni-Agent 自身的设计。Uni-Agent 的设计哲学是 "推理 = 训练,同一入口",具体体现在:
- 同一套
AgentInteraction代码驱动纯推理(OpenAI API)和 RL 训练(verl vLLM/SGLang)两种场景 - 同一个
rollout_cache数据结构在两种场景下承载不同的信息密度:训练侧有 token IDs + logprobs + mask,推理侧只有 API messages - 同一套配置体系(YAML → Pydantic)覆盖从单机调试到千卡训练的规模跨度
这个哲学可以进一步拆分为五大设计理念:
理念 1: 推理即训练(Inference = Training)
同一套代码和交互逻辑,既用于大规模推理/验证,也用于 RL 训练的 rollout 阶段。不存在两套系统、两份代码。技术保障:rollout_cache 始终记录 token IDs 和 log probs,推理时忽略、训练时直接用。
理念 2: 三层解耦(Model / Tool / Env)
Agent 的三个核心要素独立定义、通过配置组合。换模型不改工具,换环境不改交互逻辑——这是"可扩展性"的工程保障。三层抽象如下:

理念 3: 配置驱动(Config-driven)
所有实验差异体现在 YAML 配置文件中。同一份代码 + 不同配置 = 不同实验。这消除了"训练脚本"和"推理脚本"的代码重复。
具体配置体系如下:
| 层级 | 来源 | 内容 |
|---|---|---|
| verl Hydra | ppo_megatron_trainer.yaml |
训练超参、模型路径、分布式 GPU |
| Agent YAML | agent_loop_config_path |
tool 列表、deployment、max_turns |
| 运行时注入 | _init_config() + tools_kwargs |
model client/tokenizer、镜像覆写 |
合并流程:verl 注入 config/tokenizer/server_manager → 读取 Agent YAML → tools_kwargs 覆写 per-sample 参数(如不同 sample 使用不同的 Docker 镜像)。
配置融合的数据流如下:

理念 4: 异步并发优先(Async-first)
核心交互循环和环境操作全部基于 asyncio。通过 Semaphore 精确控制并发度,支持 1000+ Agent 稳定运行。
并发模型如下:
_semaphore: asyncio.Semaphore | None = None # 类级别共享
worker_concurrent = max(global_concurrency // num_workers, 1)
- 类级别 Semaphore 实现全局并发控制——同一 JVM/进程内的所有
UniAgentLoop实例共享同一个信号量 - Per-worker 限流 =
global_concurrency / num_workers——确保每个 verl worker 的负载均匀 - verl 的
AgentLoopManager通过 Ray 将 worker 分布到不同节点,Uni-Agent 在 worker 内部再用 Semaphore 做精细限流,形成两层并发控制
理念 5: 最小侵入性
- verl 作为 git submodule,不修改其源码
- 新工具通过注册机制添加
- verl 通过
_target_路径发现 Uni-Agent,零耦合
4.2 和用户的分工
核心设计哲学:用户只需定义"做什么" (tools + reward + data), Uni-Agent框架负责"怎么做" (交互循环 + 训练 + 并发)。
4.2.1 用户不需要做的
| 组件 | 为什么不需要动 |
|---|---|
| AgentInteraction 交互循环 | 通用的 model→tool→env 循环, 任务无关 |
| AgentChatModel / rollout_cache | 自动处理 tokenization、mask、log_probs |
| UniAgentLoop | 自动编排 env.start → interact → reward → output |
| verl 训练基础设施 | 异步训练、参数同步、GRPO/GSPO 算法都是通用的 |
| 并发控制 | Semaphore 自动管理 |
4.2.2 用户需要做的
用户典型自定义工作量
| 场景 | 用户需要做的事 | 工作量 |
|---|---|---|
| 用 SWE-Bench 训练新模型 | 改训练脚本的 MODEL_PATH + 集群参数 | ~10 分钟 |
| 用新 benchmark (如 HumanEval) | 写数据预处理 + 写 Reward | ~半天 |
| 加新工具 (如 web search) | 写 Tool 类 + 可执行脚本 | ~2-4 小时 |
| 用新沙箱平台 | 写 Deployment backend | ~1-2 天 |
| 全新 agent 任务类型 | Tool + Reward + 数据 + Config | ~1-3 天 |
训练脚本关键配置 (train_qwen3p5_dense.sh)
| 配置 | 值 | 作用 |
|---|---|---|
| rollout.mode=async | 异步 rollout | 训练和 rollout 完全解耦 |
| rollout.multi_turn.enable=True | 多轮交互 | 每个 rollout 是完整的 agent 轨迹 |
| rollout.agent.agent_loop_config_path | YAML 配置 | 指定 UniAgentLoop 的配置 |
| partial_rollout=True | 部分 rollout | 不需要等所有样本完成就可开始训练 |
| n_resp_per_prompt=8 | 每个 prompt 8 次采样 | GRPO 需要多个 response 对比 |
| staleness_threshold=1.0 | 异步训练阈值 | 控制 rollout 数据的"过期度" |
0x05 Uni-Agent 对 verl 的改造
Uni-Agent 通过 verl 的 AgentLoopBase 抽象类 + Hydra _target_ 注册机制接入,verl 作为只读 git submodule 保持不变。
- verl = 通用 RL 训练框架(面向单轮 RLHF 设计)
- Uni-Agent = Agent 行为层(面向多轮 Agentic RL 设计)
- 集成方式 = 纯扩展点利用,零代码修改 verl
5.1 核心改造原则
Uni-Agent 对 verl 的改造遵循 "最小侵入,最大复用" 原则:
verl 代码: 零行修改 (以 submodule 方式引入, 不修改源码)
verl 接口使用:
┌─────────────────────────────────────────────────────┐
│ agent_loop.py ←── 唯一的 verl 强依赖点 │
│ (AgentLoopBase, AgentLoopOutput, resolve_config) │
│ │
│ interaction/model.py ←── verl 工具库依赖 │
│ (chat_template, tokenizer, profiler) │
│ │
│ interaction/tool_parser.py ←── verl tools.schemas │
│ interaction/tools_manager.py │
│ interaction/interaction.py │
└─────────────────────────────────────────────────────┘
其余模块 (env, reward, deployment, tools, dashboard): 零 verl 依赖, 纯 Uni-Agent 代码
这种分层的实际意义是:如果未来需要更换 RL 训练框架(如 OpenRLHF),只需要重写 agent_loop.py 中的 UniAgentLoop,interaction/ 层及以下全部可以复用。这是一种务实的"框架解耦保险"。
5.1.1 改造决策
为什么这样设计?
| 设计决策 | 原因 |
|---|---|
| 不修改 verl | verl 是社区项目,保持上游同步能力;uni-agent 可独立升级 |
| 不继承 ToolAgentLoop | verl 的 ToolAgentLoop 假设 tool = 本地 Python 函数;uni-agent 需要远程沙箱,设计基础不同 |
| 直接继承 AgentLoopBase | 最小耦合面—只需实现 run(),其余自由发挥 |
| 独立的 Model 抽象 | verl 直接调用 server_manager.generate(); uni-agent 需要双模式(训练+推理)+rollout cache 管理 |
| 独立的 Tool/Reward 系统 | verl 的 FunctionTool 和 RewardManager 不支持沙箱内执行;uni-agent 完全重建这两层 |
5.1.2 核心改造手段
在此基础上,核心改造手段如下:
- 插件式接入:不修改verl,通过扩展点注册,保持上游同步能力
- 推理即训练:同一套AgentInteraction代码,双模式 Model抽象实现无缝切换
- 三层解耦:Model/Tool/Env独立定义、配置组合,换一个不影响其他
- 配置驱动:所有实验差异体现在YAML中,代码不变
- async-first:全链路异步 +Semaphore 精确限流,支持 1000+ 并发 agent
- 窄接口面:与verl 主要通过AgentLoopBase,AgentLoopOutput连接,另依赖少量verl 工具函数(resolve_config_path、apply_chat_template、normalize_token_ids、OpenAIFunctionTool*schemas)
因此,Uni-Agent 是 verl 的一个外挂 Agent Loop 插件,通过 Hydra + config 注册机制接入,利用 verl 的数据协议和训练引擎,但在 agent 行为层(环境、工具、交互、reward)完全自建。
架构关系图如下:

5.2 工作分类总览
Uni-Agent 的工作分为 5 个核心架构层 + 2 个工程支撑层:
| 分类 | 模块 | 解决的问题 | verl 未提供的能力 |
|---|---|---|---|
| 🔵 环境管理 | deployment/ (Local, Modal, VeFaaS, Host) | Agent 需要持久化、隔离的执行环境 | verl 只有本地 Python 函数调用,无容器/云沙箱 |
| 🔵 环境管理 | interaction/env.py (AgentEnv) | 统一的沙箱操作接口(run_action, read_file, write_file) | verl 无文件系统操作抽象 |
| 🟢 交互循环 | interaction/interaction.py (AgentInteraction) | 多步 model→tool→env 循环 + 超时/错误恢复 | verl 的 ToolAgentLoop 太简单,无超时预算、terminal 死亡检测 |
| 🟢 交互循环 | interaction/model.py (AgentChatModel) | Token 级别 rollout cache + 推理/训练双模式 | verl 只有 server_manager.generate() 的直接调用 |
| 🟢 交互循环 | interaction/model.py (OpenAICompatibleChatModel) | 推理场景复用相同交互代码 | verl 的 agent loop 只支持训练模式 |
| 🟡 工具系统 | tools/ (bash, editor, search, submit, finish) | 真实 agent 工具(在沙箱中执行脚本) | verl 的 FunctionTool 是 Python 函数,不是沙箱脚本 |
| 🟡 工具系统 | interaction/tools_manager.py + tool_parser.py | Tool schema 管理 + 多格式解析(XML, Hermes) | verl 也有 parser,但 uni-agent 重写以适配沙箱执行模式 |
| 🔴 Reward 计算 | reward/ (swe_bench, r2e_gym, swe_rebench, search) | 在沙箱内运行测试得到 reward | verl 的 RewardManager 是外部 reward model 打分,不支持沙箱内 eval |
| 🟣 训练集成 | agent_loop.py (UniAgentLoop) | 将上述所有组件粘合到 verl 的 AgentLoopBase 接口 | 这是 uni-agent 的"胶水层" |
| ⚪ 工程支撑 | async_logging.py, utils.py, dashboard/ |
日志、工具函数、监控面板 | |
| ⚪ 使用配方 | examples/ |
训练/推理脚本、数据预处理、配置模板 |
5.3🔵 环境管理层 - 解决 "在哪里执行"
5.3.1 解决的问题
Agent 需要在持久化、隔离的执行环境中运行命令、安装依赖、编辑文件、保持状态跨步。
即,Agent 需要 cd /repo && pip install && edit file && run tests
→ 需要持久化文件系统 + 进程状态
→ 需要容器隔离(安全 + 可重现)
→ 需要支持本地/云端多种部署方式
5.3.2 verl 为何不能解决
verl 的世界观只有 Model(Actor/Critic/Reference) + 进程内工具。它:
- 没有"环境"概念—不知道需要启动什么容器
- 没有资源管理—不管端口分配、OOM、容器崩溃
- 没有生命周期—不知道何时 start/close 沙箱
- 内置的
ToolAgentLoop支持FunctionTool(无状态函数)和BaseTool(有 create/execute/release 生命周期的状态工具),但所有执行都在 Python 进程内—无法操作远程文件系统或执行 bash 命令
5.3.3 Uni-Agent 的实现
# 统一沙箱抽象
class AgentEnv:
async def start() # 启动沙箱
async def install_tools() # 安装工具脚本到 /usr/local/bin/
async def run_action() # 执行 bash 命令
async def communicate() # 执行命令(支持 timeout、check 级别、error 处理)
async def read_file() # 读取沙箱内文件
async def write_file() # 写入沙箱内文件
async def close() # 销毁沙箱
# 4 种部署后端(Pydantic Discriminated Union)
DeployConfig = Annotated[
VefaasDeploymentConfig | LocalDeploymentConfig |
HostDeploymentConfig | ModalDeploymentConfig,
Field(discriminator="type")
]
具体后端如下:
| 后端 | 适用场景 |
|---|---|
| Host | 本地开发,无隔离 |
| Local | 本地容器 |
| Modal | 云端弹性沙箱 |
| VeFaaS | 企业级 FaaS |
关键文件: uni_agent/deployment/config.py, uni_agent/deployment/{host,local,modal,vefaas}/deployment.py, uni_agent/interaction/env.py
5.4 🟢 交互循环层 - 解决 "如何交互"
5.4.1 解决的问题
- 多步交互循环: model→parse→execute→observe→repeat,带完善的退出条件和错误恢复
- 训练数据增量构建: 每步自动维护 token IDs、log probs、response mask
- 推理训练一体: 同一套交互代码,底层切换 vLLM(训练)或 OpenAI API(推理)
- 训练模式需要 token IDs + log probs + mask
- 推理模式只需要 API 调用 + 结果
- → 两套数据格式,同一套交互逻辑
5.4.2 verl 为何不能解决
- verl 的内置 ToolAgentLoop 有完整的多轮状态机(PENDING→GENERATING→PROCESSING_TOOLS→TERMINATED),但它的工具执行限于进程内 Python 对象(FunctionTool 直接调用 or BaseTool 的 create/execute/release 生命周期)— 无法执行远程 bash 命令、操作沙箱文件系统
- verl 没有超时预算(timeout_budget)递减机制、terminal 死亡检测、format_error 恢复等面向真实环境的鲁棒性处理
- verl 只有 server_manager.generate() 的直接调用 — — — 没有推理模式的 API 抽象
- verl 没有 message_boundary_tokens 机制 — 不知道如何正确拼接多轮对话的 token 序列(verl 使用 apply_chat_template 全量重编码)
- 真实 agent 交互有各种异常 → 命令超时、终端死亡、token 超限、格式错误
5.4.3 Uni-Agent 的实现
交互循环 (AgentInteraction):
loop step(idx):
1. model.query(messages) → response + rollout_cache
2. tools_manager.parse_action() → tool_calls(文本解析 or 结构化)
3. tools_manager.get_tool_bash_command() → bash 命令
4. env.run_action(cmd) → observation
5. model.append_messages_to_rollout_cache() → 更新训练数据
6. 退出条件: → 多种错误处理
- finished: tool 名为 finish/submit
- max_step_limit: step_idx >= max_turns
- token_limit: MaxTokenExceededError
- format_error: FunctionCallFormatError(可恢复,不终止循环)
- syntax_error: ActionIncorrectSyntaxError(可恢复)
- timeout_error: ActionTimeoutError(timeout_budget 递减,耗尽才终止)
- terminal_not_alive: TerminalNotAliveError(不可恢复,终止)
- unknown_error: 未预期异常(终止)
统一接口
AgentChatModel(训练)/ OpenAICompatibleChatModel(推理)用 统一接口:query() + append_messages_to_rollout_cache(),且 rollout_cache 是不透明 dict,交互层不感知内部格式
双模式模型 (AgentChatModel vs OpenAICompatibleChatModel):
| 接口方法 | 训练模式 (AgentChatModel) | 推理模式 (OpenAICompatibleChatModel) |
|---|---|---|
| prepare_rollout_cache() | ||
| query() | vLLM generate → token IDs → decode | OpenAI API → text |
| append_messages_to_rollout_cache() | tokenize + 拼 prompt_ids + mask=0 | 追加到 api_messages |
| 返回值 | (text, rollout_cache, info) | 同上 |
注意: UniAgentLoop 训练路径固定使用 AgentChatModel。OpenAICompatibleChatModel 用于独立推理脚本(如 parallel_infer.py、demo.py),实现交互代码复用。
增量 Tokenize + Boundary 探测:
@cached_property
def message_boundary_tokens(self) -> list[int]:
# 通过差分对比自动提取消息间的分隔 token
# tokenize("历史+新消息") - tokenize("新消息") = 分隔符
关键文件: uni_agent/interaction/interaction.py, uni_agent/interaction/model.py
5.5 🟡 工具系统层 - 解决 "如何动作"
5.5.1 解决的问题
Agent 需要通过工具(bash、文件编辑器、搜索)与环境交互,且:
- 工具以可执行脚本形式安装到沙箱中(不是 Python 函数)
- 不同模型有不同的 tool call 格式需要解析
- 工具 schema 需要自动生成为 OpenAI 格式
5.5.2 verl 为何不能解决
verl 的 FunctionTool = Python 函数调用。而 真实 agent 工具 = 在沙箱中安装+执行脚本
verl 的工具系统支持两种模式:
# 模式1: FunctionTool - 无状态函数调用
class FunctionTool:
async def call(self, args: dict) -> ToolResponse: ...
# 模式2: BaseTool - 有生命周期的状态工具
class BaseTool:
async def create(**kwargs) -> (instance_id, ToolResponse)
async def execute(instance_id, args, agent_data) -> (ToolResponse, reward, dict)
async def release(instance_id): ...
但两者都运行在 Python 进程内,无法满足:
- 在远程容器中执行 bash -c "cd /repo && pytest"
- 安装可执行脚本到 /usr/local/bin/str_replace_editor
- 处理 bash 命令超时、输出截断、terminal 进程死亡
- 工具以 shell 脚本形式存在于沙箱文件系统中
5.5.3 Uni-Agent 的实现
Uni-Agent 关于工具系统层的实现如下图所示。其中:
-
内置工具: execute_bash, str_replace_editor, submit, finish, search, search_arxiv
-
关键文件: uni_agent/tools/base.py, uni_agent/tools/registry.py, uni_agent/interaction/tools_manager.py, uni_agent/interaction/tool_parser.py
解决:AbstractTool + @register_tool
每个 tool 有:
├─ __init__.py (schema 定义 + 注册)
└─ 可执行脚本(安装到沙箱 /usr/local/bin/)
ToolsManager:
├─ parse_action() → 从文本提取 tool call
└─ get_tool_bash_command() → 生成沙箱中执行的命令
# 工具注册
@register_tool("execute_bash")
class ExecuteBashTool(AbstractTool):
@property
def name(self) -> str: return "execute_bash"
def get_tool_schema(self) -> dict:
return build_tool_schema(description="...", arguments_model=ExecuteBashArguments)
def get_install_command(self) -> str | None:
return f"cp {self.local_path} /usr/local/bin/execute_bash && chmod +x ..."
# 工具解析 - 适配不同模型格式
parsers = {
"qwen3_coder": XMLToolParser, # <tool_call>...</tool_call>
"hermes": HermesToolParser, # ```json { "name": ..., "arguments": ... } ```
}
# 结构化 vs 文本解析双路径
if rollout_cache has structured_tool_calls: # OpenAI API 返回
parse_structured_action(tool_calls_data)
else: # vLLM 纯文本输出
parse_action(model_output)
5.5.4 Tool 执行管道
Tool 执行管道的数据流如下:

5.6 🔴 奖励计算层 - 解决 "如何评估"
5.6.1 解决的问题
Agent RL 的 reward 不是简单的模型打分,而是需要 在环境中执行验证(如运行 pytest),且:
- 必须在 agent 修改后的同一个沙箱中运行,这是因为:
- SWE-Bench: 需要运行测试套件,看 FAIL_TO_PASS 是否通过
- Search: 需要对比答案正确性
- 必须在沙箱关闭之前计算
- 不同 benchmark 有完全不同的评估逻辑
5.6.2 verl 为何不能解决
verl 的 RewardManager(如 DAPO)假设 reward_score 已经算好了:
# verl RewardManager 做的事:
# 1. 读取 rm_scores(已存在的分数)
# 2. 做 reward normalization / overlong penalty / group baseline
# 它不知道也不管 reward 是怎么来的
verl 的 reward 系统:
- 无环境访问 — 拿不到沙箱去跑测试
- 计算时序错误 — 在 rollout 结束后的训练阶段才调用,此时沙箱已关闭
- 无注册机制 — 不支持按 benchmark 切换评估逻辑
5.6.3 Uni-Agent 的实现
# agent_loop.py: reward 在 env.close() 之前计算
async with self._semaphore:
await self.env.start()
await self.env.install_tools(...)
interaction_result = await self.interaction.run()
reward_score = await self.reward_spec.compute_reward(...) # ← 此时沙箱还活着
await self.env.close() # ← reward 算完才关闭
# SWE-Bench reward 具体流程
class SWEBenchRewardSpec:
async def compute_reward(self):
# 1. 构造 eval 脚本(activate conda, apply test_patch, run pytest)
# 2. 写入沙箱:env.write_file("/tmp/eval_script.sh", script)
# 3. 在沙箱中执行:env.communicate("bash /tmp/eval_script.sh", timeout=600)
# 4. 解析输出 → reward score:swebench log_parser → FAIL_TO_PASS 通过? → True/False
| Reward 实现 | 评估方式 | 适用 benchmark |
|---|---|---|
| swe_bench | 沙箱内运行 | pytest |
| swe_rebench | 沙箱内运行测试 + 多分支 | SWE-reBench |
| r2e_gym | 沙箱内运行 eval 脚本 | R2E-Gym |
| search | 答案字符串匹配 | ASearcher |
关键文件: uni_agent/reward/base.py, uni_agent/reward/registry.py, uni_agent/reward/swe_bench.py
5.7🟣 训练集成层(胶水层)
训练集成层 将上述 4 层粘合为 verl 可消费的标准 AgentLoopOutput。
5.7.1 verl 为何不能解决
verl 的 AgentLoopBase 只定义了 run() 接口 — 具体如何编排组件、如何控制并发、如何处理异常,全部由实现者自行设计。
5.7.2 Uni-Agent 的实现
class UniAgentLoop(AgentLoopBase):
_semaphore: asyncio.Semaphore # 类级别,per-worker-process 并发控制
async def run(self, sampling_params, **kwargs) -> AgentLoopOutput:
config = self._init_config(sampling_params, **kwargs) # 三层配置合并
self.chat_model = self._init_chat_model(...)
self.tools_manager = self._init_tools_manager(...)
self.env = self._init_env(...)
self.interaction = AgentInteraction(...)
self.reward_spec = load_reward_spec(...)
async with self._semaphore: # 并发限流
try:
await self.env.start()
await self.env.install_tools(...)
interaction_result = await self.interaction.run()
reward = await self.reward_spec.compute_reward(...)
return await self.convert_to_agent_output(interaction_result)
except Exception:
return await self._build_empty_agent_output("agent_loop_failed")
finally:
await self.env.close()
独特贡献:
- 并发控制: Semaphore(global_concurrent // num_workers) per-worker-process 限流(注意:多个 Ray AgentLoopWorker 各自独立,非集群全局)
- 异常降级: 失败时返回 dummy output(mask 全 0),不破坏训练 batch
- 三层配置合并: verl config + agent_config.yaml + 样本级 tools_kwargs
- traj_masked 标记: 让 verl 训练端可选择性忽略异常轨迹
关键文件: uni_agent/agent_loop.py
5.8 ⚪ 工程支撑层
| 组件 | 解决的问题 | verl 不够的原因 |
|---|---|---|
| async_logging.py | 1000 并行 agent 的 per-run 独立日志 | verl 只有全局 logger |
| utils.py (auto_await) | sync/async 方法互操作 | verl 没有此需求 |
| dashboard/ | 实时监控并行运行的状态 / 进度 / 异常 | verl 的 wandb logger 不够细粒度 |
| examples/data_preprocess/ | 将 benchmark 数据转为标准 Parquet | 每个 benchmark 格式不同 |
| examples/agent_train/*.sh | 完整的训练启动配方 | verl 示例只有单轮 RLHF |
5.9 一句话总结
verl 解决"如何高效训练 RL 模型",Uni-Agent 解决“如何让 Agent 在真实环境中完成多步交互并产出训练数据"。二者通过AgentLoopoutput协议连接:verl消费数据,Uni-Agent 生产数据。
架构全景图如下:

在此基础上,数据流满足 RL 需求的关键点如下:
| RL 需求 | Uni-Agent 如何满足 |
|---|---|
| Token-level log probs | AgentChatModel.query() 收集 token_output.log_probs → 存入 rollout_cache["response_logprobs"] |
| Response mask (区分生成 vs 环境) | response_mask: 模型生成部分=1, tool 输出/边界=0 |
| 完整 prompt + response 拼接 | rollout_cache["prompt_ids"] 逐步拼接所有 token |
| Reward signal | RewardSpec.compute_reward() 在交互结束后运行测试得出分数 |
| 并行采样 | asyncio.Semaphore 控制并发, verl 分发多个 prompt |
| 异常轨迹处理 | mask_abnormal_exit_traj + traj_masked 标记 → verl 训练时跳过 |
| Partial rollout | verl fully_async 模式支持不等所有 rollout 完成就开始更新 |
0xFF 参考
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签:
相关文章
最新发布
- 精选 5 款基于 .NET 开源免费、功能强大的 Windows 系统优化工具
- 补充MySQL官网知识--解锁Online VARCHAR字段扩展与Index的关系
- MACD:面向大语言模型的自学习知识多智能体临床诊断(可靠、可解释且可部署的 AI 辅助诊断系统)
- 使用 dotnet-counters 观测升讯威客服系统内存占用情况和数据吞吐性能
- [开源] Meta Assistant / 告别命令行,我为一堆 Python 脚本做了一个 Windows 任务栏的“家”
- 【Agentic RL / 强化学习框架】Uni-Agent 深度技术分析(1)--- 总体
- 不做通用AI助手,先做好一个垂直Agent
- Molio 开源:把知识库、AI 写作、排版和多平台发布串成一条工作流
- imx415 启动HDR场景
- 【Vibe Coding】折腾了一个高考假,我让Codex自动修Issue...

