尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

【强化学习框架】Uni-Agent 深度技术分析(2)--- 关键技术

【强化学习框架】Uni-Agent 深度技术分析(2)--- 关键技术
📅 发布时间:2026/6/29 4:45:02

概要

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 verl 扩展点全景

verl 的AgentLoopBase通过__init__注入 6 个依赖、通过rollout.*和async_training.*开放 4 个配置命名空间、通过verl.utils和verl.tools提供 4 个工具库、通过experimental.agent_loop暴露 3 个管线入口、通过extra_fields预留 2 个数据契约通道。加上 3 个继承扩展点,总共6 类 22 个扩展点。下面我们从"verl 提供了什么"和"Uni-Agent 怎么用"两个角度逐一梳理,并用统一的编号保证两边对应关系清晰。

编号类别verl 提供Uni-Agent 使用方式Uni 使用?
E1继承AgentLoopBaseABC / run() 抽象接口UniAgentLoop(AgentLoopBase),实现run()/ 实现完整 Agent 交互栈✅
E2继承AgentLoopOutputBaseModel / 9 固定字段 + as_dict()convert_to_agent_output()填充全部 9 字段✅
E3继承_agent_loop_registry+register()装饰器不走装饰器,通过agent_loop_config_pathYAML 外部注入✅
E4注入self.config(Hydra DictConfig)读取 verl 训练超参 +agent_loop_config_path✅
E5注入self.tokenizer(HF AutoTokenizer)封装到AgentChatModel,用于 tokenize/decode/boundary 探测✅
E6注入self.server_manager(LLMServerClient)封装为AgentChatModel.client,对 interaction 层隐藏 verl 细节✅
E7注入self.processor(HF AutoProcessor)不使用——Uni-Agent 当前无多模态支持❌
E8注入self.dataset_cls(RLHFDataset类)不使用——Uni-Agent 通过 DataProto 接收数据❌
E9注入self.data_config(数据集配置)不使用——Uni-Agent 管理自己的数据格式❌
E10配置rollout.agent.*(num_workers, agent_loop_config_path, default_agent_loop)agent_loop_config_path挂载 Agent YAML(自有配置体系);num_workers计算并发✅
E11配置rollout.multi_turn.*(enable, max_parallel_calls, max_user_turns, max_assistant_turns)enable=True是前置条件;max_parallel_calls=1✅
E12配置async_training.*(staleness_threshold, partial_rollout, trigger_parameter_sync_step, require_batches)训练脚本配置,Uni-Agent 不感知,但透传max_global_steps✅
E13配置rollout.mode(async/sync)训练脚本配置(rollout_mode="async")✅
E14工具库verl.tools.schemas(OpenAIFunctionToolCall,OpenAIFunctionToolSchema,ToolResponse)两个 Parser 都输出OpenAIFunctionToolCall,保证 Tool 层解耦✅
E15工具库apply_chat_template()(verl.utils.chat_template)rollout_cache初始化和增量追加时调用✅
E16工具库normalize_token_ids()(verl.utils.tokenizer)所有 tokenize 结果标准化处理✅
E17工具库simple_timer()(verl.utils.profiler)query 耗时和 tool 耗时跟踪
E18管线AgentLoopManager训练和推理都用同一套调度器(generate_sequences())✅
E19管线DataProto(批次协议)通过non_tensor_batch["raw_prompt"]和["tools_kwargs"]传递 per-sample 数据✅
E20管线main_ppo/fully_async_main(训练入口)通过 Hydra 配置注入,不修改入口代码✅
E21数据契约AgentLoopOutput.extra_fields: dict注入traj_masked+traj_exit_reason+max_global_steps✅
E22数据契约TokenOutput.extra_fields: dict透传max_global_steps从 token 级到 output 级✅

按照"最小侵入"的组织原则,Uni-Agent 对不同类别的扩展点采用了不同的改造深度。从上表可以看出:verl 的 22 个扩展点中,Uni-Agent 使用了 19 个,未使用的 3 个(E7 processor、E8 dataset_cls、E9 data_config)都与多模态和 verl 内置数据集管理相关——这是 Uni-Agent 当前尚未覆盖的领域。

1.2 关键扩展点详解

22 个扩展点中,下面 几个是最核心的——它们决定了 verl 和 Uni-Agent 之间"谁做什么"的分工形态,也是理解 Uni-Agent 架构最关键的切入点。

E1: AgentLoopBase——继承契约

