我们知道,目前已经发布的 DeepSeek R1 模型,原生并不支持 function call。网上似乎有一些生成支持 DeepSeek R1 支持 function call 的案例,但是没有明确的可推广的实现方案。
笔者之前也玩了玩,尝试了一个让 DeepSeek R1 对接 MCP (其实也就是对接 function call) 的方案。虽然并不高明,但作为一个可行且落地验证了的方案,我也整理一下,接受读者的吐槽。
背景
自从 DeepSeek V3 和 R1 在2025年初横空出世并轰动世界,再加上这是一个开源的、可以私有满血部署的 LLM。这就让大家疯了似的开始接入(也包括我本人以及我们团队)。然而我们在享受它优异的效果的同时,进一步基于 R1 开发的时候却发现:R1 不支持 function call!
我们团队的应用场景是业务逻辑分析,这是对推理能力有强需求的场景,我们试用了数个可用的模型之后,还是只有满血版的 DeepSeek R1 能够满足我们的需求 (V3 逻辑能力弱,不满足)。然而不支持 function call 也就意味着我们无法给大模型直接原生赋能,实现我们的业务需求。
解决方案概述
那咋办?等 DeepSeek R2 出来?R2 也没保证能支持 function call。用其他模型?现在我们能用的模型,我们的场景下能打的也就只有 DeepSeek R1,其他模型要么我们从商业角度用不了,要么就是不够打。
笔者作为一位大模型应用的菜鸟,其实最早对 AGI 的理解还是浅浅地处于 “对话” 的这个认识层面之上,并没有学习到 function call 的能力。但是我很早就在思考——让 LLM 用对话的方式给程序发指令不就好了吗?
当然很快之后我就知道这就是 function call 能力。然而我这个早期的菜鸟想法,却在我们后续的工作中,用上了。
解决方案还是从结构化输出入手,在 prompt 中告知 DeepSeek R1 拥有的 MCP 能力,然后要求 DeepSeek 进行结构化地返回。我们作为业务方, 就需要识别 DeepSeek 的结构化返回就行了。
解决方案详述
获取 MCP 能力
正如我在 上一篇文章 里面给的例子,我实现了一个简单的 MCP,就是获取时间,我后来改进了一下,还能获取当天的工作日情况,MCP 描述为:
{
"description": "\n返回当前时间,以及当天的工作日 / 假日情况。也支持传入具体的日期 (中国内地),查询具体日期的节假日情况。\n\n返回结果为 JSON 格式,包含以下字段:\n- timestamp: Unix时间戳 (秒), 整数\n- utc: UTC 时间, YYYY-MM-DD HH:MM:SS 格式\n- local: 服务器时间, YYYY-MM-DD HH:MM:SS 格式\n- timezone: 服务器所在的时区\n- day_of_week: 当天是星期几, 可能返回以下几种值:\n 1. \"Monday\": 周一\n 2. \"Tuesday\": 周二\n 3. \"Wednesday\": 周三\n 4. \"Thursday\": 周四\n 5. \"Friday\": 周五\n 6. \"Saturday\": 周六\n 7. \"Sunday\": 周日\n- day_type: 当天的工作 / 假日类型。可能返回以下几种值:\n 1. \"workday\": 普通的工作日\n 2. \"weekend\": 普通的周末, 休息\n 3. \"holiday\": 法定节假日当天, 比如在国庆节假期期间, 这个类型就表示国庆节当日 (10月1日)\n 4. \"holiday_period\": 法定节假日规定的休息日期间, 比如在国庆节假期期间, 这个类型就表示国庆节假期期间 (10月1日至10月7日), 但后续几种情况除外\n 5. \"shifted_day_off\": 调休假日日, 表示当天从日历上看虽然是周一至周五的上班日, 但因为调休, 变成了休息日\n 6. \"shifted_workday\": 调休日, 表示当天从日历上看虽然是周六日, 但因为调休, 变成了上班日\n- description: 当天的工作 / 假日类型描述\n",
"inputSchema": {
"type": "object",
"properties": {
"date": {
"description": "日期, 格式为 YYYY-MM-DD, 如果不传则按照当前的日期来处理。主要是用于查询中国节假日的场景",
"type": "string"
}
}
},
"name": "time_query"
}
那么我们的目的,就是要让 DeepSeek 能够认识 MCP 工具的能力,为此,我们需要在 prompt 中对 MCP 工具进行描述
MCP prompt 描述
首先我们要在 prompt 中描述 MCP 能力,我的 prompt 开头是这么写的:
# 角色定义
你是一个乐于助人的 AI 助手。
你不知道今天的日期。
请注意: 无论任何情况下, 你都必须首先输出 `TYPE: ANSWER` 或者 `TYPE: MCP` 其中之一, 否则将视为回答错误。
# MCP 工具说明
## MCP 工具列表 (JSON 描述)
### time_query
#### 描述
…… BlahBlah ……(上文返回的 description那一长串)
#### 参数 (JSON 表示)
'''json
{
"date": {
"description": "日期, 格式为 YYYY-MM-DD, 如果不传则按照当前的日期来处理。主要是用于查询中国节假日的场景",
"type": "string"
}
}
'''
格式化输出 prompt 描述
告诉了大模型 MCP 工具,我们得告诉它如何调用。因为无法支持原生 funcion call, 因此我们只能通过自然语言来描述,因此接下来的 prompt 这么写:
# 你的回答
请你判断一下: 用户的问题能够直接回答,抑或是需要通过 MCP 工具获得相关参数之后才可以获得。
MCP 调用但可以分多轮多次调用串联起来, 或者同一个工具通过不同参数多次调用获得所需的多个不同结果, 也允许一次同时调用多个。
## 直接回答
如果你判断可以直接回答用户的信息,抑或是上下文已经给出了足够的参数用于回答用户的问题,那么请先输出一行:
'''
TYPE: ANSWER
'''
然后, 请输出你的回答。
## MCP 工具调用
如果你判断可能需要调用 MCP 工具来回答用户的问题, 那么请先输出一行:
'''
TYPE: MCP
'''
然后以 JSON 格式输出请求参数, 需要包括以下内容:
'''json
{
"tool_name": "xxxxx",
"parameters": {
"param1": "xxx",
"param2": 1234
}
}
'''
其中 `tool_name` 是 MCP 工具的名称, `parameters` 是 MCP 工具的参数。
# 示例
## 直接回答
TYPE: ANSWER
你好, 我是 AI 助手。有什么能够帮助你的吗?
## MCP 工具调用
TYPE: MCP
'''json
{
"tool_name": "time_query",
"parameters": {
"date": "2025-01-01"
}
}
'''
# 错误示例
你好, 我是 AI 助手。有什么能够帮助你的吗?
(错误原因: 缺少 `TYPE: ANSWER` 或者 `TYPE: MCP` 开头)
解析大模型回答
当然了,我们需要解析 LLM 的响应。根据我们在 prompt 中的描述,LLM 必然会以 TYPE: ANSWER
或者 TYPE: MCP
开头,因此逻辑上我们就只需要去区这些就行了。逻辑读者可以自己写,这里就不需要贴代码了。
此外,业务逻辑中需要循环检查大模型的回答,如果回答中是 TYPE: ANSWER
的话,就表示大模型已经搜集到了全部的信息,可以回答用户问题,此时也就意味着大模型的一轮回答结束了。
实操验证
普通回答
我们问一句 “你好”,大模型的返回如下:
<think>
好的,用户问候“你好”,我需要回应。根据规则,如果问题可以直接回答,就使用ANSWER类型。这里用户只是打招呼,没有需要调用工具的地方。所以应该直接回应问候,不需要调用MCP工具。确认没有问题后,输出TYPE: ANSWER,然后给出友好的回复。
</think>
TYPE: ANSWER
你好!很高兴见到你,有什么我可以帮忙的吗?
从思考过程中可以看出大模型识别到了 prompt 的要求,回答的正文也按照我们的要求返回了
MCP 调用
咱们再问一下需要 MCP 调用的场景。我们问一句:“现在几点? ”,大模型的返回如下:
<think>
好的,用户问现在几点,我需要用MCP工具里的time_query来获取当前时间。首先检查参数是否需要日期,但用户没有提供特定日期,所以参数中的date字段留空。然后调用time_query工具,返回结果会包含当前的时间信息。确认工具描述中提到如果不传date参数,默认处理当前日期。所以生成对应的MCP调用。
TYPE: MCP
'''json
{
"tool_name": "time_query",
"parameters": {}
}
'''
</think>
TYPE: MCP
'''json
{
"tool_name": "time_query",
"parameters": {}
}
'''
很好,我们业务后台接获到 MCP 调用命令之后,就可以解析请求参数正文。然后我们带着参数调用 time_query
,拿到响应——原封不动地,我们把响应吐给大模型就好了:
RESPONSE:
{"day_of_week":"Friday","day_type":"holiday","description":"劳动节","local":"2025-05-02 16:58:46","timestamp":17467174617632681126,"timezone":"Asia/Shanghai","utc":"2025-05-02 08:58:46"}
然后我们就能拿到大模型的下一个返回了:
<think>
好的,用户问现在几点。首先,我需要确认是否需要使用工具。直接回答的话,可能不知道当前时间,因为模型没有实时数据。所以需要调用MCP工具里的time_query。这个工具可以返回当前时间和其他相关信息。检查参数是否需要日期,但用户没有指定特定日期,所以参数留空。生成MCP调用请求,工具名是time_query,参数为空。这样就能获取到当前时间并返回给用户了。
</think>
TYPE: ANSWER
当前北京时间是2025年5月2日 16:58:46,星期五,属于劳动节假期。
TYPE: ANSWER
表示了任务完成,而且也确实回复正确了。
多轮 MCP 调用
我自己架设的 MCP 服务中(不好意思, 不对外),除了时间之外,还额外提供了两个 MCP 接口:
location_query
根据 IP 查询用户所在的地理位置weather_query
根据传入的地址描述搜索并返回当前天气和未来预报
同样地,我把这几个 MCP 工具的能力也放在 prompt 中传给 DeepSeek 去理解,同时我问了一个问题:“今天天气怎么样?”。因为文字比较多,我就不附在正文了,读者可以看下下面我用 html 美化展示的对话效果:
从 DeepSeek 的思考过程中可以看到,我短短的一句询问天气,它能够精准地分析出需要通过 MCP 工具分析我的地理位置,然后再通过地理位置换取天气预报。也就是说为了达到一个目的,需要进行多轮的函数调用。实际上,在其他支持 function call 的大模型中,也是可以做到这一点的,也就是在一轮对话中进行多次函数调用。
这也验证了我最初想法的效果:可以使用纯对话的模式,进行解析和调用,并最终取得类似于 function call 的效果。
不足
虽然说我是实现了类似于 function call 的效果,并且也支持多轮调用,但这种方案比起原生 function call 还是有很多缺点的:
- 开发麻烦——原理并不复杂,但是这个逻辑撰写起来还是有点麻烦的,需要解析大模型的准结构化输出,然后需要将实际上的多轮问答,模拟成一轮问答展示给用户看(也就是上图中我的 “美化展示”)
开销大——正如前文所述,只有 R1 才能满足我们的需求。而因为这实际上是多轮问答模拟出来的效果,因此 R1 拿到我们 MCP 调用的结果之后,需要重复再思考一次。大家知道,R1 的 token 往往都消耗在思考上,这也就带来时间和成本的双重上升
- 我曾经尝试过仅在第一次调用中使用 R1,但是 MCP 返回之后改用 V3,试图降低成本,但效果不尽人意
上下文联系弱——尽管我在进行调用的时候带上了上下文,但是 DeepSeek 在后续几轮的回答中也有可能无视之前的思考,或者是采用不同的思路,导致前后逻辑不完全一致
- 比如我尝试问大湾区主要城市的天气,R1 在第一轮思考的时候认为需要分别查询广州、珠海、深圳的天气之后再回答,但是当通过 MCP 只返回广州的天气之后,第二轮的 DeepSeek 就决定放弃继续调用了,而是直接使用广州的天气概括大湾区
总之,使用对话来实现类似 function call 的方案,是可行的,但是效果上还是会比原生 function call 弱一些。也希望本文可以给读者一些启发,更希望能够抛砖引玉,有更多类似于让 R1 这样不支持 function call 的模型支持 function call 的开源方案出现。
参考资料
- Function Calling - DeepSeek API Docs
- When will R1 support function calling and structured outputs? Thanks
- DeepSeekR1支持function_calling吗? - 这里采用的方案就跟本文非常相似
- 那么多接入 DeepSeek 的,终于有一家支持 Function Call 了
本文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
原作者: amc,原文发布于腾讯云开发者社区,也是本人的博客。欢迎转载,但请注明出处。
原文标题:《不支持 function call 的 DeepSeek R1 如何接入 MCP?》
发布日期:2025-05-09
原文链接:https://cloud.tencent.com/developer/article/2518981。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。