tool_registry.py — verl/tools/utils/tool_registry.py¶
文件路径¶
verl/tools/utils/tool_registry.py
文件概述¶
tool_registry.py 是工具系统的注册中心,负责从配置文件中读取工具定义,动态加载对应的工具类,并创建工具实例。它支持两种工具类型:Native(原生工具)和 MCP(通过 MCP 协议调用的远程工具)。
核心作用:在 verl 训练启动时,这个模块读取 YAML 配置文件,自动实例化所有工具,供 rollout 过程使用。
关键代码讲解¶
1. 工具类型枚举¶
两种工具类型:
- NATIVE:原生工具,代码直接定义在 verl 中(如 Gsm8kTool、SearchTool)。
- MCP:通过 MCP 协议连接的远程工具服务。
2. 动态加载工具类¶
def get_tool_class(cls_name):
module_name, class_name = cls_name.rsplit(".", 1)
if module_name not in sys.modules:
spec = importlib.util.find_spec(module_name)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
else:
module = sys.modules[module_name]
tool_cls = getattr(module, class_name)
return tool_cls
通过完全限定类名(如 "verl.tools.gsm8k_tool.Gsm8kTool")动态导入并返回类对象。使用 importlib 实现运行时加载,避免硬编码依赖。
3. 初始化 MCP 工具¶
async def initialize_mcp_tool(tool_cls, tool_config) -> list:
from verl.tools.utils.mcp_clients.McpClientManager import ClientManager
tool_list = []
mcp_servers_config_path = tool_config.mcp.mcp_servers_config_path
tool_selected_list = tool_config.mcp.tool_selected_list if "tool_selected_list" in tool_config.mcp else None
await ClientManager.initialize(mcp_servers_config_path, tool_config.config.rate_limit)
# 重试机制:等待 MCP 客户端就绪
max_retries = 10
retry_interval = 2
for i in range(max_retries):
tool_schemas = await ClientManager.fetch_tool_schemas(tool_selected_list)
if tool_schemas:
break
if i < max_retries - 1:
await asyncio.sleep(retry_interval)
else:
raise RuntimeError("Failed to initialize MCP tools after maximum retries")
for tool_schema_dict in tool_schemas:
tool_schema = OpenAIFunctionToolSchema.model_validate(tool_schema_dict)
tool = tool_cls(config=OmegaConf.to_container(tool_config.config, resolve=True), tool_schema=tool_schema)
tool_list.append(tool)
return tool_list
MCP 工具的初始化过程:
1. 初始化 ClientManager(建立与 MCP 服务的连接)。
2. 从 MCP 服务获取可用的工具 schema(带重试机制)。
3. 为每个工具 schema 创建一个工具实例。
4. 从配置文件初始化所有工具(核心入口)¶
def initialize_tools_from_config(tools_config_file):
tools_config = OmegaConf.load(tools_config_file)
tool_list = []
# 懒初始化事件循环(仅 MCP 工具需要)
tmp_event_loop = None
thread = None
def get_mcp_event_loop():
nonlocal tmp_event_loop, thread
if tmp_event_loop is None:
tmp_event_loop = asyncio.new_event_loop()
thread = threading.Thread(target=tmp_event_loop.run_forever, daemon=True)
thread.start()
return tmp_event_loop
try:
for tool_config in tools_config.tools:
cls_name = tool_config.class_name
tool_type = ToolType(tool_config.config.type)
tool_cls = get_tool_class(cls_name)
match tool_type:
case ToolType.NATIVE:
tool_schema_dict = OmegaConf.to_container(tool_config.tool_schema, resolve=True)
tool_schema = OpenAIFunctionToolSchema.model_validate(tool_schema_dict)
tool = tool_cls(config=..., tool_schema=tool_schema)
tool_list.append(tool)
case ToolType.MCP:
mcp_tools = run_coroutine(initialize_mcp_tool(tool_cls, tool_config))
tool_list.extend(mcp_tools)
finally:
# 清理事件循环
if tmp_event_loop is not None:
tmp_event_loop.call_soon_threadsafe(tmp_event_loop.stop)
if thread is not None and thread.is_alive():
thread.join(timeout=5.0)
tmp_event_loop.close()
return tool_list
这是整个工具系统的入口函数:
1. 加载 YAML 配置文件。
2. 遍历配置中的每个工具定义。
3. 根据类型分别处理 Native 和 MCP 工具。
4. MCP 工具需要异步初始化,所以创建了一个临时的事件循环。
5. 使用 finally 确保事件循环被正确清理。
配置文件示例¶
tools:
- class_name: verl.tools.gsm8k_tool.Gsm8kTool
config:
type: native
tool_schema:
type: function
function:
name: calc_gsm8k_reward
description: "A tool for calculating the reward of gsm8k"
parameters:
type: object
properties:
answer:
type: string
description: "The answer to the question"
required: ["answer"]
核心类/函数列表¶
| 类/函数 | 作用 |
|---|---|
ToolType |
工具类型枚举(Native / MCP) |
get_tool_class |
动态加载工具类 |
initialize_mcp_tool |
初始化 MCP 工具 |
initialize_tools_from_config |
从配置文件初始化所有工具(核心入口) |
与其他模块的关系¶
- 加载所有工具类:通过动态导入加载
Gsm8kTool、SearchTool、MCPBaseTool等。 - 使用
schemas.py中的OpenAIFunctionToolSchema进行 schema 验证。 - 使用
ClientManager(mcp_clients/McpClientManager.py)初始化 MCP 工具。 - 使用 OmegaConf 解析 YAML 配置文件。
- 被 rollout 系统调用:训练开始时调用
initialize_tools_from_config加载所有工具。
小结¶
tool_registry.py 是工具系统的启动入口和配置中心。它实现了工具的声明式配置——用户只需编写 YAML 文件就能灵活组合工具,无需修改代码。这种设计使得添加新工具变得非常简单。