这是 verl 与 Uni-Agent 之间唯一的代码级耦合点。verl 通过AgentLoopBase定义了 Agent 的抽象契约:

class AgentLoopBase(ABC): def __init__(self, trainer_config, server_manager, tokenizer, processor, dataset_cls, data_config, **kwargs): self.config = trainer_config.config # 完整 verl Hydra 配置 self.server_manager = server_manager # LLMServerClient self.tokenizer = tokenizer # HF AutoTokenizer @abstractmethod async def run(self, sampling_params, **kwargs) -> AgentLoopOutput: ...

契约的含义:verl 对 Agent 的认知只有一句话——"给它 sampling_params 和 kwargs,它还你一个 AgentLoopOutput"。Agent 内部发生了什么(ReAct?Plan-Execute?跑容器?调 API?),verl 完全不关心。

Uni-Agent 的run()将这个一行契约扩展为 8 步完整管线:

UniAgentLoop.run(): 1. _init_config() → YAML + verl config 合并为 config_dict 2. _init_chat_model() → AgentChatModel(client=self.server_manager, tokenizer=...) 3. _init_tools_manager() → ToolsManager(tools + parser) 4. _init_env() → AgentEnv(deployment_config) 5. env.start() → install_tools() 6. AgentInteraction.run() → 多轮 ReAct 交互 7. reward_spec.compute_reward() 8. convert_to_agent_output() → AgentLoopOutput

改造深度:~200 行。这 8 步中的每一步都对应一个独立模块(Model/Tool/Env/Reward),它们之间通过rollout_cache这个共享数据结构耦合。

E3: 注册机制——外部注入 vs 装饰器注册

verl 提供了_agent_loop_registry和register()装饰器,内置了"single_turn"和"tool_agent"两个 Agent。_agent_loop_registry+register()装饰器允许任何 Agent 框架注册自己的实现。

Uni-Agent 刻意不使用register()装饰器,而是通过agent_loop_config_pathYAML 中的_target_: uni_agent.agent_loop.UniAgentLoop,由hydra.utils.instantiate动态创建,并注入 registr。

# verl 预留的扩展口 [agent_loop.py:421-426] agent_loop_config_path = self.rollout_config.agent.agent_loop_config_path if agent_loop_config_path: resolved_path = resolve_config_path(agent_loop_config_path) agent_loop_configs = OmegaConf.load(resolved_path) for agent_loop_config in agent_loop_configs: _agent_loop_registry[agent_loop_config.name] = agent_loop_config

这种方式使得 Uni-Agent 完全不需要被 verl 的register()装饰器绑定——它通过配置文件外部注入,实现了"零代码级耦合"。这也是"最小侵入"原则的典型体现:verl 预留了一个口子,Uni-Agent 通过 YAML 配置钻了进去。这种方式使得 Uni-Agent 可以完全控制自己的初始化流程,而不受 verl 内置 Agent 的初始化契约约束。

E6: server_manager——推理引擎注入与封装

verl 注入的self.server_manager是一个LLMServerClient实例(在 Fully Async 模式下是FullyAsyncLLMServerClient)。Uni-Agent 不是直接使用它,而是做了一次适配封装:

# agent_loop.py:172-177 model_config = { "client": self.server_manager, # verl 注入 → 转交 AgentChatModel "tokenizer": self.tokenizer, # verl 注入 → 转交 AgentChatModel "max_model_len": max_model_len, # verl config → 转交 AgentChatModel "sampling_params": sampling_params, } chat_model = AgentChatModel(**model_config)

封装后,AgentChatModel通过self.client.generate(request_id=..., prompt_ids=..., sampling_params=...)调用推理引擎,完全不依赖 verl 的类型系统。这意味着:

  • AgentChatModel是可单独测试的(mock 一个generate()接口即可)
  • interaction/层对 verl 零依赖——如果换训练框架,只需改agent_loop.py中的透传代码
  • OpenAICompatibleChatModel可以复用同一套AgentInteraction,因为它实现了相同的query()接口
E10-E12: 配置命名空间——verl 的 Agent 配置插槽

verl 在 Hydra 配置体系中为 Agent 预留了三个配置命名空间:

