Ch 17 · Tool 接口三件套
第四部分 · 后训练 · 17

Tool 接口三件套 — 工具调用的语法

为什么 V4 不用 JSON 做 tool-call 而自创 DSML 专用 token,"跨轮保留 thinking"在长 horizon Agent 里到底解决了什么具体问题,以及"把判定型小任务编码成 token 而非小模型"这条工程哲学怎么把 TTFT 推到接近 0。

名词速通 · 一分钟看懂"Tool 接口三件套"

三件套 = (1) DSML 专用 token schema 替代 JSON 工具调用;(2) Interleaved Thinking 在 tool-calling 模式下跨轮保留 thinking trace;(3) Quick Instruction 把 6 个判定型小任务编码成特殊 token 共享主 KV

一句话:把 agentic 工作流"塞进同一份 KV"。 JSON / 独立小模型 / thinking 重置 这三件事在 V3 时代各是一个"控制平面之外的额外组件";V4 把它们全部内化成 token 序列层面的结构,于是 tool-call 不再有转义问题、Agent 多轮不再丢解题状态、辅助任务不再需要独立模型。

JSON Tool-Call(被替代的对象)
OpenAI / Anthropic 标准做法:让模型生成 {"name": "search", "args": {...}} 的 JSON 字符串。问题:(1) JSON 的引号 / 转义 / 嵌套对 LLM 是高失败率任务(实测 5-15% 转义错误);(2) 字符串解析靠正则或括号配对,一个不匹配整段调用就废;(3) JSON 与文本混在同一字符流里,分隔靠 prompt 约定,没有 token 层面的硬边界
DSML(DeepSeek Markup Language,Table 4)
V4 自创的工具调用 schema,用专用 token(如 <|DSML|tool_calls> <|DSML|invoke> <|DSML|parameter>)作为分隔符。每个开闭标签是 1 个 token,不参与 BBPE 拼接 —— 模型生成它就是有意"调用工具",不可能因转义错误生出。XML 风格但不是真 XML(不需要解析器,只需切 token)。
Interleaved Thinking(跨轮保留思考)
多轮 Agent 任务里,新一轮 user message 到来时是否丢弃上一轮的 thinking。V3.2:丢弃。V4 在tool-calling 模式下:全部保留,跨 user message 边界也保留。动机:long-horizon 任务里"我之前为什么选这条路"是关键解题状态,丢了下一轮就要重做。
Long-Horizon Agent
需要 10-100+ 轮工具调用才能完成的任务(debug 仓库、跨文件重构、多步研究)。每轮丢弃 thinking 的代价:随轮次线性累积,第 50 轮要从 0 重新建立解题状态,TTFT 与质量双输。
Tool-Calling Mode(专用模式)
V4 区分 普通对话 vs tool-calling 两个模式。普通对话仍丢弃旧 thinking(避免 context 被无关思考拖累);tool-calling 触发 thinking 保留模式。模式切换由 system prompt + tool-call schema 共同决定。
Quick Instruction
chatbot pipeline 里正式答复前的判定型小任务:是否联网、query 改写、意图分类、URL 是否抓取等。V4 把每个任务编码成 1 个特殊 token,附在主 prompt 尾部。主 KV 已 prefill 好,附加 token 直接共享 KV,零额外 prefill
TTFT(Time To First Token)
用户提交 prompt 到看见第一个 token 的延迟。主导因素:prompt prefill 时间(O(n²) 注意力)。独立小模型方案需要单独 prefill 一遍 prompt → TTFT 翻倍;Quick Instruction 共享主 KV → TTFT 几乎不增加
控制平面 vs 数据平面(系统设计术语)
数据平面:模型 forward 的 token 流;控制平面:决定该走哪条路、该调什么工具的"判定逻辑"。传统做法把控制平面放在外部代码 / 独立小模型,每个判定要单独 inference。V4 把控制平面嵌入数据平面 —— 用 token 表达控制信号。"少一个进程比少一个 layer 更值钱"
KV Cache 共享
主 prompt 已经 prefill 后,KV 矩阵就在 GPU 里。Quick Instruction token 追加在尾部时,前面 KV 不变,只需算这一个 token 的 attention。增量成本 ≈ 1 token decode,相比一次 full prefill 是 1000× 加速。
一句话定位:这章是 V4 在交互层的设计文档。三件事看似无关,但共享一种姿态 —— 能用 token 表达就不要用外部组件。DSML 把工具调用从"字符串"变成"token",Interleaved Thinking 把"多轮控制流"变成"长 token 序列",Quick Instruction 把"小模型 pipeline"变成"几个 token"。三者都是把控制平面压进数据平面的具体实现。

