Prhub

#25600 Add MiniCPM5 tool call parser for XML-style function calls

原始 PR 作者 zhangtao2-1 合并时间 2026-05-22 23:09 文件变更 5 提交数 11 评论 16 代码增减 +645 / -0

执行摘要

新增 MiniCPM5 XML 格式工具调用解析器

MiniCPM5 使用 XML 风格的函数调用格式(V3 Schema),与基于 JSON 的解析器不同。没有专用检测器时,SGLang 无法在流式或批推理中正确提取模型输出的工具调用。

值得精读。该 PR 是工具调用框架的一次典型扩展,展示了 parser 注册、自动检测规则添加和流式边界处理的完整实践。特别推荐关注 detect_and_parse 中的多路径解析(lxml primary + regex fallback)和 parse_streaming_increment 中的缓冲区管理逻辑。

讨论亮点
  • 流式缓冲区处理:JustinTong 指出当 bot_token '<function' 被跨 chunk 分割时当前实体会丢失后续 chunk 中的检测,应保留部分前缀。作者在后续提交中通过 _ends_with_partial_token 修复。
  • 自动检测注册:JustinTong 要求将 MiniCPM5 加入自动模板检测,否则 --tool-call-parser auto 无法命中。作者添加 _is_minicpm5 谓词和规则注册,并编写优先级测试。
  • 异常处理优化:gemini-code-assist 建议将 parse_arguments 中的裸 except 替换为具体异常类型,已在代码中采纳。
  • 未初始化变量:JustinTong 指出 has_invalid_param 在正则 fallback 前未初始化,作者在修复提交中初始化。

实现拆解

  1. 解析器核心:在 minicpm5_detector.py 中编写 MiniCPM5Detector,继承 BaseFormatDetector,实现 has_tool_calldetect_and_parseparse_streaming_increment 等方法。使用 lxml(首选)/ stdlib ElementTree 解析 XML,提供 regex 兜底。
  2. 注册到 FunctionCallParser:在 function_call_parser.py 中导入 MiniCPM5Detector 并加入映射字典,支持 --tool-call-parser minicpm5
  3. 自动模板检测:在 template_detection.py 中添加 _is_minicpm5 谓词,并在 TOOL_CALL_PARSER_RULES 中注册 DetectionRule,位于宽泛规则(Mimo/Qwen)之前,避免误匹配。
  4. 单元测试:为解析器提供单调用、CDATA、多调用、非字符串类型、未知工具等测试用例;为模板检测添加规则优先级测试。
  5. CI 注册:测试文件通过 register_cpu_ci 注册到 CPU CI 任务。
文件 模块 状态 重要度
python/sglang/srt/function_call/minicpm5_detector.py 解析器核心 added 8.89
test/registered/unit/function_call/test_minicpm5_detector.py 解析器测试 added 7.48
test/registered/unit/managers/test_template_manager.py 模板管理测试 modified 5.65
python/sglang/srt/managers/template_detection.py 模板检测 modified 6.32
python/sglang/srt/function_call/function_call_parser.py 函数调用注册 modified 4.66

关键符号

MiniCPM5Detector.__init__ MiniCPM5Detector.has_tool_call MiniCPM5Detector.detect_and_parse MiniCPM5Detector._append_tool_call MiniCPM5Detector.parse_streaming_increment get_argument_type parse_arguments _is_minicpm5

关键源码片段

python/sglang/srt/managers/template_detection.py core-logic

添加 _is_minicpm5 谓词并在 TOOL_CALL_PARSER_RULES 中注册,使 auto 检测能识别 MiniCPM5

# python/sglang/srt/managers/template_detection.pydef _is_minicpm5(ctx):
    # 检测逻辑:优先检查词表中是否有专用 token
    if ctx.has_vocab("<function") and ctx.has_vocab("<param"):
        return True
    # 兜底:检查模板中是否包含 XML 函数调用模式
    return ctx.has_pattern(r"<function\s+name=") and ctx.has_pattern(r"<param\s+name=")# 在 TOOL_CALL_PARSER_RULES 元组中注册新规则
