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 格式示例:
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") 注册即可。