命名空间关键配置项Uni-Agent 消费位置
rollout.agent.*num_workers(worker 数)agent_loop.py 计算 per-worker 并发
agent_loop_config_path(Agent YAML 路径)agent_loop.py 加载 Agent 配置
default_agent_loop(默认 Agent 名)verl 内部使用,匹配 registry
rollout.multi_turn.*enable(多轮开关)前置条件——Uni-Agent 正常运行依赖此值为 True
max_parallel_calls(并行 tool 数)当前固定为 1(与 Uni 硬编码一致)
max_user_turns/max_assistant_turnsUni-Agent 用自己的max_turns字段控制
async_training.*staleness_thresholdfully_async_rollouter.py
partial_rolloutfully_async_rollouter.py
trigger_parameter_sync_stepfully_async_rollouter.py
require_batches控制 training buffer 最小样本数

其中最关键的是agent_loop_config_path——它是 verl 发现 Uni-Agent 的桥梁。训练脚本中设置agent_loop_config_path=${AGENT_CONFIG_PATH},verl 读取该 YAML,将其中定义的 Agent 配置(_target_: uni_agent.agent_loop.UniAgentLoop)注入 registry,然后由hydra.utils.instantiate动态创建实例。

E14: tools.schemas——标准化 Tool Call 格式

verl 定义了OpenAIFunctionToolCall、OpenAIFunctionToolSchema、OpenAIFunctionCallSchema等标准数据结构。Uni-Agent 的两个 Parser 将 model 的非结构化文本输出解析为这些标准结构:

Model 文本输出: "思考...\n<tool_call>\n<function=execute_bash>\n<parameter=command>\nls /\n..." │ XMLToolParser 或 HermesToolParser ▼ OpenAIFunctionToolCall( id="uuid-...", type="function", function=OpenAIFunctionCallSchema( name="execute_bash", arguments={"command": "ls /"} ) )

这种"解析即标准化"的设计带来了一个重要保证:ToolsManager.get_tool_bash_command()接收到的永远是OpenAIFunctionToolCall,无论上游用的是什么 parser。Tool 层与 Model 层的耦合被 verl 的标准数据结构解开了。

E21-E22: extra_fields——双端元数据通道

extra_fields 是 verl 为 Agentic RL 预留的最灵活的扩展通道。它分为两端,分别搭乘不同的数据载体:

Output 端(E21)——搭乘AgentLoopOutput流向 verl trainer:

# Uni-Agent 注入 [agent_loop.py:227-229] extra_fields["traj_masked"] = traj_masked # 该 trajectory 是否被 mask extra_fields["traj_exit_reason"] = traj_exit_reason # 退出原因 # verl 消费 [agent_loop.py:960-971] # 从所有 inputs 收集 extra_fields → np.ndarray → non_tensor_batch

Token 端(E22)——搭乘TokenOutput从 verl server_manager 流向 Uni-Agent:

# verl 填充 [fully_async_rollouter.py:147-149] final_output.extra_fields["global_steps"] = global_steps final_output.extra_fields["min_global_steps"] = min_global_steps final_output.extra_fields["max_global_steps"] = max_global_steps # Uni-Agent 透传 [model.py:129-131] max_global_steps = token_output.extra_fields.get("max_global_steps", None) if max_global_steps is not None: rollout_cache["extra_fields"]["max_global_steps"] = max_global_steps

这两条通道的流向正好相反:E21 是 Agent → Trainer(Agent 告诉 trainer 这个 trajectory 的质量如何),E22 是 Trainer → Agent(trainer 告诉 Agent 当前的训练进度)。它们共同构成了 verl 和 Agent 之间的"元数据对话"。

0x02 Uni-Agent 关键技术

Uni-Agent 的技术栈可以概括为四个层次,每一层都是"Agentic-first"哲学的具体体现——不是在一个现成 RLHF 框架上零散补几个 Agent 接口,而是从第一天就为多轮交互、工具调用、环境管理、RL 训练设计的原生基础设施。把多轮交互、工具调用、环境生命周期、失败恢复、轨迹过滤和 token 级训练数据构建当作第一等公民。

层机制解决的问题
交互层ReAct 五步循环 + Duck Typing Model 抽象一套代码同时服务训练(token 级)和推理(message 级)
环境层沙箱工具 + 沙箱内 reward + 多后端部署工具隔离执行、真实环境验证、代码不变配置切换
数据层rollout_cache 增量追加 + boundary token 探测token 级精确对齐,mask=1(模型生成)/ mask=0(环境返回)天然区分
训练层Sync/Fully Async + Staleness 三层防御GPU 利用率最大化,off-policy 偏差可控

本章从 "Agentic-first" 这条设计哲学出发,逐层展开 Uni-Agent 交互栈的几个核心技术:ReAct 循环、rollout_cache、Duck Typing 抽象层、沙箱工具与奖励计算、多后端部署、以及 Sync/Async 训练模式。

