DS2API Tool Calling 终极指南:DSML 与 Canonical XML 工具块格式全解析
2026/9/15 15:22:10 网站建设 项目流程

DS2API Tool Calling 终极指南:DSML 与 Canonical XML 工具块格式全解析

【免费下载链接】ds2apiDeepSeek-Compatible Middleware Interface: A technical exploration project in Go, focusing on high-concurrency protocol adaptation. It serves as a reference implementation for converting diverse web protocols into standardized formats.项目地址: https://gitcode.com/GitHub_Trending/ds/ds2api

DS2API 是一个用 Go 编写的 DeepSeek 兼容中间件,核心价值在于高并发协议适配:它把 OpenAI Chat、Responses、Claude、Gemini 等 API 请求统一转换成标准化文本上下文,再把模型返回的工具调用(Tool Calling)解析回各协议的原生结构。其中最受关注的机制,就是本文要完整拆解的DSML / canonical XML 工具块格式——理解它,你就能明白 DS2API 如何稳定、防泄漏地执行工具调用。

1️⃣ 为什么用 XML 工具块,而不是 JSON?

传统 OpenAI 式tools调用依赖模型输出严格的 JSONtool_calls字段,但纯文本网页对话渠道没有结构化输出通道。DS2API 的解法是:让模型直接输出约定格式的 XML 文本块,由兼容层负责识别和解析

这样做有三个好处:

  • 🎯协议无关:不管客户端用哪种 API 格式,模型侧只有一套工具输出语法
  • 🛡️防误触发:代码块、行内代码里的示例 XML 会被明确忽略,不会误执行
  • 🔧强容错:对模型常见的标签拼写漂移、全角符号、漏写开标签等失误做了窄修复

语义细节的权威描述见项目文档 docs/toolcall-semantics.md,它是 Go 与 Node 两套解析实现的统一行为说明。

2️⃣ 两种可执行工具块格式

推荐格式:半角管道符 DSML 外壳

这是 Prompt 中要求模型输出的首选格式:

<|DSML|tool_calls> <|DSML|invoke name="read_file"> <|DSML|parameter name="path"><![CDATA[README.MD]]></|DSML|parameter> </|DSML|invoke> </|DSML|tool_calls>
  • <|DSML|tool_calls>:最外层 wrapper,一次响应可包含多个工具调用
  • <|DSML|invoke name="...">:每次工具调用,工具名必须放在name属性
  • <|DSML|parameter name="...">:参数节点,字符串值统一用<![CDATA[...]]>包裹

兼容格式:旧式 canonical XML

兼容层仍接受不带协议前缀的写法(参考测试夹具 tests/compat/fixtures/toolcalls/canonical_tool_call.json):

<tool_calls> <invoke name="read_file"> <parameter name="path"><![CDATA[README.MD]]></parameter> </invoke> </tool_calls>

⚠️ 注意:DSML 只是"外壳别名",进入解析器前会归一化成本地标签名tool_calls/invoke/parameter,内部始终按现有 XML 解析语义处理,并不是原生 DSML 全链路实现。

3️⃣ 工具块格式规则清单(新手速查表)

规则说明
必须有 wrapper外层<\|DSML\|tool_calls><tool_calls>缺一不可
调用放 invoke 内每个调用必须在invoke标签内,工具名放name属性
参数用 parameter每个顶层参数一个parameter name="..."节点
字符串用 CDATA代码、路径、prompt 等一律<![CDATA[...]]>包裹
数字/布尔保持纯文本123truenull直接写,会自动还原为对应 JSON 类型
数组用重复 item<item>...</item>重复子节点会被还原为数组
禁止混用标签同一工具块内不要 DSML 与旧式 XML 混搭
不要空参数缺参数应询问用户,而非输出空占位

完整指令模板由 internal/toolcall/tool_prompt.go 中的BuildToolCallInstructions生成,OpenAI / Claude / Gemini 三种适配器共用同一份规则、反例与正例。

4️⃣ 流式防泄漏:为什么工具块不会被"泄漏"到正文

DS2API 对流式(SSE)场景做了专门的"筛分器"(stream sieve)设计,源码见 internal/toolstream/(Go)与 internal/js/helpers/stream-tool-sieve/(Node),核心行为:

  • ✅ 已识别成功的工具调用不会回流为普通文本,客户端收到的delta.tool_calls是结构化增量
  • ✅ fenced 代码块(```~~~)、Markdown 行内代码中的 XML 示例始终按普通文本处理,不会误执行
  • ✅ 支持嵌套围栏(4 反引号嵌套 3 反引号)与 CDATA 内围栏保护
  • ✅ 长文本参数(如command/content)内的 CDATA 即使包含</parameter>这类片段,也不会被误判为外层结束
  • ✅ 若 wrapper 完整但内部形态不合法(如用了<param>),整块作为普通文本释放——不吞、不半漏

这一层设计直接解决了社区常见的"工具调用输出成文本、没被执行"的痛点。

5️⃣ 容错修复:模型写"歪了"怎么办

解析链路(internal/toolcall/toolcalls_parse.go)内置了一组窄修复,专门应对真实模型的失误:

  • 漏写开标签:只有 closing wrapper 存在且结构证据充分时,才补回缺失的 opening wrapper
  • 符号漂移:全角感叹号、顿号、CJK 尖括号〈〉、弯引号、重复的<、Unicode 空白等,在固定标签名上会被折回 ASCII 语义
  • 尾部分隔符<|DSML|tool_calls|这类标签后多出的非结构性分隔符会被归一化
  • 未闭合 CDATA:流式阶段保守缓冲,收尾阶段再做窄修复

但修复是有边界的:参数正文、普通聊天文本、非工具壳 XML不会被广义 Unicode 归一化;tool_calls_extra这类相似但非固定标签名仍按普通文本透传。

6️⃣ 解析结果与参数类型还原

ParseToolCallsDetailed返回结构(internal/toolcall/toolcalls_parse.go):

  • calls:解析出的工具调用列表(name+input
  • sawToolCallSyntax:检测到工具块语法或命中可修复形态时为true
  • 显式空字符串参数会保留,是否拒绝由工具执行侧 / 客户端 schema 校验决定

参数值还原规则:

参数写法还原结果
<parameter name="n">123</parameter>数字123
<parameter name="arr"><![CDATA[[1,2]]]></parameter>数组[1,2](合法 JSON 字面量)
多个<item>子节点JSON 数组
CDATA 内完整 XML 结构object / array(content/command等原文字段受保护)
单个行内标签如<b>urgent</b>保留原始字符串

7️⃣ 常见问题排查清单

  1. "工具调用输出成文本、未执行"→ 先检查模型输出是否为推荐的 DSML 外壳,或兼容的 canonical XML;旧式 `

【免费下载链接】ds2apiDeepSeek-Compatible Middleware Interface: A technical exploration project in Go, focusing on high-concurrency protocol adaptation. It serves as a reference implementation for converting diverse web protocols into standardized formats.项目地址: https://gitcode.com/GitHub_Trending/ds/ds2api

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询