跳转至

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. 工具类型枚举

class ToolType(Enum):
    NATIVE = "native"
    MCP = "mcp"

两种工具类型: - 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 文件就能灵活组合工具,无需修改代码。这种设计使得添加新工具变得非常简单。