TT Lab
博客

한국어 English 日本語 中文

开始
博客

博客

工具调用:解析器将文本转换为函数调用,strict 约束参数

状态:草稿 (2026-10-27),实测。 测试环境为一张 8GB 级笔记本 GPU(Ada 代)、vLLM 0.31.0、Qwen3-Reranker-0.6B(针对重排微调的 Qwen3 0.6B 因果语言模型)。官方文档以 0.32.0(2026-10-18 发布)为准于 2026-10-27 打开进行了确认,与 0.31.0 文档有差异的地方已单独记录。 2026-10-27 补充。 使用对话专用的 Qwen3-0.6B、Qwen3-0.6B-GPTQ-Int8、Qwen3-4B-AWQ 以及 vLLM 0.32.0 重新测得的结果已补充至第 3.1 节。用 0.31.0 测得的前述数值保持不变。 编写规则:以实测数据为基础,未测出的标记为“未实测”,未能确认的标记为“未确认”。

目录

1. 工具调用是固定格式的文本

让我们想象一下代理(Agent)调用天气 API 的场景。模型内部并没有名为“函数调用”的特殊输出装置。模型依然是一次生成一个 token,只是在训练时学到了“调用工具时要写成这种形状”。Qwen 系列采用 Hermes 格式。

<tool_call>
{"name": "get_weather", "arguments": {"city": "Seoul", "unit": "c"}}
</tool_call>

服务器要做的事有三件:

  1. 将工具列表放入提示词中。 请求中的 tools 会由模型的聊天模板以文本形式写入系统提示词中。
  2. 挑选模型写出的文本。 从生成的文本中找出上述形状并将其转换为 OpenAI 格式的 message.tool_calls 的正是 工具解析器。由于不同模型的形状各异,解析器也有多种,例如 hermes, llama3_json, mistral, qwen3_xml 。
  3. 必要时强制规定形状。 tool_choice 若 required 是

或特定的函数名,第 4 篇中的结构化输出便会在 token 层面强制规定调用的外观。 相关的启用选项如下。如果没有 --enable-auto-tool-choice ,则不接受 tool_choice="auto" 请求。

vllm serve <모델> --enable-auto-tool-choice --tool-call-parser hermes

2. 四种 tool_choice

tool_choice 调用义务 什么来保证外观
auto 无,由模型决定 仅靠解析器。如果模型写错形状,则会作为文本残留或丢失
required 一个或多个,必须调用 结构化输出强制规定外观
指定名称({"type":"function","function":{"name":...}}) 仅限该函数 结构化输出强制规定外观
none 不调用 工具定义默认保留在提示词中(可通过--exclude-tools-when-tool-choice-none 移除)

官方文档针对指定名称的调用写道:“保证可解析的调用,但不保证高质量的调用。”这句话完美总结了本文的实测结果。

外观与参数被分别约束

从 0.21.0 开始,xgrammar 的 结构标签(structural tag) 被引入用于强制工具调用,而当前的文档将强制分成了两层。

服务端的底层默认值 --tool-strict-level 有三个: auto(默认,遵循请求)、 function(在所有请求中强制外层信封)、 parameter(将所有工具视作 strict)。由于大多数代理框架并未添加 strict ,因此这是为了让运维人员能够提高底层默认值而设计的。环境变量 VLLM_ENFORCE_STRICT_TOOL_CALLING=false 会完全关闭结构标签。

3. 亲自测试

环境与方法

结果

tool_choice strict 产生 tool_calls 的次数 遵守 Schema 的参数 响应时间(中位数)
auto 无 0/5 0/5 24.5ms
auto 有 0/5 0/5 24.6ms
required 无 4/5 0/5 216.6ms
指定名称 无 4/5 0/5 216.2ms
required 有 5/5 5/5 263.1ms
指定名称 有 5/5 5/5 261.7ms

直接复制了几条原始数据的响应。

在相同的种子下, required 与指定名称的输出连字符都完全相同。因为如果只有一个工具,这两种方式会生成相同的语法。

服务器也会对此进行统计。测试结束后, /metrics 的 vllm:tool_call_parser_invocations_total 在非流式聊天请求中是 outcome="tool_call" 18, outcome="no_tool_call" 12。这与客户端统计的值(产生 tool_calls 的响应 18 个,未产生的响应 12 个)一致。

阅读方法

  1. 无 strict 的 required 仅保证了「存在调用」。 函数名称虽然对了,但参数是个空对象,或者缺少了必填字段。0.31.0 和 0.32.0 的文档中都提到「没有 strict 的工具在 required 和指定名称时也只接受宽松的参数语法」,实际测量结果与该描述相符。
  2. 开启 strict 后虽然遵守了 Schema,但语义并不正确。 虽然所有参数都能被解析为 JSON 并且遵守了必填字段和枚举值,但提问中的 Seoul 一次也没有出现过,还出现了 "none" 这样的值。语法仅过滤出「允许的 Token」,而在剩余的 Token 中究竟选择哪一个,依然由模型决定。
  3. 时间差异主要来自于 Token 数量。 strict 这一侧虽然长了大约 50ms,但其中夹杂了因为 {} 完整写出两个字段而导致 Token 增加的部分,因此不能将其简单视为语法检查本身的开销。仅单独查看语法开销的数据在第四篇中。

3-1. 2026-10-27 重新测量:熟悉工具的模型与 0.32.0

前面的测试使用的是不熟悉工具的模型,观察的是「服务器能够约束的范围」。这一次我们使用训练过工具调用的模型来观察内容本身。

模型 tool_choice strict 产生调用的次数 正确的城市与单位 响应时间中位数
Qwen3-0.6B auto 无 6/6 6/6 162.1ms
Qwen3-0.6B required 无 6/6 6/6 178.0ms
Qwen3-0.6B 指定名称 无 6/6 6/6 178.1ms
Qwen3-0.6B auto·required·指定名称 有 18/18 18/18 184.1~185.4ms
Qwen3-0.6B GPTQ 8-bit 所有六种情况 无·有 36/36 36/36 115.7~140.6ms
Qwen3-4B AWQ 4-bit 所有六种情况 无·有 36/36 36/36 338.7~362.4ms

由于该测试只包含一个工具和一个简短的问题,属于比较简单的情况。当拥有多个工具或参数较为复杂时,准确率仍有待实际测量。

4. 运维实践建议

5. 未确认的事项

参考

登录后即可点赞

评论

还没有评论。

登录后即可发表评论