Muse tool parser JSON-decodes Optional[str] parameters, so “10001” becomes 10001

使用 vLLM 的 Muse tool parser 时,若工具参数声明为 Optional[str] (即 ["string", "null"] 或 anyOf 写法),解析器会把原本的字符串值当成 JSON 解码,导致 "10001" 变成数字 10001 ,客户端拿到的类型与 schema 不

快速结论:使用 vLLM 的 Muse tool parser 时,若工具参数声明为 Optional[str](即 ["string", "null"] 或 anyOf 写法),解析器会把原本的字符串值当成 JSON 解码,导致 "10001" 变成数字 10001,客户端拿到的类型与 schema 不符。优先排查工具参数 schema 是否含可空字符串写法,并升级到包含修复的提交。

适用环境:Issue 已确认的环境为 vLLM main(fa3b9d137,从源码树在 CPU 上导入)、torch 2.15.0+cpu、transformers 5.20.2、pydantic 2.14.1、Python 3.12.6;使用 --tool-call-parser muse。GPU/CUDA 环境未在 Issue 中给出。

最快修复方案:升级到包含 #64518 的版本(该 PR 已在 Python 与 Rust 两侧修复),升级后在 Issue 给出的复现脚本中,三种 schema 均输出 "10001"。

注意事项:Issue 作者最初提出的 coerce_to_schema_type 方案并非最终采纳的修复,不要照搬;Rust 侧的验证并非由复现脚本实际执行,仅在 #64518 的 diff 中阅读确认;#64568 尝试修改同一处 Rust 检查,与 main 产生冲突,合入时需注意。

问题场景

在 vLLM 中使用 --tool-call-parser muse 解析 ATEM 格式的工具调用时,若被调用函数的某个参数 schema 声明为可空字符串({"type": ["string", "null"]} 或 pydantic 为 str | None 生成的 anyOf 写法),模型输出的原始文本 10001 会被 JSON 解码,客户端最终收到 {"zip": 10001}。而普通 {"type": "string"} 参数会正确保留为 {"zip": "10001"}。

报错原文

Muse tool parser JSON-decodes Optional[str] parameters, so "10001" becomes 10001

{'type': 'string'}                                      {"zip": "10001"}
{'type': ['string', 'null']}                            {"zip": 10001}
{'anyOf': [{'type': 'string'}, {'type': 'null'}]}       {"zip": 10001}

原因分析

Muse 解析器在 vllm/tool_parsers/muse_tool_parser.py 的 _string_parameters 中判断字符串参数时,只匹配 schema.get("type") == "string",因此漏掉了数组形式的可空类型 ["string", "null"] 以及 pydantic 为 str | None 生成的 anyOf 结构。这类参数未进入“保留原始文本”的路径,而是走了通用 JSON 解码,于是纯数字字符串被转换成了整数。

Issue 作者验证:把类型匹配改为通过 extract_types_from_schema 识别后,三种 schema 都能输出 "10001";恢复原第 336 行的判断后,整数又回来了。最终维护者说明,这与 #64517 同一根因,由 #64539 引入共享的 is_string_schema() 辅助函数统一处理 anyOf/oneOf/数组形式的可空字符串,并在 Muse、MiniCPM5、Poolside V1 解析器中复用,随后 #64518 合入,同时修复 Python 与 Rust 实现。

环境排查

  • 确认当前 vLLM 版本或提交号是否包含 #64518;Issue 复现所用为 main fa3b9d137,验证修复所用提交为 1fc9d00。
  • 确认 Python 3.12.6、torch 2.15.0+cpu、transformers 5.20.2、pydantic 2.14.1 等依赖与当前环境一致,便于复现。
  • 确认启动参数使用 --tool-call-parser muse。
  • 检查工具参数 schema 是否为 {"type": ["string", "null"]}、anyOf 或 oneOf 形式的可空字符串。
  • 若涉及 Rust 解析器(rust/src/parser/src/unified/muse.rs),需单独确认该侧是否包含修复。

解决步骤

  1. 先定位工具定义中声明为 Optional[str](可空字符串)的参数,确认其 schema 形态。
  2. 将 vLLM 升级到包含 #64518 的版本或提交;该修复在 Muse 解析器中通过统一的字符串 schema 判断,使可空字符串参数保留原始文本。
  3. 使用 Issue 中的复现脚本分别测试 {"type": "string"}、{"type": ["string", "null"]}、{"anyOf": [{"type": "string"}, {"type": "null"}]} 三种 schema,核对输出。
  4. 如继续保留自定义补丁,不要直接照搬 coerce_to_schema_type 的推测方案,应以 #64518 的实际实现为准,否则可能改变 ["string", "integer"] 等类型组合与 null 的处理行为。

验证方法

运行 Issue 中的 repro.py,在含修复的提交(如 1fc9d00)上,三种 schema 应全部输出 {"zip": "10001"},即字符串保持不变;若输出 {"zip": 10001},说明修复未生效或未正确加载。Rust 解析器一侧建议另行用实际调用验证,因为 Issue 中未实际运行。

参考来源

vllm-project/vllm #64546

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 33037

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注