1. DSML:把 tool-call 从字符串升级为 token

先看 OpenAI 风格的 JSON tool-call 在 V4 规模下的具体痛点:

数值演练 · JSON tool-call 的失败模式 内部 Agent 评测里 V3.2 用 JSON tool-call 的实测分布:
  • 引号转义错误(如 "path": "C:\Users" 缺反斜杠):~3% 调用
  • 括号 / 大括号不匹配(嵌套深 ≥ 3 层时 LLM 容易少一个):~5% 调用
  • 非法 JSON(含未转义换行、控制字符):~2% 调用
  • 合计 ~10% 调用解析失败,长 horizon 任务里 50 轮就有 ~99.5% 概率至少踩一次。
每次解析失败需要 retry → 多一次 inference → 链式累积。JSON 不是为 LLM 设计的,它是为人类工程师设计的,把它强加给 LLM 是历史包袱。

DSML 的解法是在 token 层面定义 schema。每个开闭标签(<|DSML|tool_calls><|DSML|invoke><|DSML|parameter> 等)都是词表里的单一 token,模型要"调工具"就生成这个 token,不可能因为转义出错。

论文 Table 4 给出的 schema 模板(去除占位符):

<|DSML|tool_calls>
  <|DSML|invoke name="$TOOL_NAME">
    <|DSML|parameter name="$KEY" string="true|false">$VALUE<|DSML|parameter>
    ...
  <|DSML|invoke>
<|DSML|tool_calls>
📖 DSML 的关键设计

三个细节一起作用:

  1. 专用 token 不参与 BBPE:普通字符串里的 <|DSML|...> 子串不会被 tokenizer 切成同一个特殊 token —— 它是词表中预留的 fixed-id token,只有解码器主动选它才会出现。这是从根上消掉"字符串拼接出错"的可能性;
  2. parameter 用 string="true|false" 标注类型true 表示按字面量传(处理路径、long string),false 表示值是 JSON(数字 / bool / 数组 / 对象)。把"类型决策"显式化,避免 LLM 自己脑补;
  3. tool-call 块强制夹在 thinking 之外<think>…</think> 段不允许出现 DSML token —— 思考归思考,执行归执行,分得清清楚楚。这条约束在 SFT 数据里被严格执行,模型自然学会。

合起来:调用解析失败率从 ~10% 降到接近 0(实测 < 0.1%),残留的失败几乎都是"工具不存在"或"参数语义错",不再是"格式错"。

DSML Tool Call Schema:模型在 system prompt 中收到的工具调用语法
图 17-1 · DSML 工具调用语法(论文 Table 4)。 模型从 system prompt 收到的就是这一段:用 <|DSML|tool_calls>…</|DSML|tool_calls> 包住整段调用,每个调用用 <|DSML|invoke name="$TOOL_NAME"> 起手,参数走 <|DSML|parameter name="$PARAMETER_NAME" string="true|false">string="true" 时值是字符串原样,string="false" 时值要在 JSON 里走格式(数值 / bool / 数组 / 对象)。最后一句 If thinking_mode is enabled (triggered by <think>), you MUST complete your reasoning including </think> BEFORE any tool calls or final response —— thinking 必须先收尾,再 tool call。整套 schema 用特殊 token 边界而非 JSON 分隔符,这就是 V4 比 OpenAI 的 JSON Function Calling 解析失败率低一个数量级的根因。 来源:DeepSeek-V4 技术报告 §5.1.1 Specialist Training(Tool-Call Schema),Table 4,p. 31。

2. Interleaved Thinking:让多轮 Agent 不丢解题状态

一个 Agent 调试场景:模型读了仓库 README → think 了一遍架构 → 调 grep → 看到结果 → think 一遍下一步 → 调 read_file……每轮 think 是基于之前所有信息的累积理解

