如何看懂 DSML 编码格式:DeepSeek 4 本地推理中特殊 token 与工具标记渲染完整指南
【免费下载链接】ds4DeepSeek 4 Flash and PRO local inference engine for Metal, CUDA and ROCm项目地址: https://gitcode.com/GitHub_Trending/ds4/ds4
DS4(DeepSeek 4 Flash and PRO local inference engine for Metal, CUDA and ROCm)是 DeepSeek 4 系列模型的本地推理引擎。当它扮演 Agent 时,模型并不输出普通文本,而是用一套名为DSML的专用标记语法来表达"调用工具"。这篇文章带你从零看懂 DSML 编码格式:|DSML|特殊 token 长什么样、工具调用如何被渲染成tool_calls块、终端里又是怎样把它们"翻译"回人类可读的结构化视图的 🧭
DSML 编码格式是什么:先认识这些特殊 token
DSML 不是 XML,而是 DeepSeek 4 词表中预训练好的特殊 token 组合。模型输出工具调用时,采样出来的其实是一串带|DSML|标记的 token,引擎再把它还原成语义化的调用。
以下是 DS4 词表中与聊天和工具渲染直接相关的特殊 token(完整说明见 MODEL_CARD.md):
| 用途 | Token |
|---|---|
| 序列开始 | <|begin▁of▁sentence|> |
| 助手轮结束 | <|end▁of▁sentence|> |
| 用户轮前缀 | <|User|> |
| 助手轮前缀 | <|Assistant|> |
| 思考开始 / 结束 | <think>/</think> |
| DSML 工具标记 | |DSML| |
关键点在于:|DSML|是一个独立 token,而<|DSML|tool_calls>这类标记是"尖括号 + 独立 token + 普通文本"拼出来的。引擎在编码聊天提示词时会把渲染好的标记串里的|DSML|直接替换成对应的特殊 token,保证模型看到的是"专用 token"而不是逐字的 BPE 碎片。
这一"输入 → 编码 → 结构化结果"的流水线思维,正是理解 DS4 渲染 DSML 的核心:采样 token 是原始数据,DSML 块是中间表示,终端里看到的结构化工具卡片是最终呈现。
工具标记的三层结构:tool_calls → invoke → parameter
模型发起一次工具调用时,DSML 块采用固定的三层嵌套结构:
<|DSML|tool_calls> <|DSML|invoke name="bash"> <|DSML|parameter name="command" string="true">ls -la</|DSML|parameter> <|DSML|parameter name="timeout" string="false">10</|DSML|parameter> </|DSML|invoke> </|DSML|tool_calls>理解它只需记住三条规则:
- 外层
<|DSML|tool_calls>包裹整个调用批次,一次可以包含多个invoke; - 中层
<|DSML|invoke name="$TOOL_NAME">声明工具名; - 内层
<|DSML|parameter name="..." string="true|false">携带参数——string="true"表示原始字符串,string="false"表示数字、布尔、数组、对象等 JSON 值。
一个容易踩坑的细节:DeepSeek 4.1 家族使用带空格的变体,如<|DSML| calls>、<|DSML| invoke、<|DSML| parameter,引擎会在运行时自动识别两种风格(见 ds4_server.c 中的两套宏定义)。此外还有"缺少首竖线"的短写法(<DSML|tool_calls>)和纯 XML 风格的容错识别,模型偶尔写歪时也能被救回来。
特殊 token 是如何被"渲染"进提示词的
提示词编码的入口在 ds4.c 的encode_chat_prompt:它按BOS → 系统提示 →<|User|>+用户文本 →<|Assistant|>+思考标记的顺序拼接 token。
- 非思考模式:助手轮以
</think>开头,直接"跳过"隐藏推理,让模型产出可见答案; - 思考模式:以
<think>开头,推理内容包在think标签内,最后以</think>收尾; - Max 模式:额外注入一段高推理预算的前缀指令。
真正把文本"翻译"成特殊 token 的是 special_token_at 函数:它遍历一张特殊 token 对照表(BOS/EOS、<|User|>、<|Assistant|>、think标签、GLM 的tool_call标记,以及|DSML|),命中时直接压入专用 token id,其余文本才走 BPE 分词。这套"先匹配特殊 token、剩余再分词"的策略保证了标记的字节级确定性。
一个值得注意的设计:只有渲染出来的聊天提示词才做这种替换。用户消息里出现的字面|DSML|必须保持为普通内容,不能变成控制 token——这是防止注入的关键防线。
Agent 端:边采样边渲染的流式解析器
终端里你看到的"工具调用卡片",是 ds4_agent.c 中 DSML 解析器逐字节"追"出来的。它是一个五状态机:
| 状态 | 含义 |
|---|---|
SEARCH | 普通文本中等待 DSML 起始标记 |
STRUCTURAL | 正在读取tool_calls/invoke结构标签 |
PARAM_VALUE | 正在累积参数值(可跨多个 token 到达) |
DONE | 一个完整invoke已解析,可执行 |
ERROR | 语法错误,生成可重试的工具错误 |
解析器是容错的识别器而非校验器:参数值可能分很多个小 token 到达,状态机就逐字节累积,直到看到闭合标签才把整个invoke作为原子单元提交。原始 DSML 文本会被抑制显示,取而代之的是结构化的工具可视化视图——这就是"渲染"的含义:采样层说的是 DSML,用户层看到的是干净的调用卡片。
如果模型在think内部写出 DSML(推理未闭合就进入工具块),引擎会直接忽略这段 DSML 并给出诊断,避免把半成品当工具执行。
服务端:把 DSML 翻译给 OpenAI / Anthropic 客户端
通过 HTTP 接入 DS4 服务(文档见 docs/SERVER.md)时,ds4_server.c 做了更复杂的事:
- 流式投影:把进行中的 DSML 块实时翻译成 OpenAI 的
tool_calls事件或 Anthropic 的tool_use内容块,客户端无需感知 DSML 的存在; - 精确回放(exact replay):服务端用随机 id 记住模型采样出的逐字节 DSML 块,下一轮请求原样回灌,确保 KV cache 与历史 token 严格对齐——这是工具多轮调用不"漂移"的关键;
- 转义规则:参数值里若真的包含
</|DSML|parameter>这类闭合串,会被写成</|DSML|parameter>,防止数据提前截断结构; - 截断修复:模型输出被 max tokens 截断导致 DSML 块不平衡时,引擎会自动补上缺失的闭合标签(见 ds4_server.c 的修复逻辑与配套测试);
- 错误回退:解析失败时回灌
Tool error: invalid DSML tool call,让模型重写一次有效调用。
上手清单:快速调试与验证 DSML 渲染
| 需求 | 做法 |
|---|---|
| 查看提示词/token/DSML 完整轨迹 | 启动时加--trace FILE(见 ds4_help.c) |
| 关闭精确 DSML 回放(排查缓存问题) | --disable-exact-dsml-tool-replay |
| 对照 GLM 模型 | GLM 使用原生tool_call语法,同样在终端实时渲染(ds4_help.c) |
| 参考渲染数据集 | gguf-tools/imatrix/dataset/rendered_prompts.txt 可看到真实 DSML 提示词样例 |
💡 一句话总结:模型说的是 DSML,
|DSML|是它独有的特殊 token;DS4 负责在编码时把标记对齐到专用 token、在采样时用状态机边收边渲染、在服务端把它无缝翻译成 OpenAI/Anthropic 客户端认识的标准格式——理解这条"编码 → 采样 → 渲染"链路,你就看懂了 DeepSeek 4 本地推理中工具调用的全部细节。
【免费下载链接】ds4DeepSeek 4 Flash and PRO local inference engine for Metal, CUDA and ROCm项目地址: https://gitcode.com/GitHub_Trending/ds4/ds4
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考