执行摘要
- 一句话:为ToolAgentLoop新增按样本选择工具子集的能力,支持多轮rollout中不同样本使用不同工具。
- 推荐动作:建议精读此PR以理解动态工具选择的设计,但需注意实现与描述的差异。关注
run()方法中的工具过滤逻辑和状态机各处的getattr使用,这是核心变更。同时,可参考讨论中关于未来注册式工具定义的见解。
功能与动机
根据PR body描述,真实世界智能体训练中,不同场景需要不同工具(例如gsm8k计算器、sandbox_fusion代码执行器、geo3k几何工具)。目前批次中所有行共享相同的全局tool_config_path,这限制了灵活性。此PR通过简单的配置映射+数据集级路由键实现异构工具环境,无需自定义子类或新代理循环。
实现拆解
- 入口变更:在
verl/experimental/agent_loop/tool_agent_loop.py的run()方法中,从kwargs提取extra_info.tool_selection,根据工具名称列表过滤self.tools全局工具字典,将选中的工具和模式分别存入agent_data._active_tools和agent_data._active_tool_schemas。
- 状态机适配:修改
_handle_pending_state、_handle_generating_state和_call_tool方法,使用getattr(agent_data, "_active_tools", self.tools)或getattr(agent_data, "_active_tool_schemas", self.tool_schemas)获取按样本选择的工具,替代硬编码的self.tools或self.tool_schemas。
- 配置扩展:在
verl/workers/config/rollout.py的MultiTurnConfig中添加tool_envs: Optional[dict[str, str]] = None字段,并在verl/trainer/config/rollout/rollout.yaml中补充默认值和文档说明。
- 向后兼容:当
tool_selection未提供或为空时,回退到全局工具,行为与现有main分支一致。
关键文件:
verl/experimental/agent_loop/tool_agent_loop.py(模块 代理循环;类别 source;类型 core-logic;符号 run, _handle_pending_state, _handle_generating_state, _call_tool): 核心实现文件,修改了run()方法以支持按样本工具选择,并适配状态机各阶段使用活动工具。
verl/workers/config/rollout.py(模块 配置;类别 config;类型 configuration;符号 MultiTurnConfig): 配置层扩展,在MultiTurnConfig中添加tool_envs字段以支持未来工具环境映射。
verl/trainer/config/rollout/rollout.yaml(模块 配置;类别 config;类型 configuration): 配置文件更新,为tool_envs添加默认值和文档说明。
关键符号:run, _handle_pending_state, _handle_generating_state, _call_tool
关键源码片段
verl/experimental/agent_loop/tool_agent_loop.py
核心实现文件,修改了run()方法以支持按样本工具选择,并适配状态机各阶段使用活动工具。
async def run(self, sampling_params: dict[str, Any], **kwargs) -> AgentLoopOutput:
# ... 其他初始化代码 ...
agent_data = AgentData(
messages=messages,
image_data=images,
video_data=videos,
metrics=metrics,
request_id=request_id,
tools_kwargs=tools_kwargs,
interaction=interaction,
interaction_kwargs=interaction_kwargs,
)
# Per-sample tool selection: filter global tools by extra_info.tool_selection
extra_info = kwargs.get("extra_info", {}) or {}
tool_selection = extra_info.get("tool_selection")
if tool_selection and self.tools:
# 过滤工具字典,只保留 tool_selection 中存在的工具名称
selected = {name: self.tools[name] for name in tool_selection if name in self.tools}
agent_data._active_tools = selected
agent_data._active_tool_schemas = [
t.tool_schema.model_dump(exclude_unset=True, exclude_none=True) for t in selected.values()
]
else:
# 回退到全局工具
agent_data._active_tools = self.tools
agent_data._active_tool_schemas = self.tool_schemas
# State machine loop
state = AgentState.PENDING
while state != AgentState.TERMINATED:
if state == AgentState.PENDING:
state = await self._handle_pending_state(agent_data, sampling_params)
elif state == AgentState.GENERATING:
state = await self._handle_generating_state(agent_data, sampling_params)
elif state == AgentState.PROCESSING_TOOLS:
state = await self._handle_processing_tools_state(agent_data)
elif state == AgentState.INTERACTING:
state = await self._handle_interacting_state(agent_data)
else:
logger.error(f"Invalid state: {state}")
state = AgentState.TERMINATED
# ... 输出处理 ...
评论区精华
主要讨论围绕实现方案与PR描述的差异展开:
- wuxibin89建议:“只需在extra_info中添加tool_selection,保持rollout.yaml不变,从tool_config_path加载所有工具,然后通过tool_selection选择子集。”并提到正在开发基于注册的工具定义。
- pull-ups回应已根据建议修改代码,以适应未来方向。
-
gemini-code-assist[bot]指出显著差异:PR描述提到使用extra_info.tool_env_name和预加载环境缓存,但代码使用extra_info.tool_selection过滤全局工具,且__init__无变更;回退逻辑不匹配描述——如果tool_selection提供但只包含未识别名称,代码会创建空字典而非回退到全局工具。
-
实现方案与PR描述差异 (design): 未明确解决,但PR已根据wuxibin89的早期建议修改。
- 未来工具定义方向 (design): 此PR作为过渡方案,支持简单按样本工具选择。
风险与影响
- 风险:
- 逻辑风险:在
run()方法中,如果tool_selection包含未在self.tools中的名称,selected字典可能为空,导致agent_data._active_tools为空集,后续工具调用可能失败。根据gemini-code-assist[bot]的评论,这未按描述回退到全局工具。
- 兼容性风险:修改了
AgentData对象,添加了_active_tools和_active_tool_schemas属性,如果其他代码依赖AgentData结构,可能引入意外依赖。
- 配置风险:新增
tool_envs配置字段,但实际代码未使用,可能导致配置与实际行为不一致,增加维护复杂度。
- 影响:
- 用户影响:允许用户在数据集级别指定工具子集,提升多场景训练灵活性,无需为不同工具集创建单独配置或批次。
- 系统影响:对ToolAgentLoop核心逻辑有中等影响,状态机各阶段现在支持动态工具选择,但性能开销可忽略(仅字典过滤)。
- 团队影响:为未来基于注册的工具定义铺平道路,但当前实现与PR描述存在差异,可能增加理解成本。
- 风险标记:逻辑不一致风险, 配置未使用, 缺少测试覆盖
关联脉络
- PR #6074 [BREAKING] [env] refactor: deprecate verl/interactions: 同样涉及agent_loop模块的改造,废弃interactions模块,可能影响工具交互流。
- PR #6048 [rollout] chore: single turn agent loop also enable rollout trace as tool loop: 涉及agent_loop和rollout的集成,与此PR在工具循环方面有协同。
- PR #6051 [trainer,cfg,rollout,algo] feat: Multi-Teacher OPD: 涉及多教师和路由键管理,与此PR的按样本路由概念相似。
参与讨论