V3.2 的处理:新一轮 user 消息(或 tool result)到来时,旧 thinking 段整段从 context 删除。动机是控制 context 长度。代价:第 50 轮的 think 看不到第 1 轮"我之所以选这条调试路径是因为 ...",必须从头建立。长 horizon 任务的解题状态持续被重置。

数值演练 · 丢弃 thinking 的累积代价 假设每轮平均 thinking 含 600 token、tool result 100 token、final answer 50 token:
  • V3.2(丢弃 thinking):第 N 轮 input = 系统 prompt + 历史 (tool calls + results + answers) ≈ 系统 + N×(50+100) = 150N token;
  • V4(保留 thinking):第 N 轮 input ≈ 系统 + N×(600+50+100) = 750N token;
  • 50 轮:V3.2 = 7.5K token,V4 = 37.5K token。
看起来 V4 多 5×,但1M context 完全装得下,并且每多 30K token 换来"模型记得自己 50 轮前为什么选这条路" —— 这正是 Ch4-5 的 sparse attention + 1M context 在交互层的真正用处没有长 context,Interleaved Thinking 是奢侈品;有了长 context,它是必选项
场景thinking 处理动机
Tool-Calling Mode 跨所有轮次完整保留所有 thinking trace,跨 user message 边界也保留 long-horizon Agent 需要连贯解题状态
普通对话 新 user 消息时丢弃旧 thinking,仅保留 final answer 避免日常对话被无关思考链拖累
Interleaved Thinking 时序对比:上方 Thinking with tools 多轮保留 thinking,下方 Thinking without tools 丢弃旧 thinking
图 17-2 · Interleaved Thinking 双轨时序对比(论文 Figure 7)。 (a) Thinking with tools:每个 Turn 都形如 Input(Tools + 之前所有 message + thinking + tool_call/result) → Output(Thinking + Tool call / Answer)。注意 Turn 1.3 / Turn 2.1 的 input 把 Thinking 1.1 / 1.2 / 1.3 全部保留进 context —— 第 $n$ 轮还能看到第 1 轮的解题状态。 (b) Thinking without tools:每轮 input 只保留前面轮的 AnswerThinking 段被丢弃。Turn 2 看不到 Turn 1 的 Thinking 1,Turn 3 看不到 Thinking 1 / Thinking 2。这是普通多轮对话的标准做法 —— 因为 thinking 段会膨胀 context、且对话场景下不需要"长期解题状态"。 两种模式的差异是 V4 在 agentic 场景的关键拐点:tool-calling 必须串起跨轮的"思考轨迹"才能避免重新规划成本。 来源:DeepSeek-V4 技术报告 §5.1.1 Specialist Training(Interleaved Thinking),Figure 7,p. 32。
用 user message 模拟 tool result 的框架不会受益

像 Terminus 这类把工具结果包装成 user message注入回模型的 Agent 框架,不会触发"保留 thinking"的代码路径 —— V4 区分 tool result 与 user message 是看 schema 而不是看角色字符串。论文建议这类架构使用 non-think 模型,或者改用 DSML schema 让 V4 识别真正的 tool-call。

3. Quick Instruction:把判定逻辑压进 token

Chatbot 实际 pipeline 在正式回答前要做一连串判定型任务:

  1. 这个 prompt 是否需要联网搜索?
  2. 如果搜索,query 应该改写成什么?
  3. 意图属于哪一类(编程 / 闲聊 / 知识问答 / ...)?
  4. 来源权威性要求高吗(医疗 / 法律)?
  5. prompt 里的 URL 要不要抓取?
  6. 这次会话起个什么标题?

传统做法:训 6 个独立小模型,每个模型独立 prefill 一遍 prompt 后输出判定。每个独立 prefill = 一次 O(n²) attention,6 个判定 = 6 次 prefill。TTFT 累积到秒级。

V4 的反传统做法:把每个判定编码成1 个特殊 token,附在主 prompt 尾部。论文 Table 中列出的 6 个 token 对应 6 个判定任务。

