DeepSeek原生应用与智能体开发实践(人工智能技术丛书)【行情 报价 价格 评测】-京东
【新书介绍】《DeepSeek原生应用与智能体开发实践》-CSDN博客
目录
[6.1 DeepSeek自带的Function Calling详解](#6.1 DeepSeek自带的Function Calling详解)
[6.1.1 Python使用工具的基本原理](#6.1.1 Python使用工具的基本原理)
[6.1.2 DeepSeek工具使用详解](#6.1.2 DeepSeek工具使用详解)
[6.1.3 DeepSeek工具箱的使用](#6.1.3 DeepSeek工具箱的使用)
[6.2 给大模型插上翅膀的MCP协议详解](#6.2 给大模型插上翅膀的MCP协议详解)
[6.2.1 MCP协议目的、功能与架构详解](#6.2.1 MCP协议目的、功能与架构详解)
[6.2.3 MCP实战2:本地客户端搭建与使用](#6.2.3 MCP实战2:本地客户端搭建与使用)
[6.3 在线MCP服务器的搭建与使用实战](#6.3 在线MCP服务器的搭建与使用实战)
[6.3.1 在线MCP服务器搭建](#6.3.1 在线MCP服务器搭建)
[6.3.2 在线MCP服务的连接和使用](#6.3.2 在线MCP服务的连接和使用)
[6.4 本 章 小 结](#6.4 本 章 小 结)
大模型的工具调用(Function Calling)功能,作为人工智能领域的一项关键技术,赋予了大模型与外部世界进行交互和协作的能力。在这一领域,DeepSeek所实现的工具调用机制,以其高效、灵活的特点,成为众多开发者和研究者关注的焦点。它不仅能够让模型根据需求精准地调用各类外部工具,还极大地拓展了模型的应用场景并增加了其实用价值。
而MCP(Model Context Protocol,模型上下文协议),作为一种具有开创性的开放协议,其核心目标在于标准化人工智能模型与外部数据源、工具之间的交互方式。在当今复杂多变的人工智能应用环境中,模型需要与各种不同类型的数据源和工具进行交互,以实现更加智能、高效的任务处理。MCP通过定义一套统一的通信规范,成功打破了模型与外部系统之间的壁垒,使得大语言模型(LLM)能够轻松、无缝地连接本地文件、数据库、API以及各类专业工具。这一协议的出现,为人工智能模型的集成和应用提供了更加便捷、高效的解决方案。
在本章中,我们将深入剖析DeepSeek工具调用的使用技巧以及识别方法。通过详细的讲解和实例演示,读者将能够全面了解如何运用DeepSeek的工具调用功能,实现模型与外部工具的高效协同。同时,我们也将深入探讨MCP协议在其中的重要作用,以及如何利用MCP协议进一步优化模型与外部系统的交互过程。
随着学习的深入,读者将逐渐掌握如何在实际项目中灵活运用DeepSeek的工具调用和MCP协议,构建出更加智能、高效的人工智能应用。无论是处理复杂的自然语言任务,还是与各类外部数据源和工具进行深度集成,这些知识和技能都将成为读者在人工智能领域取得成功的有力武器。我们相信,通过本章的学习,读者将能够开启一段全新的人工智能探索之旅,为未来的技术创新和应用发展奠定坚实的基础。
6.1 DeepSeek自带的Function Calling详解
相对于只能完成普通文本任务的大模型,DeepSeek一个激动人心的功能是可以自主调用外部工具函数,以自主意识的形式借用工具,完成使用者发布的命令。这意味着DeepSeek不再仅仅是一个被动的执行者,而是成为一个具有主动性的智能助手。
DeepSeek的Function Calling功能是一项具有划时代意义的进步。这一功能的实现,使得DeepSeek不仅仅局限于自身数据库知识的回答,而是跃进到了一个全新的层次------调用外部函数,其调用流程如图6-1所示。

图6-1 DeepSeek的Function Calling功能
这意味着DeepSeek大语言模型在与用户交互时,可以实时检索外部函数库。当用户提问时,模型不再仅仅是从自身知识库中寻找答案,而是会根据实际需求,在外部函数库中进行检索,找出合适的函数并调用它。这种调用外部函数的能力,使得DeepSeek可以获取到函数的运行结果,并基于这些结果进行回答。
6.1.1 Python使用工具的基本原理
工具的使用是一项非常简单的事情,从我们的祖先钻木取火,到现在人类飞上月球在太空建立永久基地,这些都离不开工具的使用。甚至在现实生活中,你决定今天出门要不要带上雨伞,都需要借助网络信息或者广播工具了解到今天的天气情况。
而Python同样也可以使用工具来完成对外部API的调用,其所需要的仅仅是一个函数名称而已。示例代码如下:
创建一个简单的查询天气的API
def get_weather(location = ""):
"读者可以编写对应的天气查询API,这里我们仅仅作演示"
if location == "Shanghai":
return 23.0
elif location == "TianJin":
return 25.0
else:
return "未查询到相关内容"
location = "Shanghai"
注意写法格式,里面的单引号不能少
result = eval(f"get_weather(location='{location}')")#使用eval调用与字符串名称对应的函数
print("查询到的结果是:", result)
最终打印结果如下:
查询到的结果是: 23.0
可以看到,Python中提供的eval()函数可以根据传入的字符串自动运行对应的函数。在这个示例中,我们将location变量的值嵌入到字符串中,然后将该字符串作为代码传给eval()函数执行。注意,在嵌入变量值时,我们使用了单引号将变量值括起来,以确保代码的正确解析。
eval()函数是Python的一个内置函数,它的功能是将字符串作为Python代码执行。其工作原理可以简单概括为"字符串解析和执行"。
当我们调用eval()函数并传入一个字符串时,函数会尝试解析这个字符串,将它转换成Python的表达式或语句,然后在当前的命名空间中执行这些表达式或语句。例如,如果我们传入字符串"1+2",print(eval("1+2"))。eval()函数会将这个字符串解析为Python的加法表达式,然后计算这个表达式的值,返回结果3。
6.1.2 DeepSeek工具使用详解
在上一小节中,我们展示了如何在Python中调用函数,但是,我们面临一个更复杂的问题:如何在大模型DeepSeek中调用工具?这个问题看似简单,实则涉及许多深层次的技术与思考。就如同多年前人们询问计算机"今天是晴天还是雨天"一样,我们如今要探讨的是如何让大模型调用工具来解决问题。
先回到日常生活中的一个例子。在决定今天的穿着之前,我们通常会有一个明确的前置任务:了解今天的天气。那么,如何获取天气信息呢?以下是一些可能的方法:
- A:对着衣橱问自己应该穿什么衣服。这显然不是获取天气信息的正确途径。
- B:使用互联网登录天气网站,输入本地名称查询。这是一个有效且常用的方法。
- C:打开一本书并阅读任意一页。这与获取天气信息无关。
- D:打开空调。这同样不能告诉我们今天的天气情况。
对于大多数读者来说,选择B是显而易见的,这是基于我们的常识和日常经验。然而,这种基于目标寻找最合适解决方案的能力并非天生,而是需要我们后天的学习和积累。我们需要知道哪些工具或方法可以帮助我们实现目标,这通常需要一个知识库或他人的指导。有知识库辅助研判的任务流程如图6-2所示。

图6-2 有知识库辅助研判的任务流程
上图所示是一个基于常识的决策过程,同时也是我们在日常生活中做出明智决策并取得良好结果的通用步骤。在每次决策之前,我们依赖的是深厚的知识储备或知识库,它们如同明灯,照亮我们前行的道路,引导我们做出最优决策。
当我们回到DeepSeek调用工具的问题时,面临的挑战是如何让这个大模型也具备这样的决策能力,即根据给定的任务,它能知道应当调用哪些工具。作为深度学习程序设计人员,我们的责任不仅是开发模型,更要引导模型如何使用工具。我们可以提供格式化的API信息,这种方式就像是给大模型提供一本详细的程序文档。在这份文档中,我们详细描述每个工具API的功能、参数以及返回值,告诉大语言模型在何时、何地可以调用这些API,并且当API被调用后,返回相应的API的JSON对象。
这样的方式能够让大模型更加智能化地运用工具,进而提升其解决问题的效率和准确性。想象一下,当大模型遇到问题时,它可以像人类一样查阅"工具书",找到最合适的工具,然后利用这个工具解决问题。
一个可供DeepSeek进行调用的简单函数如下所示:
定义工具函数
def get_weather(function_params):
"""模拟获取天气的工具函数"""
location = function_params0
这里可以调用真实的天气 API
return f"{location}的天气晴朗"
上述函数对象描述了一个名为get_weather的工具API。通过这个API,大模型可以根据输入的城市名称获取当前的天气情况。这样的描述方式清晰明了,使得大模型能够准确理解并调用这个API。因此,通过对工具API中的描述进行甄别,从而判定使用哪一个最合适的工具,加上合理的引导和训练,可以使大模型更加智能化,从而完成对工具的使用。
作者完成了一个在DeepSeek中使用工具的完整示例,代码如下所示:
from openai import OpenAI
import json
client = OpenAI(
api_key="sk-c646e1c201d74777b54f45c60973f4f3",
base_url="https://api.deepseek.com",
)
def get_weather(function_params):
return "天气晴朗"
def send_messages(messages):
response = client.chat.completions.create(
model="deepseek-chat",
messages=messages
)
return response.choices0.message
system_prompt = """
你在运行一个"思考""工具调用""响应"循环。每次只运行一个阶段
1."思考"阶段:你要仔细思考用户的问题。
2."工具调用"阶段:选择可以调用的工具,并且输出对应工具需要的参数。
3."响应"阶段:根据工具调用返回的影响,回复用户问题。
已有的工具如下:
get_weather:
e.g. get_weather:天津
返回天津的天气情况
Example:
question:天津的天气怎么样?
thought:我应该调用工具查询天津的天气情况
Action:
{
"function_name":"get_weather",
"function_params":"天津"
}
调用Action的结果:"天气晴朗"
Answer:天津的天气晴朗
"""
question = "Shanghai的天气怎么样"
messages = [{"role": "system", "content": system_prompt},
{"role": "user", "content": question}]
message = send_messages(messages)
response = message.content
action = response.split("Action:")1
action = json.loads(action)
print(f"ModelResponse:\n {action}")
生成调用代码
function_name = action"function_name"
function_params = action"function_params"
code = f"{function_name}({function_params})"
print(code)
使用eval对生成的代码进行计算,这里假设get_weather函数已经被定义过
result = eval(code)
print(result)
这段代码实现了一个简单的对话系统,能够根据用户的问题调用相应的工具并生成回答。首先,代码通过OpenAI库初始化了一个客户端,并设置了API密钥和基础URL。接着,定义了一个get_weather函数,用于模拟获取天气的功能,返回固定的"天气晴朗"结果。send_messages函数则负责向模型发送消息并获取模型的响应。系统提示(system_prompt)中详细描述了对话系统的三个阶段:思考、工具调用和响应,并提供了一个示例说明如何调用get_weather工具,以回答与天气相关的问题。
生成结果如下所示:
ModelResponse:
{'function_name': 'get_weather', 'function_params': '上海'}
get_weather('上海')
天气晴朗
具体来看,在代码的执行部分,用户提出了一个关于上海天气的问题。系统通过send_messages函数将问题发送给模型,模型根据系统提示生成一个包含工具调用信息的响应。代码通过解析响应中的Action部分,提取出需要调用的工具名称和参数,并生成相应的调用代码。最后,使用eval函数执行生成的代码,模拟工具调用的过程,并输出结果。整个过程展示了如何通过模型生成工具调用指令,并动态执行这些指令来完成用户请求。
6.1.3 DeepSeek工具箱的使用
上面我们演示了在DeepSeek中使用单一工具的方法。但是,在具体工作中,我们可能会面临一个选择的问题,即在一个"工具箱"中完成工具的选择,之后再使用工具去完成我们的目标。下面给出一个在DeepSeek中有选择地使用工具的完整示例:
from openai import OpenAI
import json
初始化 OpenAI 客户端
client = OpenAI(
api_key="sk-c646e1c201d74777b54f45c60973f4f3", # 替换为你的API 密钥
base_url="https://api.deepseek.com", # DeepSeek API的基础 URL
)
定义工具函数
def get_weather(function_params):
"""模拟获取天气的工具函数"""
location = function_params0
这里可以调用真实的天气 API
return f"{location}的天气晴朗"
def get_stock(function_params):
"""模拟获取股票的工具函数"""
location = function_params0
这里可以调用真实股票价格 API
return f"{location}的股票价格为18.88"
定义工具列表
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的天气情况",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称,例如:上海",
}
},
"required": "location",
},
},
},
{
"type": "function",
"function": {
"name": "get_stock",
"description": "模拟获取股票的工具函数",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "股票名称,例如:上海证券",
}
},
"required": "location",
},
},
}
]
发送消息并调用工具
def send_messages_tools(messages):
"""发送消息并调用工具"""
response = client.chat.completions.create(
model="deepseek-chat", # 使用的模型
messages=messages, # 消息列表
tools=tools, # 工具列表
tool_choice="auto", # 让模型自动选择是否调用工具
)
return response.choices0.message
系统提示
system_prompt = """
你是一个智能助手,能够通过"思考""工具调用"和"响应"三个阶段来处理用户的问题。每个阶段的任务如下:
-
**思考阶段**:你需要仔细分析用户的问题,判断是否需要调用工具来获取信息。如果需要调用工具,明确选择适合的工具并准备调用参数。
-
**工具调用阶段**:根据思考阶段的结果,选择合适的工具并生成工具调用请求。工具调用的参数需要符合工具的定义。
-
**响应阶段**:根据工具调用的返回结果,生成对用户问题的最终回答。
示例:
用户问题:天津的天气怎么样?
思考:我需要调用工具查询天津的天气情况。
工具调用:
{
"function_name": "get_weather",
"function_params": {"location": "天津"}
}
工具调用结果:"天津的天气晴朗"
最终回答:天津的天气晴朗。
"""
用户问题
question = "Shanghai的天气是什么?"
消息列表
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": question},
]
发送消息并获取模型响应
message = send_messages_tools(messages)
打印模型返回的原始消息
print(f"Initial Model Response: {message}")
检查模型是否返回了工具调用请求
if message.tool_calls:
解析工具调用请求
tool_call = message.tool_calls0
function_name = tool_call.function.name
function_params = json.loads(tool_call.function.arguments)
打印工具调用信息
print(f"Tool Call: {tool_call}")
print(f"Function Name: {function_name}")
print(f"Function Params: {function_params}")
根据工具名称调用相应的工具
if function_name == "get_weather":
result = get_weather(function_params\["location"])
elif function_name == "get_stock":
result = get_stock(function_params\["location"])
else:
result = "未知工具"
打印工具执行结果
print(f"Tool Result: {result}")
结果如下所示:
Initial Model Response: ChatCompletionMessage(content='', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=ChatCompletionMessageToolCall(id='call_0_18c66898-e9e9-45de-98bf-4e4961cb9400', function=Function(arguments='{"location":"Shanghai"}', name='get_weather'), type='function', index=0))
Tool Call: ChatCompletionMessageToolCall(id='call_0_18c66898-e9e9-45de-98bf-4e4961cb9400', function=Function(arguments='{"location":"Shanghai"}', name='get_weather'), type='function', index=0)
Function Name: get_weather
Function Params: {'location': 'Shanghai'}
Tool Result: Shanghai的天气晴朗
我们分别对其进行讲解。首先,定义要使用的工具,并通过列表的形式对工具进行汇总,这里定义了两个工具函数get_weather和get_stock,分别用于模拟获取指定城市的天气情况和股票价格。每个函数接受一个参数function_params,其中包含所需的参数信息(如城市名称或股票名称),并返回相应的模拟结果。接着,代码定义了一个tools列表,其中包含这两个工具函数的元数据描述,包括函数名称、功能描述以及参数的定义(如参数类型、描述等)。这些元数据可以用于动态调用这些工具函数,或者集成到其他系统中进行自动化处理。
以下是对代码的详细分步讲解和模型介绍,我们将结合代码的每一部分进行说明。
- 初始化OpenAI客户端
from openai import OpenAI
import json
初始化 OpenAI 客户端
client = OpenAI(
api_key="sk-c646e1c201d74777b54f45c60973f4f3", # 替换为你的API密钥
base_url="https://api.deepseek.com", # DeepSeek API的基础URL
)
(1)功能:初始化OpenAI客户端,用于与DeepSeek API进行交互。
(2)关键点:
-
api_key:用于身份验证的API密钥。
-
base_url:DeepSeek API的基础URL,指定API的访问地址。
-
定义工具函数
def get_weather(function_params):
"""模拟获取天气的工具函数"""
location = function_params0
return f"{location}的天气晴朗"
def get_stock(function_params):
"""模拟获取股票的工具函数"""
location = function_params0
return f"{location}的股票价格为18.88"
(1)功能:定义了两个工具函数,分别用于模拟获取天气和股票信息。
(2)参数:
- function_params:一个列表,包含工具调用时传递的参数(如城市名称或股票名称)。
(3)返回值:返回一个字符串,表示模拟的结果。
(4)说明:这些函数是模拟实现,实际应用中可以通过调用真实的API获取数据。
- 定义工具列表
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的天气情况",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称,例如:上海",
}
},
"required": "location",
},
},
},
{
"type": "function",
"function": {
"name": "get_stock",
"description": "模拟获取股票的工具函数",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "股票名称,例如:上海证券",
}
},
"required": "location",
},
},
}
]
(1)功能:定义工具函数的元数据,包括名称、描述和参数。
(2)结构:
- type:工具类型,这里是function。
- name:工具函数的名称。
- description:工具函数的功能描述。
- parameters:定义工具函数的参数类型和结构。
- properties:参数的属性(如location的类型和描述)。
- required:指定哪些参数是必需的。
(3)说明:这些元数据用于指导模型如何调用工具函数。
- 发送消息并调用工具
def send_messages_tools(messages):
"""发送消息并调用工具"""
response = client.chat.completions.create(
model="deepseek-chat", # 使用的模型
messages=messages, # 消息列表
tools=tools, # 工具列表
tool_choice="auto", # 让模型自动选择是否调用工具
)
return response.choices0.message
(1)功能:发送用户问题到模型,并返回模型的响应。
(2)参数:
- model:使用的模型名称(这里是deepseek-chat)。
- messages:包含系统提示和用户问题的消息列表。
- tools:工具列表。
- tool_choice:设置为auto,表示由模型自动决定是否调用工具。
(3)返回值:返回模型生成的消息。
(4)说明:该函数是核心逻辑,负责与模型交互并获取工具调用请求。
- 系统提示
system_prompt = """
你是一个智能助手,能够通过"思考""工具调用"和"响应"三个阶段来处理用户的问题。每个阶段的任务如下:
-
**思考阶段**:你需要仔细分析用户的问题,判断是否需要调用工具来获取信息。如果需要调用工具,明确选择适合的工具并准备调用参数。
-
**工具调用阶段**:根据思考阶段的结果,选择合适的工具并生成工具调用请求。工具调用的参数需要符合工具的定义。
-
**响应阶段**:根据工具调用的返回结果,生成对用户问题的最终回答。
示例:
用户问题:天津的天气怎么样?
思考:我需要调用工具查询天津的天气情况。
工具调用:
{
"function_name": "get_weather",
"function_params": {"location": "天津"}
}
工具调用结果:"天津的天气晴朗"
最终回答:天津的天气晴朗。
"""
(1)功能:指导模型如何处理用户问题。
(2)内容:
- 定义了模型的工作流程(思考、工具调用、响应等)。
- 提供了一个示例,帮助模型理解如何调用工具并生成响应。
(3)说明:系统提示是模型行为的关键指导,确保模型能够正确调用工具。
- 用户问题与消息列表
question = "Shanghai的天气是什么?"
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": question},
]
(1)功能:定义用户问题,并将其与系统提示一起组成消息列表。
(2)结构:
- "role": "system":系统提示,用于指导模型。
- "role": "user":用户问题。
(3)说明:消息列表是模型输入的核心部分,决定了模型的行为和输出。
- 发送消息并获取模型响应
message = send_messages_tools(messages)
print(f"Initial Model Response: {message}")
(1)功能:发送消息列表到模型,并打印模型的初始响应。
(2)说明:模型的初始响应可能包含工具调用请求或直接回答。
- 检查工具调用请求
if message.tool_calls:
解析工具调用请求
tool_call = message.tool_calls0
function_name = tool_call.function.name
function_params = json.loads(tool_call.function.arguments)
打印工具调用信息
print(f"Tool Call: {tool_call}")
print(f"Function Name: {function_name}")
print(f"Function Params: {function_params}")
根据工具名称调用相应的工具
if function_name == "get_weather":
result = get_weather(function_params\["location"])
elif function_name == "get_stock":
result = get_stock(function_params\["location"])
else:
result = "未知工具"
打印工具执行结果
print(f"Tool Result: {result}")
(1)功能:解析模型的工具调用请求,执行相应的工具函数,并返回结果。
(2)步骤:
- 检查模型是否返回了工具调用请求(tool_calls)。
- 解析工具调用的名称和参数。
- 根据工具名称调用相应的工具函数。
- 打印工具执行结果。
(3)说明:该部分是工具调用的核心逻辑,负责执行工具函数并处理结果。
- 代码运行流程
(1)初始化OpenAI客户端。
(2)定义工具函数和工具列表。
(3)发送用户问题到模型,并获取模型的初始响应。
(4)检查模型是否返回了工具调用请求。
(5)解析工具调用请求,执行相应的工具函数。
(6)打印工具执行结果。
上面示例代码实现了一个基于工具调用的智能助手系统,能够根据用户问题动态调用工具函数并返回结果。通过定义工具函数、工具列表和系统提示,代码实现了灵活的问题处理机制,适用于需要外部数据支持的场景(如天气查询、股票查询等)。
6.1.4 DeepSeek工具调用判定依据
在我们进行工具调用时,DeepSeek也需要有一个判断依据,为了找到这个判断依据,我们修改部分代码,特别是工具箱的描述,如下所示:
from openai import OpenAI
import json
初始化 OpenAI 客户端
client = OpenAI(
api_key="sk-c646e1c201d74777b54f45c60973f4f3", # 替换为你的API密钥
base_url="https://api.deepseek.com", # DeepSeek API的基础URL
)
定义匿名工具函数
def get_tool2(function_params):
location = function_params0
return f"{location}的天气晴朗"
def get_tool1(function_params):
location = function_params0
return f"{location}的股票价格为18.88"
定义工具列表
tools = [
{
"type": "function",
"function": {
"name": "get_tool1",
"description": "模拟获取股票的工具函数",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "",
}
},
"required": "location",
},
},
},
{
"type": "function",
"function": {
"name": "get_tool2",
"description": "模拟获取天气的工具函数",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "",
}
},
"required": "location",
},
},
}
]
发送消息并调用工具
def send_messages_tools(messages):
"""发送消息并调用工具"""
response = client.chat.completions.create(
model="deepseek-chat", # 使用的模型
messages=messages, # 消息列表
tools=tools, # 工具列表
tool_choice="auto", # 让模型自动选择是否调用工具
)
return response.choices0.message
系统提示
system_prompt = """
你是一个智能助手,能够通过"思考""工具调用"和"响应"三个阶段来处理用户的问题。每个阶段的任务如下:
-
**思考阶段**:你需要仔细分析用户的问题,判断是否需要调用工具来获取信息。如果需要调用工具,明确选择适合的工具并准备调用参数。
-
**工具调用阶段**:根据思考阶段的结果,选择合适的工具并生成工具调用请求。工具调用的参数需要符合工具的定义。
-
**响应阶段**:根据工具调用的返回结果,生成对用户问题的最终回答。
示例:
用户问题:天津的天气怎么样?
思考:我需要调用工具查询天津的天气情况。
工具调用:
{
"function_name": "get_tool",
"function_params": {"location": "天津"}
}
工具调用结果:"天津的天气晴朗"
最终回答:天津的天气晴朗。
"""
用户问题
question = "Shanghai的天气是什么?"
消息列表
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": question},
]
发送消息并获取模型响应
message = send_messages_tools(messages)
打印模型返回的原始消息
print(f"Initial Model Response: {message}")
检查模型是否返回了工具调用请求
if message.tool_calls:
解析工具调用请求
tool_call = message.tool_calls0
function_name = tool_call.function.name
function_params = json.loads(tool_call.function.arguments)
打印工具调用信息
print(f"Tool Call: {tool_call}")
print(f"Function Name: {function_name}")
print(f"Function Params: {function_params}")
根据工具名称调用相应的工具
if function_name == "get_tool1":
result = get_tool1(function_params\["location"])
elif function_name == "get_tool2":
result = get_tool2(function_params\["location"])
else:
result = "未知工具"
打印工具执行结果
print(f"Tool Result: {result}")
在上面代码中,我们匿名定义了工具函数,并删除了其描述部分,而仅仅在工具箱列表中对函数的作用进行解释。我们重新运行工具函数调用,结果如下所示:
Initial Model Response: ChatCompletionMessage(content='', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=ChatCompletionMessageToolCall(id='call_0_c5a5e11b-9045-4fe5-8c89-0b6ca0d768b9', function=Function(arguments='{"location":"Shanghai"}', name='get_tool2'), type='function', index=0))
Tool Call: ChatCompletionMessageToolCall(id='call_0_c5a5e11b-9045-4fe5-8c89-0b6ca0d768b9', function=Function(arguments='{"location":"Shanghai"}', name='get_tool2'), type='function', index=0)
Function Name: get_tool2
Function Params: {'location': 'Shanghai'}
Tool Result: Shanghai的天气晴朗
可以看到,我们替换了名称,并在函数体内部删除了注释,仅仅在工具描述列表中对每个工具函数进行描述,结果如下:
Initial Model Response: ChatCompletionMessage(content='', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=ChatCompletionMessageToolCall(id='call_0_94b9f60d-bc93-406a-bc6a-d72f985e33d1', function=Function(arguments='{"location":"Shanghai"}', name='get_tool2'), type='function', index=0))
Tool Call: ChatCompletionMessageToolCall(id='call_0_94b9f60d-bc93-406a-bc6a-d72f985e33d1', function=Function(arguments='{"location":"Shanghai"}', name='get_tool2'), type='function', index=0)
Function Name: get_tool2
Function Params: {'location': 'Shanghai'}
Tool Result: Shanghai的天气晴朗
可以看到,此时生成的结果依旧输出了对应的天气情况。下一步,我们继续修改内容,在工具list中删除了函数描述与参数描述,但是在工具函数体内部加上对应的描述,如下所示:
定义工具函数
def get_tool2(function_params):
"模拟获取天气的工具函数"
location = function_params0
return f"{location}的天气晴朗"
def get_tool1(function_params):
"模拟获取股票的工具函数"
location = function_params0
return f"{location}的股票价格为18.88"
定义工具列表
tools = [
{
"type": "function",
"function": {
"name": "get_tool1",
"description": None,
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "",
}
},
"required": "location",
},
},
},
{
"type": "function",
"function": {
"name": "get_tool2",
"description": None,
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "",
}
},
"required": "location",
},
},
}
]
打印结果如下所示:
Initial Model Response: ChatCompletionMessage(content='', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=ChatCompletionMessageToolCall(id='call_0_f5dd2f6e-e1f3-4c0a-a5ee-627fd0e9341e', function=Function(arguments='{"location":"Shanghai"}', name='get_tool1'), type='function', index=0))
Tool Call: ChatCompletionMessageToolCall(id='call_0_f5dd2f6e-e1f3-4c0a-a5ee-627fd0e9341e', function=Function(arguments='{"location":"Shanghai"}', name='get_tool1'), type='function', index=0)
Function Name: get_tool1
Function Params: {'location': 'Shanghai'}
Tool Result: Shanghai的股票价格为18.88
可以看到,虽然有DeepSeek输出了结果,但是很明显,此时的模型存在函数调用错误,即我们在定义的工具列表中删除了函数描述,就会造成函数调用错误的结果,这一点需要读者注意。
6.2 给大模型插上翅膀的MCP协议详解
大模型的工具调用(Function Calling)功能,作为人工智能领域的一项关键技术,赋予了大模型与外部世界进行交互和协作的能力。在这一领域,DeepSeek所实现的工具调用机制,以其高效、灵活的特点,成为众多开发者和研究者关注的焦点。它不仅能够让模型根据需求精准地调用各类外部工具,还极大地拓展了模型的应用场景和实用价值,使得大模型在诸如自然语言处理、智能客服、内容生成等众多领域展现出了强大的实力。然而,随着人工智能技术在特定领域和垂直行业的深入应用,我们逐渐发现,一些专业场景下的大模型面临着新的挑战。
这些特定领域和垂直行业的大模型,由于其训练数据和应用场景的特殊性,往往本身不具备或者无法直接执行某些特定的Function Calling。例如,在医疗领域,大模型可能需要调用专业的医学诊断工具来获取准确的诊断结果;在金融领域,可能需要调用实时的金融数据接口以进行精准的风险评估。在这些情况下,仅仅依靠大模型自身的能力是远远不够的,我们额外需要一种能够使得大模型直接、高效调用这些专业工具的方法,而MCP正是为解决这一问题而诞生的。
MCP作为一种具有开创性的开放协议,如图6-3所示。其核心目标在于标准化人工智能模型与外部数据源、工具之间的交互方式。在当今复杂多变的人工智能应用环境中,模型需要与各种不同类型的数据源和工具进行交互,以实现更加智能、高效的任务处理。通过MCP,大模型可以轻松地与各种专业工具进行连接和通信,无须进行复杂的接口开发和数据转换,从而大大提高了开发效率和应用效果。同时,MCP的标准化特性也保证了不同模型、不同工具之间的兼容性和互操作性,为人工智能技术的广泛应用和深入发展奠定了坚实的基础。

