有人问过这样一个问题:
“为啥非要自创协议而不是沿用 MCP 呢?自然语言是各个模型最通用的语言,语言理解力达标,就能用对格式,从而规避 MCP 时期让模型直接输出 JSON 的不稳定。况且现在模型训练 Function Calling 的方法也证实了自然语言对 JSON 是更优越的。比如 DS,内部的工具表示是 DSML 而不是 JSON。”
这段话里其实藏着四个关键问题,值得逐一拆开:
- 自然语言 和 严格 JSON,哪个更适合模型来表达工具调用?
- MCP 到底是干什么的?它和 Function Calling 是什么关系?
- DeepSeek 为什么用 DSML 而不是 JSON?
- 我们到底该不该“自创协议”?
本文会把这些问题讲清楚,并配上具体示例。
一、先分清两层:模型侧 vs 协议侧
很多人把 MCP 和 Function Calling 混为一谈,这是理解偏差的起点。
| 层级 | 名称 | 解决什么问题 | 典型形式 |
|---|---|---|---|
| 模型侧 | Function Calling / Tool Use | 模型如何表达“我要调用哪个工具、参数是什么” | JSON Schema + 模型输出结构化参数 |
| 协议侧 | MCP(Model Context Protocol) | 工具如何被发现、如何被不同客户端复用、鉴权怎么做 | JSON-RPC 2.0 |
一个简单的类比:
- Function Calling 是“模型说我想点一杯咖啡(大杯、少糖)”。
- MCP 是“咖啡店的标准菜单系统,任何 App 都能连上去点单,而不用每家 App 都单独对接一次”。
MCP 并没有取代模型的 Function Calling,它是在 Function Calling 之上加了一层标准化协议。模型仍然需要决定调用什么,只是工具的定义和执行被抽到了独立的 Server 里。
二、为什么模型直接吐 JSON 会不稳定?
模型是在海量自然语言上预训练的,对自然语言的理解和生成能力远强于严格的结构化格式。
常见失败模式
当要求模型输出严格 JSON 时,经常出现:
- 多写或少写大括号、引号
- 字段名幻觉(
location写成city) - 类型错误(数字写成字符串)
- 在 JSON 前后夹杂自然语言
- 长参数时格式崩溃
这些错误在早期 Function Calling 中非常常见,尤其是较小模型或复杂参数场景。
研究证据:Natural Language Tools(NLT)
2025 年有一篇重要论文 Natural Language Tools: A Natural Language Approach to Tool Calling In Large Language Agents(arXiv:2510.14453),提出用自然语言替代严格 JSON 做工具选择。
核心做法是把工具选择解耦:
- 让模型对每个工具回答 YES / NO,而不是生成完整 JSON。
- 工具描述用自然语言。
实验结果(跨 10 个模型、6400 次试验):
- 准确率从 69.1% → 87.5%(+18.4 个百分点)
- 方差下降约 70%
- Token 消耗降低约 31%
后续复现研究(2026)在 14 个模型、8560 次试验中也得到了类似结论:NLT 平均提升 14.9 个百分点,关键错误减少 93%。
结论:对很多模型来说,自然语言描述 + 简单决策 比强制生成复杂 JSON 更稳定、更准确。
三、DeepSeek 的选择:DSML
DeepSeek 没有选择让模型直接生成标准 JSON,而是设计了 DSML(DeepSeek Markup Language)。
DSML 示例
模型内部生成的工具调用大致长这样:
<|DSML|tool_calls>
<|DSML|invoke name="read_file">
<|DSML|parameter name="path" string="true">src/app.ts</|DSML|parameter>
</|DSML|invoke>
</|DSML|tool_calls>或者一个更完整的搜索示例:
<|DSML|calls>
<|DSML|invoke name="search">
<|DSML|parameter name="queries" string="true">小柴胡冲剂 布洛芬 相互作用 一起吃</|DSML|parameter>
</|DSML|invoke>
</|DSML|calls>服务端再转换成标准格式
推理框架 / API Server 会把 DSML 解析成 OpenAI 兼容的 tool_calls:
{
"tool_calls": [{
"type": "function",
"function": {
"name": "read_file",
"arguments": "{\"path\":\"src/app.ts\"}"
}
}]
}为什么这样设计?
- XML 风格的标记对模型来说更容易稳定生成(标签有明确起止)。
- 参数值直接写在标签之间,避免复杂的 JSON 转义。
- 服务端负责解析和校验,模型只需“说清楚意图”。
这正是“自然语言(或模型友好格式)优先”的实践体现。
四、标准 Function Calling 示例(对比)
OpenAI / 兼容接口风格
工具定义:
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather of a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
}
},
"required": ["location"]
}
}
}模型输出(理想情况):
{
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\": \"Hangzhou, Zhejiang\"}"
}
}]
}Claude(Anthropic)风格
工具定义使用 input_schema:
{
"name": "get_weather",
"description": "Get the current weather for a given city.",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}模型返回的是 tool_use content block,而不是 tool_calls 数组。
五、MCP 协议示例(协议层)
MCP 使用 JSON-RPC 2.0。下面是典型的工具发现与调用流程。
1. 列出工具(tools/list)
请求:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}响应:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "get_weather",
"description": "Get current weather information for a location",
"inputSchema": {
"type": "object",
"properties": {
"location": {"type": "string"}
},
"required": ["location"]
}
}
]
}
}2. 调用工具(tools/call)
请求:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {
"location": "Hangzhou"
}
}
}响应:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{
"type": "text",
"text": "Current weather in Hangzhou: 24°C, Cloudy"
}
],
"isError": false
}
}注意:MCP 客户端拿到工具定义后,仍然是通过模型的 Function Calling 能力把这些工具交给模型,再把模型的决策转成 tools/call 请求发给 Server。
六、四种思路对比
| 方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 严格 JSON Function Calling | 结构清晰、易解析、生态成熟 | 模型容易格式错误,Token 开销大 | 工具少、参数简单、模型强(有严格模式) |
| DSML / 模型友好标记 | 生成更稳定,模型友好 | 需要专用解析器 | DeepSeek 等自研模型 |
| Natural Language Tools | 准确率高、方差小、Token 少 | 参数复杂时表达能力受限 | 工具选择为主、参数简单的场景 |
| MCP | 工具一次编写、多客户端复用 | 有协议开销,Token 可能更多 | 需要跨应用、跨团队复用工具时 |
七、实践建议
-
模型侧优先考虑“模型友好”
如果是自研模型或开源模型,可以像 DeepSeek 一样设计更易生成的格式(DSML、XML、甚至自然语言 + 解析),而不是强行让模型当 JSON 生成器。
-
协议侧按需选择 MCP
- 只给自己的产品用 → 直接 Function Calling 就够了。
- 要给多个客户端(Claude Desktop、Cursor、自己的 Agent)共用工具 → MCP 很有价值。
- 工具数量非常多时,注意 Token 成本,有人开始转向 CLI-first 方案。
-
两者可以共存
MCP Server 对外暴露标准协议,内部执行时仍然可以用任何模型友好的方式让模型决策。
-
生产中建议加一层校验与修复
即使模型很强,也建议对工具调用结果做 schema 校验、重试或 fallback。
八、总结
自然语言(或模型友好格式)是模型最擅长的表达方式,强制严格 JSON 会带来不必要的不稳定。
DeepSeek 用 DSML、NLT 论文的实验,都在验证这一点。
但 MCP 解决的是另一层问题——工具的标准化发现与复用,它和“模型怎么表达工具调用”并不矛盾。
正确的理解是:
- 模型怎么说话 → 尽量贴合模型优势(自然语言 / DSML 等)。
- 工具怎么被连接 → 需要协议层(MCP 或其他方案)来做标准化。
不要把“模型侧的格式选择”和“协议侧的生态标准”混为一谈。两者解决的是不同问题,也都可以根据实际场景灵活选择。
原文 https://blog.csdn.net/2301_79518550/article/details/169368587
跳到内容