跳转至

tool_parser.py — 定义了工具调用解析器——从 LLM 生成的文本中提取函数调用信息

文件路径: verl/experimental/agent_loop/tool_parser.py

文件概述

定义了工具调用解析器——从 LLM 生成的文本中提取函数调用信息。不同的模型使用不同的工具调用格式(如 Hermes 格式、GPT-OSS 格式),本文件通过注册表模式支持多种格式。

关键代码讲解

1. FunctionCall 数据类

class FunctionCall(BaseModel):
    arguments: str
    """函数参数,JSON 格式字符串。注意:模型可能生成无效 JSON 或幻觉参数。"""
    name: str
    """函数名称"""

2. ToolParser 基类与注册机制

class ToolParser(ABC):
    _registry: dict[str, type["ToolParser"]] = {}  # 类级别的注册表

    def __init__(self, tokenizer):
        self.tokenizer = tokenizer

    @abstractmethod
    async def extract_tool_calls(self, responses_ids: list[int]) -> tuple[str, list[FunctionCall]]:
        """从 token IDs 中提取工具调用。返回 (剩余文本, 函数调用列表)"""
        raise NotImplementedError

    @classmethod
    def get_tool_parser(cls, name: str, tokenizer):
        """根据名称获取解析器实例"""
        if name not in cls._registry:
            raise ValueError(f"Unknown tool parser: {name}")
        return cls._registry[name](tokenizer)

    @classmethod
    def register(cls, name: str):
        """注册装饰器"""
        def decorator(subclass):
            cls._registry[name] = subclass
            return subclass
        return decorator

3. HermesToolParser - Hermes 格式解析器

Hermes 格式使用 <tool_call>...</tool_call> 标签包裹 JSON 格式的工具调用。

@ToolParser.register("hermes")
class HermesToolParser(ToolParser):
    def __init__(self, tokenizer):
        super().__init__(tokenizer)
        self.tool_call_start_token = "<tool_call>"
        self.tool_call_end_token = "</tool_call>"
        self.tool_call_regex = regex.compile(r"<tool_call>(.*?)</tool_call>", regex.DOTALL)

    async def extract_tool_calls(self, responses_ids):
        text = await loop.run_in_executor(None, self.tokenizer.decode, responses_ids)

        if self.tool_call_start_token not in text:
            return text, []

        matches = self.tool_call_regex.findall(text)
        function_calls = []
        for match in matches:
            function_call = json.loads(match)
            name, arguments = function_call["name"], function_call["arguments"]
            function_calls.append(FunctionCall(
                name=name,
                arguments=json.dumps(arguments, ensure_ascii=False)
            ))

        content = self.tool_call_regex.sub("", text)
        return content, function_calls

Hermes 格式示例:

这里是模型的文本回复
<tool_call>
{"name": "search", "arguments": {"query": "verl framework"}}
</tool_call>

4. GptOssToolParser - GPT-OSS 格式解析器

GPT-OSS 使用自定义的标记格式(来源于 OpenAI Harmony 协议):

@ToolParser.register("gpt-oss")
class GptOssToolParser(ToolParser):
    def __init__(self, tokenizer):
        super().__init__(tokenizer)
        # Chain-of-Thought 模式(需要排除,因为 CoT 中可能包含类似工具调用的文本)
        self.cot_pattern = regex.compile(
            r"<\|start\|>assistant<\|channel\|>analysis<\|message\|>.*?<\|end\|>", regex.DOTALL
        )
        # 工具调用模式
        self.tool_call_pattern = regex.compile(
            r"<\|start\|>assistant<\|channel\|>[^<]* to=functions\.([^<]+) "
            r"<\|constrain\|>json<\|message\|>(.*?)<\|call\|>",
            regex.DOTALL,
        )

GPT-OSS 格式示例:

<|start|>assistant<|channel|>default to=functions.search <|constrain|>json<|message|>{"query": "verl"}<|call|>

核心类/函数列表

名称 类型 说明
FunctionCall 数据类 工具调用的参数和名称
ToolParser 抽象基类 工具调用解析器基类(含注册表)
HermesToolParser 类 Hermes 格式解析器
GptOssToolParser 类 GPT-OSS 格式解析器

与其他模块的关系

  • 被 ToolAgentLoop(tool_agent_loop.py)使用,在 LLM 生成后提取工具调用
  • 解析结果 FunctionCall 传递给 _call_tool() 执行实际工具

小结

tool_parser.py 通过注册表模式支持多种工具调用格式的解析。核心思路是:将 LLM 生成的 token IDs 解码为文本,然后用正则表达式提取结构化的函数调用信息。目前支持 Hermes 和 GPT-OSS 两种格式,新格式只需继承 ToolParser 并通过 @ToolParser.register("format_name") 注册即可。