TOOL_CALL_PARSER_RULES = (
    DetectionRule(name="gemma4", value="gemma4", predicate=_is_gemma4),
    DetectionRule(name="gpt_oss", value="gpt-oss", predicate=_is_gpt_oss),
    DetectionRule(name="kimi_k2", value="kimi_k2", predicate=_is_kimi_k2),
    DetectionRule(name="minimax", value="minimax-m2", predicate=_is_minimax),
    DetectionRule(name="interns1", value="interns1", predicate=_is_interns1),
    DetectionRule(name="mistral", value="mistral", predicate=_is_mistral),
    DetectionRule(name="glm45", value="glm45", predicate=_is_glm45),
    # 新增 minicpm5 规则,位于宽泛规则(mimo、qwen)之前,避免误匹配
    DetectionRule(name="minicpm5", value="minicpm5", predicate=_is_minicpm5),
    DetectionRule(name="xml_kv_tool_call", value="glm45", predicate=_is_xml_kv_tool_call),
    DetectionRule(name="mimo", value="mimo", predicate=_is_mimo),
    DetectionRule(name="qwen", value="qwen", predicate=_is_qwen3),
    DetectionRule(name="deepseek_v3", value="deepseekv3", predicate=_is_deepseek_v3),
    DetectionRule(name="deepseek_r1", value="deepseekv3", predicate=_is_deepseek_r1),
)

评论区精华

流式 chunk 边界处理 正确性

JustinTong 指出当 bot_token '<function' 被跨 chunk 分割(如 '<' 和 'function')时,当前实体会丢失后续 chunk 中的检测,应保留部分前缀。

结论:作者在后续提交中通过 _ends_with_partial_token 保留可能性片段修复。 · 已解决

自动检测注册 设计

JustinTong 要求将 MiniCPM5 加入自动模板检测,否则用户使用 --tool-call-parser auto 无法命中。并给出了具体实施位置。

结论:作者添加了 _is_minicpm5 谓词和规则注册,并编写了优先级测试。 · 已解决

裸 except 块与异常范围 style

gemini-code-assist 建议将 parse_arguments 中的裸 except 替换为具体的(ValueError, SyntaxError, TypeError)。

结论:代码中已体现具体异常,已采纳。 · 已解决

正则 fallback 未初始化变量 正确性

JustinTong 指出当 XML 解析失败进入 regex fallback 时,has_invalid_param 未初始化导致异常。

结论:作者在修复提交中初始化该变量。 · 已解决

风险与影响

  • 流式 token 分割:若 bot_token 被跨 chunk 分割,可能丢失调用。当前代码通过 _ends_with_partial_token 保留部分前缀以缓解,但仍需更多测试覆盖。
  • 自动检测误匹配:如果有其他模型也包含 <function<param token,可能被误判为 MiniCPM5。当前规则置于宽泛规则之前,且测试验证了不被 Qwen 捕获。
  • 性能:lxml 解析比正则略重,流式场景中每个 chunk 都触发解析,可能影响首 token 延迟;但仅在处理包含工具调用的响应时触发。
  • 依赖:lxml 是可选依赖,fallback 到 stdlib ET 功能一致但 CDATA 保留受限。
  • 兼容性:不影响其他模型,但 auto 检测的优先级变化需确保回归覆盖。
  • 用户影响:MiniCPM5 用户可使用 --tool-call-parser minicpm5auto 启用工具调用,无需自行编写 parser 或 post-processing。
  • 系统影响:FunctionCallParser 增加一个映射项,模板检测增加一个规则,无性能退化。
  • 团队影响:后续需维护该 parser,并关注 lxml 依赖是否引入新问题。
  • 适用范围:仅 MiniCPM5(MiniCPM-4)V3 Schema。
流式 token 分割 自动检测误匹配 lxml 可选依赖 正则 fallback 边缘情况

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论