2.1 "Agentic-first"是什么

"Agentic-first"的核心主张可以用一句话概括:同一套AgentInteraction.run()同时服务推理与训练。环境必须被系统管理而不是临时拼接;reward 计算必须发生在环境可访问的时刻;rollout_cache/response_mask/extra_fields必须天然面向 RL 消费。

"Agentic-first"决定了 Uni-Agent 与 OpenHands / SWE-Agent 的本质区别——不是"能不能跑 Agent",而是它把Task → Agent推理 → Trajectory → Reward → RL Update → Better Model这一闭环做成了可复用基础设施。

2.2 完整的 Multi-Turn 交互栈

Uni-Agent 没有使用 VeRL 的ToolAgentLoop,而是用UniAgentLoop 实现了上述闭环。Uni-Agent 是目前唯一将 Agent 交互栈与 verl Agentic RL 训练管线深度集成的开源框架。

class UniAgentLoop(AgentLoopBase): async def run(self, sampling_params, **kwargs): # verl 只提供这个接口框架 # 下面全是 Uni-Agent 自己的逻辑: self.chat_model = self._init_chat_model(...) # Agent 特有 self.tools_manager = self._init_tools_manager(...) # Agent 特有 self.env = self._init_env(...) # Agent 特有 self.interaction = AgentInteraction(...) # Agent 特有 await self.env.start() # 环境管理 interaction_result = await self.interaction.run() # 交互循环 reward_score, info = await self.reward_spec.compute_reward( interaction_result=interaction_result, ) # 奖励计算 output = await self.convert_to_agent_output(...) # 转为 verl 格式
2.2.1 UniAgentLoop vs ToolAgentLoop

为什么不使用verl 内置的ToolAgentLoop,而是新写一个UniAgentLoop ?

ToolAgentLoop 是为轻量本地 tool 调用设计的——在 Python 进程内调用FunctionTool(本地函数),每次 tool call 独立无状态,是一个通用的 ReAct 状态机实现。

而真实 Agent 任务需要的是持久化远程沙箱 + 复杂环境管理——文件系统、进程状态跨步保持、bash 命令执行。这不是"多几个参数"的差异,而是架构假设的根本不同。

UniAgentLoop 在 ToolAgentLoop 基础上增加了 sandbox 环境管理、bash-based tool 隔离执行、双 tool parser 和双 model 后端——这些是 verl 的ToolAgentLoop完全不提供的。

维度verl ToolAgentLoopUniAgentLoop
Tool 执行Python 进程内调用 FunctionTool远程沙箱中执行 bash 命令
环境无状态——每次 tool call 独立有状态持久沙箱——文件系统、进程状态跨步保持
适用任务轻量 tool(计算器、API 查询)重量级 agent 任务(SWE、代码修复、搜索)
状态机PENDING→GENERATING→PROCESSING_TOOLS 循环更灵活的 step 循环 + 多种退出条件
Reward无内置 reward(依赖 verl RewardManager)内置 reward spec,在沙箱内运行测试,实现了两层 Reward 系统
并发控制无asyncio.Semaphore 控制全局并发

相关新闻

  • 前端MD5实战指南:从原理到应用与安全实践
  • 3步彻底告别Edge:Windows系统浏览器清理终极指南
  • 瑞萨RA8M2以太网流量控制:水印与暂停功能配置详解

最新新闻

  • FakeLocation位置模拟终极指南:如何在Android设备上实现精准定位伪装?
  • 信息学奥赛经典题解:小球下落(drop)的二叉树模拟与优化
  • RA8T2 ADC16H自校准与自诊断功能详解与实战配置
  • 3分钟解锁QQ音乐加密文件:qmcdump无损转换工具完全指南
  • VisionMaster 实战解析:线线测量在精密尺寸检测中的应用
  • UE4SS终极指南:5步打造完美虚幻引擎游戏Mod环境

日新闻

  • ENVI5.3.1实战:基于Landsat 8影像的区域无缝镶嵌与精准裁剪
  • 3步完成HS2-HF Patch安装:新手快速打造完美HoneySelect2体验
  • 微信好友检测终极指南:3分钟发现谁已悄悄删除你

周新闻

  • Windows字体自定义终极方案:No!! MeiryoUI完全指南
  • Deepin Boot Maker:告别命令行,3分钟制作Linux启动盘的智能解决方案
  • Plain Craft Launcher 2:重新定义你的Minecraft游戏体验

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号