图6-3 MCP通信协议
MCP通过定义一套统一的通信规范,成功打破了模型与外部系统之间的壁垒,使得大语言模型(LLM)能够轻松、无缝地连接本地文件、数据库、API以及各类专业工具。这一协议的出现,为人工智能模型的集成和应用提供了更加便捷、高效的解决方案。
读者将逐渐掌握如何在实际项目中灵活运用DeepSeek的工具调用和MCP协议,构建出更加智能、高效的人工智能应用。同时,我们也将深入探讨MCP协议在其中的重要作用,以及如何利用MCP协议进一步优化模型与外部系统的交互过程。
无论是处理复杂的自然语言任务,还是与各类外部数据源和工具进行深度集成,这些知识和技能都将成为读者在人工智能领域取得成功的有力武器。我们相信,通过本章的学习,读者将能够开启一段全新的人工智能探索之旅,为未来的技术创新和应用发展奠定坚实的基础。
6.2.1 MCP协议目的、功能与架构详解
MCP协议的问世,旨在攻克AI模型与外部数据源和工具之间错综复杂的连接难题。在过往的开发进程中,开发者常常需要为不同的数据源和工具量身定制连接方案,这不仅耗费大量的时间与精力,还极易导致代码的冗余和混乱。而MCP协议凭借其标准化的特性,犹如一把万能钥匙,巧妙地减少了开发者的重复劳动,为开发过程带来了前所未有的便捷与高效。
MCP堪称AI世界的万能连接器,其作为标准化协议的重要性不言而喻。它搭建起了一座沟通的桥梁,让形形色色的应用和工具能够毫无阻碍地实现无缝连接。无论这些应用和工具来自何处、具有何种特性,MCP都能凭借其强大的兼容性,将它们紧密地联系在一起,共同构建起一个协同共生的AI生态系统。
从架构层面来看,MCP主要由Host、Client和Server三大部分构成。其中,客户端扮演着通信使者的角色,负责在AI模型与外部系统之间传递信息;而服务器则如同一位睿智的中间人,巧妙地连接着外部系统,这些外部系统可能是庞大的数据库,也可能是功能各异的API。通过这种明确的分工协作,MCP实现了AI模型与外部世界的高效交互。
相较于传统的Function Calling,MCP展现出了显著的优势。传统的Function Calling往往呈现出碎片化的特点,不同的AI模型和应用可能采用不同的调用方式,这使得开发者在面对不同的项目时,需要不断地重新学习和适应,极大地增加了开发的难度和成本。而MCP通过统一协议,打破了这种碎片化的局面,为开发者提供了一个统一、规范的操作标准。它不仅仅是一种技术上的革新,更是一种生态整合的利器。
通过MCP,不同的AI模型、应用和工具能够在一个统一的框架下协同工作,形成了一个更加紧密、高效的AI生态。这种标准化和生态整合的优势,使得MCP成为推动AI技术发展的重要力量,为AI的广泛应用和深入发展奠定了坚实的基础。
- 标准化交互协议
MCP以一种极具创新性的方式,将AI模型与外部资源的交互过程抽象为三个关键部分,构建起了一套高效、统一的交互体系。
- MCP Host:作为发起请求的AI应用,它就像是整个交互流程的指挥官。常见的如Claude、Cursor IDE等,这些应用在实际运行中会产生与外部资源交互需求,从而触发整个MCP交互流程。
- MCP Client:它运行在Host之中,扮演着格式转换大师的角色。当Host发起请求时,MCP Client会将请求精心格式化为结构化消息。这种结构化消息具有统一的格式和规范,便于后续的传输和处理,就像是将杂乱的信息整理成有序的包裹,以便准确送达目的地。
- MCP Server:作为轻量级中间件,它是连接本地或远程资源的桥梁。无论是庞大的数据库,还是功能丰富的API,MCP Server都能与之建立连接。它负责执行具体的操作,并将操作结果返回,就如同一位高效的快递员,准确地将货物送达并带回反馈信息。
- 核心组件
MCP的核心组件犹如一套精密的齿轮组,相互配合,共同推动AI模型与外部资源进行有效交互。
- 资源(Resources):这是向模型暴露的数据对象,是模型获取信息的源泉。例如文件,它可能包含着重要的文本、图像等数据;数据库表,则存储着结构化的数据信息。这些资源为模型提供了丰富的素材,使其能够进行更深入地分析和处理。
- 工具(Tools):可调用的函数是模型实现各种功能的利器。比如执行SQL查询,模型可以通过调用相应的工具从数据库中提取所需的数据;操作浏览器,则可以让模型获取网页上的信息。这些工具赋予了模型强大的操作能力,使其能够与现实世界进行更紧密的互动。
- 提示词(Prompts):预定义的指令模板就像是模型的行动指南。它们指导模型如何利用资源和工具,让模型知道在什么情况下使用什么资源和工具,以及如何正确地使用它们。通过提示词,模型能够更加智能、高效地完成任务。
- 动态发现机制
MCP具备动态发现机制,这一特性使其具有类似即插即用的硬件生态的优势。它能够自动识别新接入的服务器功能,无须修改模型代码即可扩展能力。当有新的资源或工具接入时,MCP可以迅速发现并适配,为模型提供更多的功能和可能性。这大大简化了系统的扩展和维护过程,提高了系统的灵活性和可扩展性。其主要作用如下:
- 消除碎片化集成:在传统方案中,每个AI应用都需要单独开发连接器,例如连接天气API、数据库驱动等。这不仅增加了开发的工作量,还导致了代码的冗余和复杂。而MCP通过统一协议,打破了这种碎片化的局面,减少了重复开发,提高了开发效率。
- 提升开发效率:开发者无须再关注底层的实现细节,只需专注于如何利用MCP提供的资源和工具来实现业务逻辑。这使得开发者能够将更多的精力投入到创新和业务优化上,大大缩短了开发周期。
- 增强数据安全:本地Server直接访问敏感数据,避免了将数据传输到云端可能带来的安全风险。同时,权限控制精细到工具级别,例如可以限制模型仅能读取特定目录的数据,进一步保障了数据的安全性。
- 提升模型实用性:MCP使模型能够突破训练数据的限制,实时操作现实系统。例如,模型可以自动抓取新闻、分析销售数据等,为实际应用提供了更多的可能性。
- MCP协议的通信流程
MCP的通信流程在整体中,各个环节紧密配合,确保信息的准确传递和处理。其流程如下:
(1)MCP Host向MCP Client发送请求,请求中包含了资源、工具和提示词等关键信息。这些信息就像是舞蹈的指令,指导着后续的动作。
(2)MCP Client接收到请求后,将其格式化为结构化消息。这一过程就像是将指令转化为标准的舞蹈动作,便于后续的传输和执行。
(3)格式化后的结构化消息被发送给MCP Server,MCP Server根据消息中的信息执行相应的操作,并将结果返回给MCP Client。
- MCP服务端
MCP Server作为轻量级中间件,承担着连接本地/远程资源、执行具体操作并返回结果的重要职责。其主要功能如下:
- 资源管理:对本地资源进行全面管理,如文件、数据库表等。它负责资源的存储、访问控制等操作,确保资源的安全和有效利用。
- 工具管理:管理可调用的函数,如执行SQL查询、操作浏览器等。它负责工具的注册、调用和监控,保证工具的正常运行。
- 请求处理:接收MCP Client发送的请求,解析请求中的资源、工具和提示词等信息。根据解析结果,调用相应的资源或工具执行操作,确保请求得到准确处理。
- 结果返回:将操作结果返回给MCP Client,以便后续的处理和使用。
可以看到,在与相关技术的对比中,MCP协议是针对不同模型定制的接口,生态封闭、通用性和灵活性不足,而MCP作为标准化协议适用性和兼容性更广。对于使用的大模型,MCP提供底层协议支持工具的标准化调用,避免重复开发适配层,提升效率、降低成本。
同时,MCP凭借其标准化的交互协议、核心组件、动态发现机制等优势,解决了传统AI应用中的诸多问题,为AI技术的发展和应用提供了强大的支持。
6.2.2 MCP实战1:本地工具服务端搭建
MCP的作用是搭建工具函数与大模型应用的桥梁,我们首先将完成工具函数类的设置。在具体使用上,读者需要安装MCP专用的库包,命令如下所示:
pip install mcp
下面示例实现了多个可用MCP调用的函数,并使用mcp.tool()进行修饰,代码如下所示:
'''
MCP服务端程序
提供 stdio 协议与MCP客户端进行通信
'''
import datetime
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("DemoServer") # 创建服务实例
@mcp.tool()
def add_numbers(a: int, b: int) -> int:
"""
计算两个整数的和。
Args:
a (int): 第一个整数,例如:5
b (int): 第二个整数,例如:3
Returns:
int: 两数之和,例如:8
"""
return a + b
@mcp.tool()
def multiply_numbers(a: int, b: int) -> int:
"""
计算两个整数的乘积。
Args:
a (int): 被乘数,例如:4
b (int): 乘数,例如:7
Returns:
int: 两数乘积,例如:28
"""
return a * b
@mcp.tool()
def date_time() -> str:
"""
获取当前系统时间(基于服务器时区)。
Returns:
str: 格式为YYYY-MM-DD HH:MM:SS的时间字符串,例如:2024-05-01 15:30:00
"""
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@mcp.tool()
def get_weather(city_name: str) -> dict:
"""
获取指定城市的模拟天气信息(当前为静态数据)。
Args:
city_name (str): 城市名称(中文或拼音),例如:shanghai 或 上海
Returns:
dict: 包含城市和温度的字典,例如:{'城市': '上海', '温度': '23℃'}
"""
return {
'城市': city_name,
'温度': "23℃"
}
if name == "main":
mcp.run(transport="stdio") # 使用标准输入输出协议运行服务
在上面代码中,我们定义了多个函数,并通过说明文本对函数作用进行讲解,而mcp.tool()的作用是对其进行注册,将函数转换为可供MCP框架使用的内容。
6.2.3 MCP实战2:本地客户端搭建与使用
下面我们将完成MCP客户端搭建,在这里我们的目标是使用DeepSeek完成工具的调用。首先是DeepSeek大模型的提示设置与输出,代码如下所示:
from openai import OpenAI
from mcp import ClientSession, StdioServerParameters # 从mcp模块导入ClientSession和StdioServerParameters类
from mcp.client.stdio import stdio_client # 从mcp.client.stdio模块导入stdio_client函数
import sys # 导入sys模块,用于处理命令行参数
import json # 导入json模块,用于处理JSON数据
定义一个函数,用于转换tools工具中JSON数据
def transform_json(tools):
s = "MCP服务器提供的工具如下:"
for tool in tools: # 遍历工具列表
s = s + f"""
tool_name: {tool.name},
tool_description: {tool.description},
-
input title: {tool.inputSchema'title'},
-
input properties:"{tool.inputSchema'properties'},
"""
return s
def ask_llm_deepseek(question, tools_list):
TODO: 实现与LLM的交互逻辑
这里只是一个示例,实际应用中需要根据具体情况进行实现
system_prompt = tools_list + '\n 根据以上描述,用户要求:%s ,请生成一个工具调用命令,要求以json格式输出{"tool":工具名,"tool_input":参数字典},只输出json,不要输出其他内容' % (
question)
client = OpenAI(
api_key="sk-282074c41d594514aee6fd6f179ed292",
base_url="https://api.deepseek.com/beta",
)
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": "Hello"},
],
max_tokens=1024,
temperature=0.99,
stream=False
)
generated_text = response.choices0.message.content # 提取文本内容
return generated_text, client # 返回文本而非response对象
def llm_deepseek(question,client):
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "user", "content": question},
],
max_tokens=1024,
temperature=0.99,
stream=False
)
generated_text = response.choices0.message.content # 提取文本内容
return generated_text
从上面代码中可以看到,我们定义一个函数用于转换tools工具中JSON数据,而DeepSeek函数的作用是对输入的内容和函数进行匹配,并完成函数的调用。这里需要注意,我们使用System_prompt对DeepSeek的作用以及输出格式做出规范,并且在system_prompt中添加了函数的用法和说明。
完整的MCP客户端搭建与使用如下所示:
'''
MCP客户端程序
通过 stdio 协议与MCP服务器进行通信
获取MCP服务器的工具清单和调用参数
并调用工具实现MCP服务中函数功能
'''
from openai import OpenAI
from mcp import ClientSession, StdioServerParameters # 从mcp模块导入ClientSession和StdioServerParameters类
from mcp.client.stdio import stdio_client # 从mcp.client.stdio模块导入stdio_client函数
import sys # 导入sys模块,用于处理命令行参数
import json # 导入json模块,用于处理JSON数据
定义一个函数,用于转换tools工具中JSON数据
def transform_json(tools):
s = "MCP服务器提供的工具如下:"
for tool in tools: # 遍历工具列表
s = s + f"""
tool_name: {tool.name},
tool_description: {tool.description},
-
input title: {tool.inputSchema'title'},
-
input properties:"{tool.inputSchema'properties'},
"""
return s
def ask_llm_deepseek(question, tools_list):
TODO: 实现与LLM的交互逻辑
这里只是一个示例,实际应用中需要根据具体情况进行实现
system_prompt = tools_list + '\n 根据以上描述,用户要求:%s ,请生成一个工具调用命令,要求以json格式输出{"tool":工具名,"tool_input":参数字典},只输出json,不要输出其他内容' % (
question)
client = OpenAI(
api_key="sk-282074c41d594514aee6fd6f179ed292",
base_url="https://api.deepseek.com/beta",
)
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": "Hello"},
],
max_tokens=1024,
temperature=0.99,
stream=False
)
generated_text = response.choices0.message.content # 提取文本内容
return generated_text, client # 返回文本而非response对象
def llm_deepseek(question,client):
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "user", "content": question},
],
max_tokens=1024,
temperature=0.99,
stream=False
)
generated_text = response.choices0.message.content # 提取文本内容
return generated_text
import os
server_script_path = os.path.join(os.path.dirname(file), "mcp_tool_and_server.py")
定义服务器参数
server_params = StdioServerParameters(
command="python", # 运行命令
args=server_script_path, # 服务器脚本路径
env=None # 可选的环境变量
)
async def run(question = "你是谁?"):
建立连接
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
初始化连接
await session.initialize()
列出可用工具
tools = await session.list_tools()
#print("可用工具:", tools.tools)
s = transform_json(tools.tools) +"\n"
#print(s)
response, client = ask_llm_deepseek(question,s)
清理Markdown标记
response = response.strip().replace(''''json', '').replace(''''', '').strip()
#print(response)
mtools = json.loads(response) # 现在response是纯JSON字符串
print("mtools: --> ",mtools)
if 'tool' in mtools:
tool_name = mtools'tool'
tool_input = mtools'tool_input'
调用工具
print("调用工具:", tool_name, tool_input)
ret = await session.call_tool(tool_name, tool_input)
if ret:
try:
r = json.loads(ret.content0.text)
except:
r = ret.content0.text
print("工具返回结果:", r)
questions = f"用户的问题是{question},根据{tool_name}的返回结果为:{r},根据以上信息,回答问题。"
r = llm_deepseek(questions,client)
print(r)
else:
r = llm_deepseek(question,client)
print(r)
if name == "main":
import asyncio
questions = '计算一下356\*125', '25+38是多少?', '现在的时间是什么时候?', 'Shanghai天气怎么样?','请给我讲一个笑话'
for question in questions:
asyncio.run(run(question))
上面代码的核心功能是通过一个本地工具服务器(MCP服务器)和LLM(如DeepSeek)结合,根据用户问题动态调用工具并生成答案。以下是代码开始部分的详细解析:
- 模块引入
- from openai import OpenAI:虽然命名为OpenAI,但后续代码实际上是通过自定义类与DeepSeek的API交互,可能是为了兼容性或扩展性而命名。
- from mcp import ClientSession, StdioServerParameters 和 from mcp.client.stdio import stdio_client:用于与MCP服务器交互,ClientSession管理会话,StdioServerParameters配置服务器参数,stdio_client用于创建客户端连接。
- import sys, json:sys用于处理命令行参数(尽管代码中未直接使用),json用于解析和生成JSON数据。
- import os:用于处理文件路径,确定MCP服务器脚本的位置。
- 工具描述转换函数
- transform_json(tools):将MCP服务器提供的工具列表转换为人类可读的字符串格式,包括工具名称、描述、输入标题和属性。这有助于将工具信息传递给LLM以生成调用命令。
- LLM交互函数
- ask_llm_deepseek(question, tools_list):构建系统提示(System Prompt),将工具描述和用户问题结合,生成一个JSON格式的工具调用命令。通过DeepSeek API发送请求,并返回生成的文本和客户端对象。
- llm_deepseek(question, client):直接通过客户端对象发送用户问题到LLM,并返回生成的文本。
工具调用与结果处理:
(1)在run异步函数中,首先通过stdio_client和ClientSession与MCP服务器建立连接。
(2)列出可用工具,并将其转换为可读字符串。
(3)调用ask_llm_deepseek生成工具调用命令,解析返回的JSON字符串以获取工具名称和输入 参数。
(4)如果成功解析出工具信息,则调用相应工具并处理返回结果。如果工具返回JSON格式的结果,则尝试解析,否则直接打印文本结果。
(5)根据工具返回结果或原始问题,再次调用LLM以生成最终答案。
(6)最后使用asyncio.run(run(question))逐个处理每个问题。
注意,在这里我们每次处理一个问题都会重新建立与MCP服务器的连接和LLM的交互;这在实际应用中可能不是最高效的方式,但便于演示和测试。
6.3 在线MCP服务器的搭建与使用实战
上面我们介绍了MCP的基本使用和本地化部署的方法,并通过实际编程展示如何在单一机器上完成MCP工具的部署与使用。然而,MCP更多地应用在搭建适配于网络的MCP服务器上,通过网络服务的形式为更多的使用者提供服务。本节将介绍基于网络的MCP服务器的搭建与使用。
6.3.1 在线MCP服务器搭建
我们通过MCP官方提供的配置可以完成MCP服务器的搭建,但是对于新手来说,需要了解和掌握服务器的配置,并对文件的命名与代码的编写要求有一定的了解。为了方便使用MCP在线服务器的搭建,我们可以使用现成的Python库来完成MCP服务器的搭建。首先我们需要安装以下两个Python库,打开终端运行如下命令:
pip install fastapi
pip install fastapi_mcp
fastapi与fastapi_mcp是用于通过网络对MCP服务提供支持的Python库,在安装成功后,我们可以直接移植前面准备的函数,完整的服务器代码如下所示:
from fastapi import FastAPI, HTTPException
from fastapi_mcp import FastApiMCP
from pydantic import BaseModel
from datetime import datetime
初始化FastAPI应用
app = FastAPI(title="AI计算服务", description="提供基础数学计算、时间查询和天气模拟功能")
数学运算端点
@app.get("/add", operation_id="add_numbers")
def add_numbers(a: int, b: int):
"""计算两个整数的和"""
return {"result": a + b}
@app.get("/multiply", operation_id="multiply_numbers")
def multiply_numbers(a: int, b: int):
"""计算两个整数的乘积"""
return {"result": a * b}
时间获取端点
@app.get("/datetime", operation_id="date_time")
def date_time():
"""获取当前系统时间"""
return {"datetime": datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
@app.post("/weather", operation_id="get_weather")
def get_weather(city_name: str):
"""获取指定城市天气信息"""
return {
"city": city_name,
"temperature": "23℃"
}
初始化MCP服务器
mcp = FastApiMCP(
app,
name="Math & Weather API",
description="提供数学运算和天气查询服务的MCP接口",
base_url="http://localhost:8000",
include_operations=[
"add_numbers",
"multiply_numbers",
"date_time",
"get_weather"
]
)
mcp.mount()
if name == "main":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
程序代码较为简单,整体逻辑就是首先定义服务器的名称与服务器描述,之后将不同的工具函数进行注册,并提供访问地址和端口,最后将MCP服务器挂载和启动。直接运行上述代码,结果如图6-4所示。

图6-4 MCP服务器的启动
可以看到此时我们已经正常启动了MCP服务,下面我们将完成客户端的编写。
6.3.2 在线MCP服务的连接和使用
接下来,我们将完成在线MCP服务的连接和使用。同样,我们可以根据前面一节展示的本地MCP客户端,修改代码如下:
import asyncio
import json
import logging
from mcp.client.session import ClientSession
from mcp.client.sse import sse_client
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(name)
工具描述转换器
def transform_tools(tools):
tool_list = \[\]
for tool in tools:
parameters = {}
required = tool.inputSchema.get('required', \[\])
for name, prop in tool.inputSchema'properties'.items():
parametersname = {
'type': prop'type',
'description': prop.get('description', ''),
'required': name in required
}
tool_list.append({
'name': tool.name,
'description': tool.description,
'parameters': parameters
})
return json.dumps(tool_list, ensure_ascii=False, indent=2)
from openai import OpenAI
def ask_llm_deepseek(question, tools_list):
TODO: 实现与LLM的交互逻辑
这里只是一个示例,实际应用中需要根据具体情况进行实现
system_prompt = tools_list + '\n 根据以上描述,用户要求:%s ,请生成一个工具调用命令,要求以json格式输出{"tool":工具名,"tool_input":参数字典},只输出json,不要输出其他内容' % (
question)
client = OpenAI(
api_key="sk-282074c41d594514aee6fd6f179ed292",
base_url="https://api.deepseek.com/beta",
)
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": "Hello"},
],
max_tokens=1024,
temperature=0.99,
stream=False
)
generated_text = response.choices0.message.content # 提取文本内容
return generated_text, client # 返回文本而非response对象
def llm_deepseek(question,client):
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "user", "content": question},
],
max_tokens=1024,
temperature=0.99,
stream=False
)
generated_text = response.choices0.message.content # 提取文本内容
return generated_text
async def main(question = "25+38是多少?"):
async with sse_client("http://localhost:8000/mcp") as streams:
async with ClientSession(streams0, streams1) as session:
await session.initialize()
获取工具列表
tools = (await session.list_tools()).tools
s = transform_tools(tools) +"\n"
response, client = ask_llm_deepseek(question, s)
清理Markdown标记
response = response.strip().replace(''''json', '').replace(''''', '').strip()
print(response)
mtools = json.loads(response) # 现在response是纯JSON字符串
print("mtools: --> ",mtools)
if 'tool' in mtools:
tool_name = mtools'tool'
tool_input = mtools'tool_input'
调用工具
print("调用工具:", tool_name, tool_input)
ret = await session.call_tool(tool_name, tool_input)
if ret:
try:
r = json.loads(ret.content0.text)
except:
r = ret.content0.text
print("工具返回结果:", r)
questions = f"用户的问题是{question},根据{tool_name}的返回结果为:{r},根据以上信息,回答问题。"
r = llm_deepseek(questions,client)
return r
if name == "main":
questions = '计算一下356\*125', '25+38是多少?', '现在的时间是什么时候?', 'Shanghai天气怎么样?', '请给我讲一个笑话'
for question in questions:
response = asyncio.run(main(question))
print(response)
print("--------------------------------------------------------")
从上面代码中可以看到,这里我们主要修改了其中的MCP连接地址,从而完成了MCP工具的调用与结果获取。读者可以自行尝试。
6.4 本 章 小 结
本章全面讲解了DeepSeek工具调用及MCP基本原理,并展示了两者相互调用的高效方法。通过实战演练MCP在构建复杂系统中的作用,以及DeepSeek在特定领域的应用实践,读者能够深刻理解了这两项技术在大数据处理和模型训练中的强大功能。进一步地,本章深入解析了MCP的系统架构,探讨了DeepSeek与MCP的协同优化机制,并展望了它们在大数据和人工智能领域的未来发展趋势。通过本章的学习,读者将全面掌握DeepSeek与MCP的核心技术,为未来的数据处理和模型训练项目奠定坚实基础。
