修复 vLLM 中的工具调用准确性问题
原题Fixing Tool-Call Accuracy in vLLM
推荐理由Ubicloud 用约束解码修复工具调用格式,帮助开源模型部署者区分 schema 合规与任务正确,排查无效调用如何污染后续上下文。
读前速览
Ubicloud 通过在 vLLM 中启用约束解码,提升了 Kimi 和 DeepSeek 模型的工具调用可靠性,确保 100% 符合 schema,并防止无效调用污染上下文。
工具调用准确率的重要性
准确的工具调用对于可靠的智能体工作流至关重要。无效调用会污染上下文窗口、降低模型性能,并因重试而增加运营成本。K2VV 基准测试表明,标准服务方式往往无法达到 100% 的准确率。
约束解码的工作原理
约束解码通过修改 token 采样过程,阻止模型生成语法上无效的工具调用。推理引擎根据工具 schema 在每一步限制有效 token 的集合,从而保证输出符合所需格式。
在 vLLM 中的实现
虽然 vLLM 支持约束解码,但由于与部分模型存在兼容性问题,该功能默认关闭。Ubicloud 专门为 Kimi 和 DeepSeek 部署启用了这一功能,成功将工具调用准确率提升至 100%,确保了高质量的服务。
完整摘要
本文探讨了开放权重模型中工具调用准确率偏低的问题,无效调用会导致性能下降和成本增加。文章解释了约束解码如何在生成过程中强制遵循 schema,以及 Ubicloud 如何实现该功能,使 Kimi 和 DeepSeek 部署的工具调用准确率达到完美水平。
目录 · 5 节
修复 vLLM 中的工具调用准确性Fixing Tool-Call Accuracy in vLLM
Ubicloud 机器映像正式发布Announcing Ubicloud Machine Images
DeepSeek V4、Kimi K2.6 和 Nemotron 3 Ultra 的人工智能安全研究AI Safety Study for DeepSeek V4, Kimi K2.6, and Nemotron 3 Ultra
PostgreSQL 与 OOM Killer:我们为何采用严格的内存超额承诺PostgreSQL and the OOM Killer: Why We Use Strict Memory Overcommit
Ubicloud 价格调整Ubicloud Price Adjustment
Ubicloud 驱动的 ClickHouse PostgreSQL:快速事务处理与分析ClickHouse PostgreSQL powered by Ubicloud: Fast transactions and analytics
使用开源软件虚拟化 NVIDIA HGX B200 GPUVirtualizing NVIDIA HGX B200 GPUs with Open Source
通过自动生成截图保持文档更新Keeping Documentation Up-To-Date via Automated Screenshot Generation
基于视觉语言模型的端到端 OCREnd-to-End OCR with Vision Language Models
硬件触控,更强大的 SSHHardware Touch, Stronger SSH
AI 编程:冷静审视AI Coding: A Sober Review
MHz 仍然重要吗?Does MHz still matter?
推理请求的生命周期(vLLM V1):如何高效地大规模提供 LLM 服务Life of an inference request (vLLM V1): How LLMs are served efficiently at scale
Ubicloud Premium Runners:构建速度提升 2 倍,缓存容量扩大 10 倍Ubicloud Premium Runners: 2x Faster Builds, 10x Larger Cache
PostgreSQL 性能:本地存储与网络附加存储PostgreSQL Performance: Local vs. Network-Attached Storage
Ubicloud PostgreSQL:新功能,更高性能Ubicloud PostgreSQL: New Features, Higher Performance
构建突发型实例:使用 cgroups 切分 CPUBuilding Burstables: cpu slicing with cgroups
无忧 Kubernetes,兼具裸金属的性价比Worry-free Kubernetes, with price-performance of bare metal
Ubicloud 的轻量级 CLIent 命令行界面方案Ubicloud's Thin CLIent approach to command line interfaces
Dewey.py:用开放模型重建深度研究Dewey.py: Rebuilding Deep Research with Open Models
Ubicloud 突发型虚拟机,每小时仅需 0.01 美元起Ubicloud Burstable VMs starting at $0.01 per hour
Debugging Hetzner: Uncovering failures with powerstat, sensors, and dmidecode
云端虚拟化:Red Hat、AWS Firecracker 与 Ubicloud 的内部原理Cloud virtualization: Red Hat, AWS Firecracker, and Ubicloud internals
OpenAI o1 与 QwQ-32B:分析OpenAI o1 vs. QwQ-32B: An Analysis
让 GitHub Actions 和 Docker 层缓存提速 4 倍Making GitHub Actions and Docker Layer Caching 4x Faster
EuroGPT:开源且注重隐私的 ChatGPT Enterprise 替代方案EuroGPT: Open source and privacy conscious alternative to ChatGPT Enterprise
不到 200 行代码实现私有网络对等连接Private Network Peering under 200 Lines
Ubicloud 上的 Lantern:使用 PostgreSQL 构建 AI 应用程序Lantern on Ubicloud: Build AI applications with PostgreSQL
Postgres 上具备 Elastic 级品质的全文搜索:Ubicloud 上完全托管的 ParadeDBElastic-Quality Full Text Search on Postgres: Fully managed ParadeDB on Ubicloud
Ubicloud 负载均衡器:简单且经济高效Ubicloud Load Balancer: Simple and Cost-Effective
用 Ruby 构建基础设施控制平面的 13 年13 Years of Building Infrastructure Control Planes in Ruby
为自己运行 Postgres 与为他人运行 Postgres 的区别Difference between running Postgres for yourself and for others
Ubicloud 块存储:加密Ubicloud Block Storage: Encryption
宣布 Ubicloud 推出全新计算功能Announcing New Ubicloud Compute Features
我们如何启用 ARM64 虚拟机How we enabled ARM64 VMs
Ubicloud 防火墙:Linux Nftables 如何实现灵活规则Ubicloud Firewalls: How Linux Nftables Enables Flexible Rules
使用 Linux Flowtables 提升网络性能Improving Network Performance with Linux Flowtables
欧盟新的云可移植性要求——这意味着什么?EU's new cloud portability requirements - What do they mean?
Ubicloud 托管 Arm 运行器,性价比提升 100 倍Ubicloud hosted Arm runners, 100x better price/performance
使用 SPDK 构建云端块存储(非复制)Building block storage for the cloud with SPDK (non-replicated)
开放且可移植的 Postgres 即服务Open and portable Postgres-as-a-service
构建简单授权系统(ABAC)的经验教训Learnings from Building a Simple Authorization System (ABAC)
vCPU、线程、核心、节点、插槽。如今这些 CPU 术语都是什么意思?vCPU, thread, core, node, socket. What do CPU terms mean these days?
Ubicloud 简介Introducing Ubicloud
2026年10月20日 · 阅读需8分钟October 20, 2026 · 8 min read

