vLLM Tool Calling 完全指南:从自动函数调用到自研工具解析器插件
【免费下载链接】vllmA high-throughput and memory-efficient inference and serving engine for LLMs项目地址: https://gitcode.com/GitHub_Trending/vl/vllm
本文以 vLLM 官方文档 docs/features/tool_calling.md 为主体,系统讲解 vLLM 在 Chat Completion API 中的工具调用(Tool Calling)能力:tool_choice的auto、required、none及命名函数四种模式的实现机制与约束解码行为,覆盖 Llama、Mistral、Hermes、DeepSeek、GLM 等 20 余种模型族的解析器配置方式,并结合vllm/tool_parsers/源码剖析解析器注册机制,最后给出编写自定义 Tool Parser 插件的完整实践步骤与性能基准测试方法。
快速上手:一分钟跑通工具调用
vLLM 支持命名函数调用,并支持auto、required(vllm>=0.8.3起)以及none三种tool_choice取值。以 Meta 的 Llama 3.1 8B 为例,由于该模型的tokenizer_config.json中不包含 vLLM 所需的工具调用模板,需要显式指定 vLLM examples 目录中的llama3_json工具调用聊天模板:
vllm serve meta-llama/Llama-3.1-8B-Instruct \ --enable-auto-tool-choice \ --tool-call-parser llama3_json \ --chat-template examples/tool_chat_template_llama3.1_json.jinja然后发送一个能触发工具调用的请求:
from openai import OpenAI import json client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy") def get_weather(location: str, unit: str): return f"Getting the weather for {location} in {unit}..." tool_functions = {"get_weather": get_weather} tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get the current weather in a given location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City and state, e.g., 'San Francisco, CA'"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]} }, "required": ["location", "unit"], }, }, }, ] response = client.chat.completions.create( model=client.models.list().data[0].id, messages=[{"role": "user", "content": "What's the weather like in San Francisco?"}], tools=tools, tool_choice="auto", ) tool_call = response.choices[0].message.tool_calls[0].function print(f"Function called: {tool_call.name}") print(f"Arguments: {tool_call.arguments}") print(f"Result: {tool_functionstool_call.name)}")预期输出:
Function called: get_weather Arguments: {"location": "San Francisco, CA", "unit": "fahrenheit"} Result: Getting the weather for San Francisco, CA in fahrenheit...这个例子完整演示了四个环节:启用工具调用启动服务端、定义实际的函数处理工具调用、以tool_choice="auto"发起请求、解析结构化响应并执行对应函数。
也可以指定调用某个特定函数(命名函数调用):
tool_choice={"type": "function", "function": {"name": "get_weather"}}注意命名函数调用会走结构化输出(structured outputs)后端,首次使用时 FSM 需要现场编译,会有数秒甚至更长的额外延迟,之后编译结果被缓存,后续请求不再有该开销。
最后必须明确:调用方需要自行负责——(1) 在请求中定义合适的 tools;(2) 在聊天消息中包含相关上下文;(3) 在应用逻辑中处理返回的工具调用。vLLM 本身只负责生成并解析工具调用,不执行函数。
四种 tool_choice 模式与约束解码行为
这是理解 vLLM 工具调用语义的核心。是否对模型生成施加工具参数 schema 约束,取决于tool_choice模式与每个工具上的strict字段:
tool_choice取值 | 是否 schema 约束解码 | 行为 |
|---|---|---|
| 命名函数(named function) | 是(经由 structured outputs 后端) | 参数保证是符合该函数参数 schema 的合法 JSON |
"required" | 是(经由 structured outputs 后端) | 与命名函数相同,且模型必须至少产出一个工具调用 |
"auto" | 仅当至少一个工具设置了strict: true | 结构化标签解析器在工具显式声明strict: true时约束工具调用参数;否则模型自由生成,工具调用从原始文本中提取 |
"none" | 不适用 | 不产生任何工具调用 |
三种模式的具体语义:
- 命名函数调用:默认即可用,vLLM 使用结构化输出保证响应匹配
tools参数中 JSON schema 定义的工具参数对象。文档强调一句话原则:you are guaranteed a validly-parsable function call — not a high-quality one(保证可解析,不保证质量)。为了效果最好,建议在 prompt 中同样写明期望的输出格式/schema,让模型本意生成与强制约束的 schema 保持一致。 tool_choice='required':与命名函数一样走结构化输出,默认启用、适用于任何受支持模型。设置后模型保证基于tools列表生成一个或多个工具调用,调用数量由用户查询决定,输出格式严格遵循tools中的 schema。替代解码后端的支持列入 V1 引擎路线图。tool_choice='none':即使请求中定义了 tools,模型也不会产生任何工具调用,只返回普通文本。官方文档特别提醒:默认情况下只要请求中带了 tools,无论tool_choice为何值,工具定义都会被渲染进 prompt;若在tool_choice='none'时希望连工具定义也排除,需要加--exclude-tools-when-tool-choice-none启动参数。该参数在 启动参数定义 中对应exclude_tools_when_tool_choice_none字段。
从源码结构看,auto模式下的约束能力由 ToolParser 基类 中的两个类属性驱动:structural_tag_model(标记该解析器对应 xgrammar 内置结构化标签模型)和supports_required_and_named。基类的adjust_request方法(abstract_tool_parser.py#L118-L165)会把tools参数转换成 JSON schema 并注入请求的structured_outputs字段——这正是命名函数与required模式获得 schema 约束的内部实现路径。
Strict 模式
- 对
tool_choice="required"或命名函数调用,无论strict字段如何,结构化标签约束始终生效; - 对
tool_choice="auto",至少一个工具设置strict: true即"加入"结构化标签约束,否则模型自由生成、工具调用从原始文本提取; strict字段在 Chat Completion、Responses、Anthropic Messages 三个 API 面均受支持。
为获得与严格 schema 强制最好的兼容性,工具参数 schema 建议采用 OpenAI strict-schema 风格书写:
- 每个 object 的
parameters中设置additionalProperties: false; properties中的全部字段都列入required;- 可选字段用允许
null表示,例如{"type": ["string", "null"]}。
此外 vLLM 提供全局开关环境变量VLLM_ENFORCE_STRICT_TOOL_CALLING(默认true)。设为false时,vLLM 不再为工具调用附加结构化标签,与逐工具的strict字段无关。该开关只影响基于结构化标签的工具调用,不改变命名函数调用与tool_choice="required"所使用的 schema 派生式结构化输出。源码中默认值定义见 vllm/envs.py#L240 与 vllm/envs.py#L1777-L1778,且ToolParser.get_structural_tag在 abstract_tool_parser.py#L167-L184 中会先检查该环境变量,为false时直接返回None。
VLLM_ENFORCE_STRICT_TOOL_CALLING=false vllm serve ...tool_choice="auto"时 schema 级约束同时要求VLLM_ENFORCE_STRICT_TOOL_CALLING=true(默认值)且至少一个工具声明strict: true;两者都满足且所选解析器支持结构化标签时,vLLM 才约束工具调用参数。否则 vLLM 从原始文本提取工具调用,参数偶尔会格式错误或不满足函数参数 schema。
自动函数调用的启动参数
要启用auto自动函数调用,需设置以下标志:
--enable-auto-tool-choice——必选。告诉 vLLM 允许模型在合适时机自主生成工具调用;--tool-call-parser—— 选择工具解析器(可选清单见下文)。源码中的校验逻辑要求二者必须同时提供:cli_args.py#L431-L432 中--enable-auto-tool-choice若缺少--tool-call-parser会直接抛TypeError;--tool-parser-plugin——可选,用于把用户自定义的工具解析器注册进 vLLM,注册后的解析器名可被--tool-call-parser引用;--chat-template——可选。指向处理tool角色消息与携带历史工具调用的assistant消息的聊天模板路径。Hermes、Mistral、Llama 模型的tokenizer_config.json自带兼容工具调用的模板,但也可以指定自定义模板。若模型的tokenizer_config.json中配置了专门用于工具调用的聊天模板,该参数可设为tool_use,vLLM 会按 transformers 的规范选用它。
当前仓库内置解析器名注册表见 vllm/tool_parsers/init.py,其中--tool-call-parser可填的每一个名字都通过ToolParserManager.register_lazy_module惰性注册——首次使用时才真正 import 对应模块,避免启动时加载全部解析器依赖。
各模型族解析器配置
以下配置均继承自官方文档,并按仓库实际文件核实模板路径。
Hermes 模型(hermes)
适用于 Hermes 2 Pro 之后的所有 Nous Research Hermes 系列模型:
NousResearch/Hermes-2-Pro-*NousResearch/Hermes-2-Theta-*NousResearch/Hermes-3-*
注意:Hermes 2Theta模型因创建过程中的 merge 步骤,工具调用质量与能力已知退化。
启动标志:--tool-call-parser hermes
Mistral 模型(mistral)
mistralai/Mistral-7B-Instruct-v0.3(已确认)- 其他 Mistral 函数调用模型同样兼容
已知问题:
- Mistral 7B 难以正确生成并行工具调用;
- 仅针对 Transformers 分词后端:Mistral 的
tokenizer_config.json聊天模板要求工具调用 ID 恰好为 9 位数字,远短于 vLLM 生成的 ID,不满足会抛异常。为此 vLLM 额外提供两个模板:- examples/tool_chat_template_mistral.jinja —— "官方" Mistral 聊天模板的微调版,配合 vLLM 工具调用 ID 工作(要求
tool_call_id字段截断到末 9 位); - examples/tool_chat_template_mistral_parallel.jinja —— "更好"的版本,在提供 tools 时追加一条工具使用系统提示,显著提升并行工具调用的可靠性。
- examples/tool_chat_template_mistral.jinja —— "官方" Mistral 聊天模板的微调版,配合 vLLM 工具调用 ID 工作(要求
推荐标志:
- 使用 Mistral AI 官方格式:
--tool-call-parser mistral - 可用 Transformers 格式时:
--tokenizer_mode hf --config_format hf --load_format hf --tool-call-parser mistral --chat-template examples/tool_chat_template_mistral_parallel.jinja
Mistral AI 官方发布的模型有两种格式:默认auto/mistral参数走官方格式(--tokenizer_mode mistral --config_format mistral --load_format mistral,基于 mistral-common 分词后端);可用 Transformers 格式时走hf参数并配合上述 parallel 模板。
Llama 模型(llama3_json)
Llama 3.1、3.2 和 4 系列均受支持:meta-llama/Llama-3.1-*、meta-llama/Llama-3.2-*、meta-llama/Llama-4-*。
受支持的是 JSON 形式的工具调用;Llama-3.2 引入的 pythonic 工具调用见下文pythonic解析器;Llama 4 模型建议使用llama4_pythonic解析器。内建 python 工具调用或自定义工具调用格式不受支持。
已知问题:Llama 3 不支持并行工具调用(Llama 4 支持);模型可能以错误格式生成参数(例如把数组序列化成字符串而非数组)。
vLLM 为 Llama 3.1 / 3.2 提供两个 JSON 模板:
- examples/tool_chat_template_llama3.1_json.jinja —— Llama 3.1 "官方"模板的微调版,与 vLLM 配合更好;
- examples/tool_chat_template_llama3.2_json.jinja —— 在 3.1 模板基础上增加图片支持。
推荐标志:--tool-call-parser llama3_json --chat-template {见上}
针对 Llama 4,vLLM 提供 pythonic 与 JSON 两种模板,推荐 pythonic:examples/tool_chat_template_llama4_pythonic.jinja。Llama 4 使用--tool-call-parser llama4_pythonic --chat-template examples/tool_chat_template_llama4_pythonic.jinja。
IBM Granite
ibm-granite/granite-4.0-h-small及其他 Granite 4.0 模型:--tool-call-parser granite4ibm-granite/granite-3.0-8b-instruct:--tool-call-parser granite --chat-template examples/tool_chat_template_granite.jinja(examples/tool_chat_template_granite.jinja 为 Hugging Face 原始模板的修改版,支持并行函数调用)ibm-granite/granite-3.1-8b-instruct:--tool-call-parser granite(可直接用 Hugging Face 上的聊天模板,支持并行函数调用)ibm-granite/granite-20b-functioncalling:--tool-call-parser granite-20b-fc --chat-template examples/tool_chat_template_granite_20b_fc.jinja(examples/tool_chat_template_granite_20b_fc.jinja 融合了 Hermes 模板的函数描述元素并遵循其论文"Response Generation"模式的系统提示,支持并行函数调用)
InternLM 模型(internlm)
internlm/internlm2_5-7b-chat(已确认),其他 internlm2.5 函数调用模型亦兼容- 已知问题:
internlm/internlm2-chat-7b上工具调用结果不稳定
推荐标志:--tool-call-parser internlm --chat-template examples/tool_chat_template_internlm2_tool.jinja
Jamba 模型(jamba)
支持 AI21 Jamba-1.5 系列:ai21labs/AI21-Jamba-1.5-Mini、ai21labs/AI21-Jamba-1.5-Large。标志:--tool-call-parser jamba
xLAM 模型(xlam)
xLAM 解析器专门处理以多种 JSON 风格生成工具调用的模型,可检测四种输出样式:
- 直接 JSON 数组:以
[开头]结尾的输出字符串; - 思考标签:
<think>...</think>标签内含 JSON 数组; - 代码块:
json ...中的 JSON; - 工具调用标签:
[TOOL_CALLS]或<tool_calls>...</tool_calls>标签。
支持并行函数调用,且能有效分离文本内容与工具调用。支持模型:Salesforce Llama-xLAM(Salesforce/Llama-xLAM-2-8B-fc-r、Salesforce/Llama-xLAM-2-70B-fc-r)与 Qwen-xLAM(Salesforce/xLAM-1B-fc-r、Salesforce/xLAM-3B-fc-r、Salesforce/Qwen-xLAM-32B-fc-r)。
标志:Llama 基底--tool-call-parser xlam --chat-template examples/tool_chat_template_xlam_llama.jinja;Qwen 基底--tool-call-parser xlam --chat-template examples/tool_chat_template_xlam_qwen.jinja
Qwen 模型
Qwen2.5 的tokenizer_config.json聊天模板已包含 Hermes 风格工具调用支持,直接复用hermes解析器即可。支持Qwen/Qwen2.5-*与Qwen/QwQ-32B。标志:--tool-call-parser hermes
DeepSeek-V3 模型(deepseek_v3)
deepseek-ai/DeepSeek-V3-0324:配合 examples/tool_chat_template_deepseekv3.jinjadeepseek-ai/DeepSeek-R1-0528:配合 examples/tool_chat_template_deepseekr1.jinja
标志:--tool-call-parser deepseek_v3 --chat-template {见上}
DeepSeek-V3.1 模型(deepseek_v31)
deepseek-ai/DeepSeek-V3.1,配合 examples/tool_chat_template_deepseekv31.jinja。标志:--tool-call-parser deepseek_v31 --chat-template {见上}
OpenAI OSS 模型(openai)
openai/gpt-oss-20b、openai/gpt-oss-120b。标志:--tool-call-parser openai
Kimi-K2 模型(kimi_k2)
moonshotai/Kimi-K2-Instruct。标志:--tool-call-parser kimi_k2
Hunyuan 模型(hunyuan_a13b)
tencent/Hunyuan-A13B-Instruct(聊天模板已包含在 Hugging Face 模型文件中)。标志:非推理--tool-call-parser hunyuan_a13b;推理模式追加--reasoning-parser hunyuan_a13b
Cohere Command A Reasoning(cohere_command3)
CohereLabs/command-a-reasoning-08-2025。标志:--tool-call-parser cohere_command3 --reasoning-parser cohere_command3。注意:该解析器依赖cohere_melody包,vLLM 默认不安装,使用前需自行安装。
LongCat-Flash-Chat 模型(longcat)
meituan-longcat/LongCat-Flash-Chat及其 FP8 版本。标志:--tool-call-parser longcat
GLM-4.5 / GLM-4.7 模型
- GLM-4.5(
zai-org/GLM-4.5、zai-org/GLM-4.5-Air、zai-org/GLM-4.6):--tool-call-parser glm45 - GLM-4.7(
zai-org/GLM-4.7、zai-org/GLM-4.7-Flash):--tool-call-parser glm47
从源码注册表看,两个解析器名实际映射到同一个类Glm47MoeModelToolParser(见 vllm/tool_parsers/init.py#L57-L64),说明 vLLM 已将 GLM 4.5/4.7 的工具调用处理统一到同一个 MoE 工具解析器实现中。
FunctionGemma 模型(functiongemma)
Google FunctionGemma 是 2.7 亿参数的轻量函数调用专用模型,基于 Gemma 3,面向笔记本、手机等边缘设备部署,支持google/functiongemma-270m-it。它使用独特的输出格式:
<start_function_call>call:get_weather{location:<escape>London<escape>}<end_function_call>官方建议针对具体函数调用任务微调以获得最佳效果。标志:--tool-call-parser functiongemma --chat-template examples/tool_chat_template_functiongemma.jinja
Qwen3-Coder 模型(qwen3_xml)
Qwen/Qwen3-Coder-480B-A35B-Instruct、Qwen/Qwen3-Coder-30B-A3B-Instruct。标志:--tool-call-parser qwen3_xml
Olmo 3 模型(olmo3)
Olmo 3 的工具调用输出与pythonic解析器期望的格式高度相似但有差异:每次工具调用仍是 pythonic 字符串,但并行调用以换行分隔,并包裹在<function_calls>..</function_calls>XML 标签内;此外解析器额外接受 JSON 布尔与空值字面量(true、false、null),以及 pythonic 的True、False、None。支持allenai/Olmo-3-7B-Instruct、allenai/Olmo-3-32B-Think。标志:--tool-call-parser olmo3
GigaChat 3 模型(gigachat3)
聊天模板来自 Hugging Face 模型文件。支持ai-sage/GigaChat3-702B-A36B-preview(含-bf16变体)与ai-sage/GigaChat3-10B-A1.8B(含-bf16变体)。标志:--tool-call-parser gigachat3
Apertus 模型(apertus)
swiss-ai/Apertus-8B-Instruct-2509、swiss-ai/Apertus-70B-Instruct-2509。需使用 examples 目录的聊天模板(修复了若干 OpenAI 兼容性问题):--tool-call-parser apertus --chat-template examples/tool_chat_template_apertus.jinja
Pythonic 工具调用模型(pythonic)
越来越多模型直接输出 python 列表(而非 JSON)来表示工具调用,天然支持并行工具调用,且消除了 JSON schema 歧义。例如查询旧金山与西雅图天气时模型可能生成:
[get_weather(city='San Francisco', metric='celsius'), get_weather(city='Seattle', metric='celsius')]限制:
- 模型不能在同一次生成中同时输出文本和工具调用。对特定模型这也许不难改变,但社区对工具调用起止应发射哪些 token 尚无共识(Llama 3.2 尤其不发射任何此类 token);
- Llama 小模型使用工具的能力较弱。
示例支持模型(⚠️ 表示小模型经常无法以正确格式发出工具调用,结果因模型而异):
meta-llama/Llama-3.2-1B-Instruct⚠️(配 examples/tool_chat_template_llama3.2_pythonic.jinja)meta-llama/Llama-3.2-3B-Instruct⚠️(同上)Team-ACE/ToolACE-8B(配 examples/tool_chat_template_toolace.jinja)fixie-ai/ultravox-v0_4-ToolACE-8B(配 examples/tool_chat_template_toolace.jinja)meta-llama/Llama-4-Scout-17B-16E-Instruct⚠️(配 examples/tool_chat_template_llama4_pythonic.jinja)meta-llama/Llama-4-Maverick-17B-128E-Instruct⚠️(配 examples/tool_chat_template_llama4_pythonic.jinja)
标志:--tool-call-parser pythonic --chat-template {见上}
工具调用性能基准测试
要度量真实工具调用流量下的服务延迟与吞吐,可使用 BFCL(Berkeley Function Calling Leaderboard)数据集配合vllm bench serve。完整的服务端 + 客户端命令见 docs/benchmarking/cli.md 中的 BFCL 基准小节。
编写工具解析器插件
如果目标模型不在上表支持范围内,官方鼓励社区贡献解析器与工具调用聊天模板。工具解析器插件是一个包含一个或多个ToolParser实现的 Python 文件,可参考 vllm/tool_parsers/hermes_tool_parser.py 中的Hermes2ProToolParser编写。插件文件结构如下:
# import the required packages # define a tool parser and register it to vllm # the name list in register_module can be used # in --tool-call-parser. you can define as many # tool parsers as you want here. class ExampleToolParser(ToolParser): def __init__(self, tokenizer: TokenizerLike): super().__init__(tokenizer) # adjust request. e.g.: set skip special tokens # to False for tool call output. def adjust_request(self, request: ChatCompletionRequest | ResponsesRequest) -> ChatCompletionRequest | ResponsesRequest: return request # implement the tool call parse for stream call def extract_tool_calls_streaming( self, previous_text: str, current_text: str, delta_text: str, previous_token_ids: Sequence[int], current_token_ids: Sequence[int], delta_token_ids: Sequence[int], request: ChatCompletionRequest, ) -> DeltaMessage | None: return delta # implement the tool parse for non-stream call def extract_tool_calls( self, model_output: str, request: ChatCompletionRequest, ) -> ExtractedToolCallInformation: return ExtractedToolCallInformation(tools_called=False, tool_calls=[], content=text) # register the tool parser to ToolParserManager ToolParserManager.register_lazy_module( name="example", module_path="vllm.tool_parsers.example", class_name="ExampleToolParser", )然后即可在命令行中加载该插件:
vllm serve <model> \ --enable-auto-tool-choice \ --tool-parser-plugin <absolute path of the plugin file> \ --tool-call-parser example \ --chat-template <your chat template>插件机制源码剖析
结合仓库源码可以理解这套插件机制的三个关键点:
- 必须实现的两个解析入口:
extract_tool_calls处理非流式响应(拿到完整模型输出后一次性解析),extract_tool_calls_streaming处理流式响应(增量解析,需要状态——当前 token/差异及已解析内容,因此基类构造函数维护了prev_tool_call_arr、current_tool_id、streamed_args_for_tool等流式状态,见 abstract_tool_parser.py#L72-L81)。 - 可选覆写的
adjust_request:用于调整请求,例如把工具调用输出的skip special tokens设为False。注意基类默认实现还会做一件重要的事:当tool_choice为命名函数或required时,自动从tools提取 JSON schema 写入request.structured_outputs(abstract_tool_parser.py#L118-L165),这正是"命名函数/required 模式保证可解析"的底层机制。 - 惰性注册:
ToolParserManager(abstract_tool_parser.py#L222-L262)同时支持即时注册(register_module,可作装饰器)与惰性注册(register_lazy_module,仅记录name -> (module_path, class_name),首次通过get_tool_parser访问时才 import 并缓存)。vLLM 内置的 40 余个解析器(hermes、llama3_json、pythonic、glm45、kimi_k2等)全部走惰性注册,插件加载则由import_tool_parser方法调用import_plugin完成用户文件的导入。
小结
tool_choice的四种取值对应两套不同机制:命名函数与required走结构化输出后端(schema 强约束,首次有 FSM 编译延迟);auto依赖解析器从模型自由生成文本中提取工具调用,仅当strict: true与VLLM_ENFORCE_STRICT_TOOL_CALLING(默认开)同时满足才附加结构化标签约束;none完全关闭工具调用,需要时可加--exclude-tools-when-tool-choice-none排除 prompt 中的工具定义;- 启用
auto必须成对提供--enable-auto-tool-choice与--tool-call-parser,聊天模板按模型族选择对应模板(本仓库 examples/ 目录提供了 Mistral、Llama、Granite、DeepSeek、xLAM 等全部官方模板文件); - 支持模型不在清单中时,通过
--tool-parser-plugin注册自定义ToolParser子类即可扩展,惰性注册机制保证插件不影响启动性能; - 性能评估可用 BFCL 数据集配合
vllm bench serve,方法见 docs/benchmarking/cli.md。
【免费下载链接】vllmA high-throughput and memory-efficient inference and serving engine for LLMs项目地址: https://gitcode.com/GitHub_Trending/vl/vllm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考