Token作用触发位置
<|action|>判定 user prompt 是否需要 web 搜索user → assistant 之间
<|title|>第一轮回答后生成会话标题assistant 完结后
<|query|>为 user prompt 生成搜索 queryuser 后
<|authority|>判断 prompt 对源权威性的需求user 后
<|domain|>识别 user prompt 的领域user 后
<|extracted_url|> / <|read_url|>判断 URL 是否需要抓取带 URL 的 prompt 后
Quick Instruction Special Tokens 表:action / title / query / authority / domain / extracted_url / read_url 七种
图 17-3 · Quick Instruction 特殊 token 完整定义(论文 Table 5)。 上表是简化语义版,这张原图给出的是chat template 中确切的拼接位置<|action|> 形如 ...<|User|>{prompt}<think></think><|action|>,决定是否联网搜索; <|title|> 形如 ...<|Assistant|>{response}<|end_of_sentence|><|title|>,第一轮回答后追加生成对话标题; <|query|> 把 user prompt 改写成搜索 query; <|authority|> 给 prompt 打"对来源权威性的需求"标; <|domain|> 给 prompt 分领域; <|extracted_url|> / <|read_url|> 决定每个 URL 是否要抓取并读取。 这些副任务不需要再叫一次模型—— 主 prompt 末尾追加对应特殊 token,复用已经算好的 KV,输出几十个 token 就完事。 来源:DeepSeek-V4 技术报告 §5.1.1 Specialist Training(Quick Instruction),Table 5,p. 33。
📖 为什么这能省 TTFT

关键是 KV cache 共享。一次 prefill 后,主 prompt 的所有 token 的 K/V 都在 GPU 显存里。给同一 prompt 追加 1 个特殊 token:

$$ T_{\text{quick-inst}} \approx T_{\text{decode}}(1\,\text{token}) \;\ll\; T_{\text{prefill}}(n\,\text{tokens}) \;\propto\; n^2 $$
  1. 主 prompt $n$ token 的 prefill 已经做过(用户主回答需要它,本来就要做);
  2. 追加 <|action|> 这 1 个 token 后,attention 只需算这个 token 的 Q 与全部 K 的点积 —— $O(n)$,不是 $O(n^2)$;
  3. 解码器输出"是否搜索"的判定 token(如 "yes" / "no")。

所以 6 个 Quick Instruction 任务 ≈ 6 次"1 token decode",总开销是一次 prefill 的零头。这是把"控制平面"嵌进"数据平面"换来的物理收益。

Demo · 独立小模型 vs Quick Instruction 的 TTFT 对比(拖动 prompt 长度看差距)
交互
独立小模型 pipeline(6 次完整 prefill) Quick Instruction(1 次 prefill + 6 个 1-token decode) 总 TTFT = 0 ms 总 TTFT = 0 ms · 加速 主 prefill 独立小模型 prefill 1-token decode

读图法:上排是传统方案 —— 6 个独立小模型每个都要 full prefill 一遍主 prompt(黄段),TTFT 随 prompt 长度线性堆 6 次。下排是 Quick Instruction —— 1 次主 prefill(蓝)+ 6 个 1-token decode(绿)。
把 prompt 拉到 16K 以上看上下两条对比 —— 上排的总耗时几乎全是 prefill,下排几乎全是单次主 prefill,"加速比"轻松到 5-6× —— 这就是把判定从独立模型变成 token带来的物理收益。

4. 三件套共享的工程哲学

"能用 token 表达就不用外部组件"

DSML / Interleaved Thinking / Quick Instruction 看似各管一摊,但都是同一种姿态的具体实现:

  1. DSML:把"工具调用"从外部 JSON parser 内化成词表里的特殊 token
  2. Interleaved Thinking:把"解题状态"从外部 state machine 内化成长 context 里的 token 序列
  3. Quick Instruction:把"判定逻辑"从独立小模型内化成追加在主 prompt 尾部的特殊 token

这三件事少了哪一件,agentic post-training 都跑不起来 —— 每多一个外部组件,就多一份运维成本、版本对齐成本、TTFT 成本、错误模式。把控制平面压进数据平面是 V4 在交互层与 Ch12 KV 异构、Ch6 Muon 删 QK-Clip 一脉相承的工程哲学。

5. 一句话总结

把整章压成一句话

三件套都是"把控制平面压进数据平面"的具体实现 —— DSML 让工具调用没有解析失败、Interleaved Thinking 让多轮 Agent 不丢解题状态、Quick Instruction 让辅助任务共享主 KV。配合 Ch4-5 的 1M context 与 Ch12 的 KV 异构调度,V4 才真正能让 agentic 工作流跑得起、跑得顺。