杰西·东克维利特和李俊豪Jesse Donkervliet and Junhao Li
高级软件工程师Senior Software Engineers
当我使用开放权重模型时,发现实际效果与质量基准所显示的水平相去甚远。When I use an open-weight model, I see results that are nowhere close to what the quality benchmarks indicate.
如今与 AI 模型合作的软件开发者Software developer working with AI models these days
工具调用准确性是服务开放权重模型时的重要一环。如果工具调用不准确,模型就会在无意义的上下文基础上继续生成内容。这会导致结果质量低下,尤其是在多轮会话很常见的智能体工作负载中。遗憾的是,许多服务提供商和广泛使用的推理引擎都无法完全通过工具调用准确性测试。例如,我们使用 vLLM 0.28 版本部署 Kimi K2.6 时,只有 92% 的情况下能得到符合预期的工具调用 schema。换句话说,每尝试调用 25 次工具,就有 2 次失败。好在 vLLM 最近加入了对受约束解码的支持,这种技术可以确保输出符合 schema。随后,这项功能作为服务器端标志在 vLLM 0.31 中发布。启用后,使用较新的 Kimi 和 DeepSeek 模型即可实现 100% 的 schema 准确率。本文将先介绍工具调用 schema 准确率及其在服务智能体工作负载时的重要性,然后探讨受约束解码,以及它如何解决 schema 准确率问题。Tool call accuracy is an important part of serving open-weight models. When your tool calls aren’t accurate, the model builds up on nonsensical context. This leads to low quality results, particularly for agentic workloads where multi-turn sessions are common.
Sadly, many providers and widely-used inference engines don’t fully pass tool-call accuracy tests. For example, when we served Kimi K2.6 with vLLM version 0.28, we got the expected tool call schema only 92% of the time. In other words, 2 out of every 25 attempted tool calls failed.
Fortunately, vLLM recently added support for constrained decoding, a technique to guarantee schema-adherence. This feature has then been released as a server-side flag in vLLM 0.31. By enabling it, you can get to 100% schema accuracy with the newer Kimi and DeepSeek models.
In this blog post, we first describe tool call schema accuracy and why it’s important in serving agentic workloads. We then talk about constrained decoding and how it fixes the schema accuracy problem.
工具调用 SchemaTool-call Schemas
你可以通过两个步骤让 LLM 调用外部工具:(1) 告知 AI 模型有哪些可用的外部工具调用;(2) 指示模型如何输出结构化的工具调用。例如,将 OpenCode 连接到 Kimi 时,OpenCode 会向 Kimi 发送一个 JSON 数组,描述可用的工具。列表中的一个工具是 webfetch,用于获取网页内容:You can enable an LLM to call an external tool in two steps: (1) inform the AI model about the existence of externally available tool calls and (2) provide the model with instructions on how to output structured tool-calls. For example, when connecting OpenCode to Kimi, OpenCode sends Kimi a JSON array describing available tools. One of the tools in this list is webfetch, which fetches Web page contents:
[{
"type": "function",
"function": {
"name": "webfetch",
"description": "- Fetches content from a specified URL\n- Takes a URL and optional format as input\n- Fetches the URL content, converts to requested format (markdown by default)\n- Returns the content in the specified format\n- Use this tool when you need to retrieve and analyze web content\n\nUsage notes:\n [content redacted for brevity]\n",
"parameters": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "The URL to fetch content from"
},
"format": {
"type": "string",
"enum": [
"text",
"markdown",
"html"
],
"description": "The format to return the content in (text, markdown, or html). Defaults to markdown.",
"default": "markdown"
},
"timeout": {
"type": "number",
"description": "Optional timeout in seconds (max 120)"
}
},
"required": [
"url"
]
}
}
}, ...]
随后,推理引擎会将此 JSON 描述封装在特定于 LLM 的 token 中,再传递给 LLM,以匹配 LLM 经过训练后识别工具声明的方式。在为 Kimi 提供服务时,内容大致如下:The inference engine then wraps this JSON description in LLM-specific tokens before passing it into the LLM, to match how the LLM was trained to recognize tool declarations. When serving Kimi, this looks something like:
<|im_system|>tool_declare<|im_middle|>[{"type":"function","function":{...}, ...]<|im_end|>
描述该工具(或上文 JSON 中的函数)的 JSON 对象包含工具的参数、参数类型、哪些参数是必需的,等等。这称为工具的模式。智能体框架(例如 OpenCode)要成功执行由 LLM 生成的工具调用,该调用就必须遵循其模式中规定的规则。然后,当用户通过智能体框架请求获取网页时,LLM 可能会输出以下令牌:The JSON object describing the tool (or function, in the JSON above) includes the tool’s parameters, their types, which of the parameters are required, etc. This is known as the tool’s schema. For an agent harness (e.g., OpenCode) to successfully execute a tool call generated by the LLM, the call must follow the rules specified in its schema.
Then, when a user asks to fetch a Web page via their agent harness, the LLM may output the following tokens:
I'll fetch that page. <|tool_calls_section_begin|><|tool_call_begin|>functions.webfetch:0<|tool_call_argument_begin|>{"url": "https://ubicloud.com", "format": "markdown"}<|tool_call_end|><|tool_calls_section_end|>
这些令牌由推理服务器的解析器进行解析,提取出 LLM 专用令牌,并将调用转换为如下 JSON 对象:These tokens are parsed by the inference server’s parser, extracting the LLM-specific tokens and turning the call into a JSON object such as:
{
"role": "assistant",
"content": "I'll fetch that page.",
"tool_calls": [
{
"id": "functions.webfetch:0",
"type": "function",
"function": {
"name": "webfetch",
"arguments": "{\"url\": \"https://example.com\", \"format\": \"markdown\"}"
}
}
]
}
然后,这会被发送回你的 harness(在我们的示例中是 OpenCode),由它运行 webfetch 工具,并将其输出发送回模型。This is then sent back to your harness (OpenCode, in our example), which runs its webfetch tool and sends its output back to the model.
架构准确性的重要性The Importance of Schema Accuracy
要使工具调用成功,该调用必须遵循工具的 schema。最简单的工具调用方式,是让模型自由生成工具调用,并相信模型经过训练后足以生成正确的调用。这与我们相信模型能够生成语法正确的文本类似。由于现代模型擅长遵循结构化模式,这种简单方式在大多数情况下都行得通。但“大多数情况下”也意味着,仍有许多工具调用会失败,导致错误消息污染上下文窗口、模型性能下降,以及智能体不得不重试一次或多次而增加成本。为了避免在使用 Kimi 时出现这类问题,Moonshot AI 发布了 K2VV ToolCall 基准测试。该基准测试让服务提供方能够验证其为 Kimi 提供的服务是否达到与原版相同的质量,包括默认行为——只生成有效的工具调用。K2VV ToolCall 的工作负载由 2000 个请求组成,旨在评估 LLM 何时生成工具调用,以及生成的工具调用在语法上是否正确(即是否遵循所提供的 schema)。该基准测试通过量化两项指标来分析模型质量:触发相似度和 schema 准确率。触发相似度考察服务何时决定触发工具调用,以及这些决策与 Moonshot AI 自有服务有多相似。Schema 准确率则量化遵循工具 schema、因而能够成功调用的工具调用所占比例。例如,当工具调用准确率为 50% 时,模型生成的工具调用中有一半格式错误。由于语言模型具有概率性,schema 准确率通常难以达到理想水平。模型根据可能性生成 token,并不运行编译器或类型检查器。即使提示清晰,它仍可能生成格式错误的工具调用。例如,这可能是因为模型的训练数据与当前推理框架不匹配。模型使用许多工具调用数据集进行训练,每个数据集都有自己的工具、schema 和约定。因此,即便某个工具或字段名在当前生产环境中并不存在,模型也可能生成它在训练期间见过的名称。例如,可用工具是 web_search 时,它可能生成 search_web;或者在当前 schema 要求 location 时,它可能使用 city_name。For a tool-call to succeed, the call must adhere to the tool’s schema.
The simple approach to tool calling is to let the model generate tool calls freely and trust that the model’s training is sufficient to generate correct calls. This is similar to how we trust the model to generate syntactically correct prose.
The simple approach works most of the time because modern models are good at following structured patterns. But success “most of the time” implies that many tool calls can still fail, resulting in context window pollution due to error messages, lower model performance, and increased cost due to the agent having to retry the call one or more times.
To avoid such experiences when using Kimi, Moonshot AI published their K2VV ToolCall benchmark. This benchmark lets providers verify that they serve Kimi with the same quality as the original, including its default behavior of only generating valid tool calls.
K2VV ToolCall’s workload consists of 2000 requests selected to evaluate when the LLM generates tool calls and whether the generated tool calls are syntactically correct (i.e., adhere to the provided schema). The benchmark allows analyzing the model’s quality by quantifying two metrics: trigger similarity and schema accuracy. Trigger similarity considers when the service decides to trigger a tool call, and how similar its decisions are to Moonshot AI’s own service. Schema accuracy quantifies the fraction of tool-calls that adhere to the tool’s schema and can therefore be invoked successfully. For example, when tool-call accuracy is 50%, half of the tool calls generated by the model are malformed.
Schema accuracy usually falls short due to the probabilistic nature of language models. A model generates tokens based on likelihood. It is not running a compiler or type checker. Even with a clear prompt, it can still produce malformed tool calls. This can be caused by, for example, a mismatch between the model’s training data and the current inference harness. Models are trained on many tool-calling datasets, each with its own tools, schemas, and conventions. As a result, a model may generate a tool or field name that it has seen during the training, even if it does not exist in the current production environment. For example, it may generate search_web when the available tool is web_search, or use city_name when the active schema expects location.
受限解码Constrained Decoding
LLM 会计算每个 token 成为序列中下一个 token 的概率,然后通过加权采样选出下一个 token。受约束解码通过阻止模型采样会导致工具调用语法无效的 token,避免生成格式错误的工具调用。使用受约束解码时,推理引擎不会让模型自由采样 token、再事后验证结果,而是逐步引导生成过程。在每一步中,只允许生成有效的 token。哪些 token 可以生成,由提供的工具 schema 决定。vLLM 使用 XGrammar 库中的结构标签来实现这一功能。下面的代码片段展示了 XGrammar 在 Kimi K2.6 中实现受约束解码的部分内容。它定义了工具调用的有效结构,包括调用从何处开始、索引位于何处、参数从何处开始,以及必须遵循 schema 约束生成 JSON 的位置。An LLM computes for each token the probability that it is the next in the sequence. It then selects the next token by performing a weighted sample. Constrained decoding avoids malformed tool calls by preventing models from sampling tokens that would result in a syntactically invalid tool call. When using constrained decoding, instead of letting the model sampling tokens freely and validating the result afterward, the inference engine guides generation step by step. At each step, only valid tokens are allowed.
Which tokens are allowed is determined by the provided tool schema. vLLM implements this using structural tags from the XGrammar library. The snippet below shows an excerpt of how XGrammar implements constrained decoding in Kimi K2.6. It defines the valid structure of a tool call, including where the call begins, where the index appears, where the arguments begin, and where schema-constrained JSON must be generated.
# Kimi K2.6 tool call syntax:
# <|tool_call_begin|>{function.name}:{index}
# <|tool_call_argument_begin|>{arguments}
# <|tool_call_end|>
TagFormat(
begin=f"{TOOL_CALL_BEGIN_PREFIX}{name}{TOOL_CALL_SUFFIX}",
content=SequenceFormat(
elements=[
RegexFormat(pattern=r"\d+"),
ConstStringFormat(value=TOOL_CALL_ARGUMENT_BEGIN),
JSONSchemaFormat(
json_schema=parameters,
any_order=any_order,
max_whitespace_cnt=max_whitespace_cnt,
),
]
),
end=TOOL_CALL_END,
)
你可以在这里找到完整实现。有了这个定义,生成过程必须遵循一条有效路径:You can find the full implementation here.
With this definition, generation must follow a valid path:
开始工具调用 → 工具名称 → 工具调用索引 → 开始参数 → 受 Schema 约束的 JSON → 结束工具调用Begin tool call → Tool name → Tool call index → Begin arguments → Schema-constrained JSON → End tool call
生成过程中会阻止无效路径。例如,当生成进入工具调用索引阶段时,引擎会将采样限制为整数 token,并屏蔽其他所有内容。下图转载自 XGrammar 的文章,直观展示了无效 token 是如何被屏蔽的。Invalid paths are blocked during generation. For example, when generation reaches the tool-call index phase, the engine restricts sampling to integer tokens and masks out everything else. The figure below, reproduced from XGrammar’s article, provides a good visualization of how invalid tokens are masked.

来源:Dong, Yixin 等人。《XGrammar:面向大型语言模型的灵活高效结构化生成引擎》。机器学习与系统会议论文集,第 7 卷(2025 年)。[链接]Source: Dong, Yixin, et al. "XGrammar: Flexible and efficient structured generation engine for large language models." Proceedings of Machine Learning and Systems 7 (2025). [link]
这使模式准确性从尽力而为提升为运行时保证。模型仍然决定生成哪个 token,但现在只能从受限且语法正确的集合中选择。值得注意的是,这种运行时强制机制带来的性能开销微乎其微。XGrammar 通过预先计算各阶段的 token 掩码,并在生成过程中高效地在 GPU 上应用这些掩码,确保工具调用正确,同时不会显著牺牲性能。This changes schema accuracy from a best-effort approach to a runtime guarantee. The model still decides which token to generate, but now the token is selected from a restricted and syntactically-correct set.
Notably, this runtime enforcement adds negligible performance overhead. By precomputing token masks for each phase, and applying them efficiently on the GPU during generation, XGrammar guarantees correct tool calls without trading off significant performance.
受约束解码行为的差异Differences in Constrained-Decoding Behavior
Moonshot AI 建议启用受限解码,而且其自有 API 也采用这种行为。因此,我们最初在 vLLM 的 fork 版本中加入了受限解码。vLLM 在 v0.25 中加入这项功能后,我们便改用上游提供的功能来处理 Kimi 和 DeepSeek。遗憾的是,不久之后,我们发现工具调用准确率又降回了 91%!经过分析,我们确认 vLLM 的受限解码实现本身仍然正确。相反,我们发现,vLLM 改变了未明确启用受限解码的请求所采用的默认行为。这主要是因为 vLLM 的设计目标是支持各种各样的模型。支持受限解码需要针对每个模型单独处理,相关问题大致分为三类:Moonshot AI recommends constrained decoding to be turned on and its own API behaves as such. So we initially added constrained decoding in our fork of vLLM. After vLLM added this feature in v0.25, we switched to using this feature from upstream for Kimi and DeepSeek. Unfortunately not long after, we found out that our tool-call accuracy score dropped back to 91%!
After analyzing the problem, we confirmed that vLLM’s implementation of constrained decoding was still correct. Instead, we found that vLLM changed its default behavior for requests that do not explicitly turn constrained decoding on. This is primarily because vLLM is designed to support a wide range of models. Supporting constrained decoding is a per-model task and problems fall into three camps:
由于这些问题,vLLM 维护者报告称,在启用受约束解码后,多个模型出现了问题。因此,对于 vLLM 这类与模型无关的服务引擎而言,默认禁用受约束解码是更稳妥的选择。后来,vLLM 添加了 --tool-strict-level 标志,让用户可以为自己的部署启用工具调用严格模式。不过,Ubicloud 的重点与 vLLM 不同。我们专注于提供 Kimi 和 DeepSeek 模型服务,并达到用户对官方服务提供商的质量预期。因此,我们启用了受约束解码,并重新运行了 K2VV ToolCall 基准测试。在确认所有提供服务的模型版本都实现了 100% 的工具调用模式准确率后,我们将这一改动部署到了生产环境。随后,我们撰写了这篇博客文章,分享我们的经验。Due to these issues, vLLM maintainers reported seeing problems with several models after constrained decoding was enabled. This made disabling constrained decoding by default the safer option for a model-agnostic serving engine such as vLLM. Later, vLLM added the --tool-strict-level flag so that users can enable tool-call strictness for their deployments.
Ubicloud’s focus is different from that of vLLM however. We focus on serving Kimi and DeepSeek models with the quality that users expect from first-party providers. For this reason, we enabled constrained decoding and re-ran the K2VV ToolCall benchmark. Once we verified 100% tool-call schema accuracy across all served model versions, we deployed the change to production. We then typed up this blog post to share our learnings.
结论Conclusion
使用 LLM 时,你观察到的质量并非仅由模型决定,推理引擎也发挥着重要作用。更换推理引擎及其设置,就会改变模型的输出质量。在部署 Kimi K2.6 和 DeepSeek V4 Flash 时,我们发现工具调用模式存在不准确之处,导致质量明显下降。我们先维护了自己的 vLLM 分支,随后启用 vLLM 中新的约束解码功能,解决了这个问题。这帮助我们将 K2VV ToolCall 基准测试中的工具调用准确率从 92% 提升到了 100%。在这篇博客文章中,我们分享了在工具调用和约束解码方面的经验。如果你对此感兴趣,或者想与我们交流模型部署中的经验教训,欢迎给我们的支持邮箱发邮件。When using LLMs, the quality you observe isn’t based on the model alone. The inference engine also plays an important role. Change the inference engine and its settings, and you change the model’s output quality.
When serving Kimi K2.6 and DeepSeek V4 Flash, we observed visible quality degradations due to tool-call schema inaccuracies. We solved this problem by first carrying our own vLLM fork and then enabling the new constrained decoding feature in vLLM. This helped improve our tool-call accuracy in the K2VV ToolCall benchmark from 92% to 100%.
In this blog post, we shared our experience with tool calling and constrained decoding. If this is interesting or you’d like to share war stories about serving models, please drop us a line in our support email.
关于我们状态支持信任中心服务条款联系我们隐私政策About UsStatusSupportTrust CenterTerm of ServiceContact UsPrivacy Policy


