MCP 与 Function Calling 的关系
MCP 与 Function Calling 的关系
目录
前言
学习 UP 主 马克的技术工作坊 的 MCP 与 Function Calling 的关系 视频,了解下 MCP 与 Function Calling 之间的关系,记录下个人学习笔记,仅供自己参考😄
1. 简述
自 Anthropic 公司推出 MCP 之后,有很多人把 Function Calling 与 MCP 做了类比,其中流传最广最为常见的一种说法便是 MCP 统一了 Function Calling 的协议,有了 MCP 之后 Function Calling 便没有存在的必要了
这种说法到处可见流传极广,甚至还会出现在各类专业的科普视频和科普文章中,乍一看这种说法好像是对的,毕竟这两者做的都是调用工具相关的事情,而 MCP 更加的标准,那肯定是 MCP 取代 Function Calling 了,但实际上这种说法其实是有很大问题的
Function Calling 和 MCP 并不是相互替代的关系而是互补的关系,它们在大模型领域所处的地位完全不同,在未来很长的一段时间里 Function Calling 和 MCP 都将同时存在,各自发挥自己的价值,实际上我们在 MCP 终极指南的基础篇中也提到过 Function Calling 和 MCP 甚至可以同时出现在一个链路中,因为它们在链路中所扮演的角色完全不同
为了帮助大家搞清楚 Function Calling 和 MCP 之间的关系,这篇文章给大家全方位介绍一下 Function Calling,主要是覆盖了 Function Calling 的具体概念、使用方法和协议细节,让大家彻底明白 Function Calling 到底是个什么东西,与 MCP 的关系呢到底是怎么样的
最后,为了验证我的说法,我将演示如何在一个链路中同时使用 Function Calling 和 MCP,相关代码可以在 UP 提供的 GitHub 仓库中找到
2. 什么是 Function Calling
在通常情况下,一个模型训练好了之后它的知识库就有定格了,对于超出知识库范围的答案它无法给你满意的回复,用经典的问答场景举例:

用户问模型一个问题,模型就只能从自己的知识库里面寻找答案,如果用户问的问题超出了模型的知识范围,模型要么回答不知道,要么就胡言乱语
那现在试想一下如果模型有一种方式能够调用天气接口拿到纽约最近几天的天气信息的话,那模型是不是就可以回答这个问题了呢,没错,这也就是 Function Calling 所提供的能力了

简单来说,Function Calling 指的是模型与外部工具交互的一种能力,在这些外部工具中有些可以用来查询天气,有些可以用来查询新闻,有些可以用来发送邮件,这些呢都统称为外部工具。实际上模型自己其实是无法调用工具的,它想调用的话还需要一个中间人来帮它做这件事情,这个我们后面会详细展开,暂时先不必研究

另外工具这种说法比较抽象,换一个词更合适一些,那就是函数,没错,工具本质上就是编程语言里面的函数,还是以我们前面的天气预报来举例,假如说我们想要使用查询天气的工具,那本质上可能就是要调用一个叫做 get_forecast 的函数,你传入所查地区的经纬度就可以拿到对应的天气预报结果,在这个过程里面,我们既可以说是使用了查询天气的工具,也可以说是调用了查询天气的函数,这两种说法都是一样的

再回头想想 Function Calling 这个词,这不就是函数调用吗,所以使用工具就是就是调用函数,说白了 Function Calling 就是模型调用函数的一种能力,在调用函数之后我们不是可以拿到函数的返回结果吗,模型就可以从这个结果中总结答案,再告诉用户纽约明天的天气信息
3. Function Calling 链路分析
是不是还是感觉有些抽象,没关系,我们可以拿 ChatGPT 来举个具体点的例子来辅助理解,首先在输入框中输入纽约明天的天气怎么样,然后点击发送:

此时从界面中可以看出 ChatGPT 先搜索了网络,搜索结束之后才给出了最终的答案,我不清楚它背后是用什么原理完成这一套流程的,毕竟是 Closed AI 嘛,除了早期的模型之外什么也没有 open,不过可以肯定的是这套流程用 Function Calling 的方式肯定是可以完成的,而且呢它大概率用的就是 Function Calling,因为这个技术就是 OpenAI 发扬光大的,所以后面我们就假设它用的就是 Function Calling
我们来画一下整体的链路图,看看它整个链路是怎么运转的:

首先整个链路至少有两个角色:用户和 ChatGPT 应用,大致流程如上图所示,用户发送问题给 ChatGPT 应用,ChatGPT 应用返回答案给用户。当用户的请求发送给 ChatGPT 应用之后会发生很多事情,ChatGPT 应用会调用 OpenAI 服务器,OpenAI 服务器又会调用 GPT-4o 的模型,还有搜索网络等等
所以为了把事情解释清楚,我们需要把 ChatGPT 应用拆分为三个角色,分别是网络搜索函数、OpenAI 服务器和 GPT-4o 模型,到这里为止我们的四大 boss 就算是集齐了,下面我们就要研究下这四个 boss 在这段时间里面到底是干了些什么事情

一开始用户的问题会发送给 OpenAI 服务器,OpenAI 服务器接到问题之后,会把用户的问题转发给模型,不过同时给到模型的不仅仅是用户问题,还有可用的工具比如搜索网络、搜索文件之类的,这其实就是告诉模型用户想知道这个问题的答案,你看看你能不能直接回答,如果可以的话就直接给我答案好了,否则我这里还有些工具你看看能不能用上,想调哪个跟我说
模型接到了 OpenAI 服务器的请求之后先评估了下用户要问明天的天气是吧,那我自己肯定是回答不出来的,我的知识到今年 1 月份就截止了,不过好在这里有个搜索网络的工具啊,我要不就用它先搜索网络吧,于是模型就决定要使用这个工具了,不过注意模型的功能呢只是接受用户的问题返回对应的答案,它本身呢其实是没有使用工具的能力的
那谁能使用工具呢,当然是 OpenAI 服务器咯,所以这里模型仅仅是返回了一个工具使用请求给到了 OpenAI 服务器,告诉它我想要使用搜索网络这个工具,工具参数是纽约明天的天气,请帮我调用一下吧,OpenAI 服务器接到请求之后调用了对应的网络搜索函数,函数执行完毕之后返回了对应的结果
OpenAI 服务器拿到结果之后又紧接着把它发回给了模型,模型接到结果后根据用户问题总结了一份最终的答案,并把它发给了 OpenAI 服务器,OpenAI 服务器再把答案返回给用户,整个对话到此就结束了,这就是我们刚才问完问题之后所发生的事情
那你可能不禁要问 Function Calling 在哪呢,别着急我们不妨先猜猜看,首先 Function Calling 翻译成中文是 “函数调用”,那这个链路里面哪个地方在调用函数呢?没错就是网络搜索函数与 OpenAI 服务器之间的交互,也就是下图中红色框的部分,那 Function Calling 肯定就发生在这个环节里面了

对吗?不对,这个呢就是很多人理解上的第一个错误,也正是这个想法让很多人认为 MCP 会取代 Function Calling,毕竟都在干调用函数的事情嘛
实际上,虽然 Function Calling 这个名字的含义呢是函数调用,但是它本身并没有规定对应的函数到底是如何调用的,所以它跟红色框里面的这一部分几乎没有什么关系
Function Calling 这个名字呢有一定的误导性,我们再回忆一下之前所说的 Function Calling 的概念,Function Calling 是模型调用函数的一种能力,这种能力呢是需要一个中间人去参与的,在我们的链路里面 GPT-4o 是模型,网络搜索函数是函数,OpenAI 服务器就是那个中间人
不过光有这三个角色还不够,想要搞清楚 Function Calling 到底是什么,我们还需要把模型这个角色再具体细化一下:

首先我们这个链路用到了 GPT-4o 模型,但这个地方写 GPT-4o 模型呢并不准确,OpenAI 服务器调用的并不是 GPT-4o 模型,它调用的其实是 GPT-4o 模型的 API,这个 API 呢就是一个 http 接口,这个接口内部所调用的才是真正的 GPT-4o 模型
在 API 拿到请求之后,它会把这个请求做个转换,发给真正的 GPT-4o 模型,GPT-4o 模型返回结果之后,对应的 API 还会再把模型的结果再做一次转换,通过 API 接口发送出去。解析工具调用结果的这一部分呢也是如此,API 先将工具执行结果给到模型,模型解析之后再返回最终答案
那你可能会问,模型的输入和输出还会跟模型 API 的输入和输出不同吗?怎么中间还有个转换呢?对,确实是不同,由于模型训练方式的差异,每个模型能接受的参数格式以及它输出的内容格式都会有所不同,所以这一层转换是必须的
那再回忆一下我们前面说过 Function Calling 指的是模型调用函数的一种能力,模型是 GPT-4o,函数呢也通过参数传给了模型,所以严格来说 Function Calling 是作用在模型 API 和模型之间的这个部分(下图中红色框部分)
具体来说,如果模型能够挑选函数、解析函数、执行结果,并且根据结果给出最终答案的话,那么我们就称模型有 Function Calling 的能力,不过大家基本上都没有跟模型直接打过交道,顶多呢就是调用个模型的 API。实际上对于 GPT-4o 这种闭源模型你也没法跟它打交道,你能做的就是通过模型的 API 来间接的使用 GPT-4o 的模型,所以在大部分情况下我们会把 Function Calling 的概念稍微的变一变,把它变为模型 API 调用函数的能力,具体指代的呢就是 OpenAI 服务器和模型 API 之间的内容(下图中蓝色框部分)

仔细看一看,这两部分其实本质是一样的,都是从函数列表里面挑选函数的能力,只不过一个呢是用模型 API 在挑选,一个呢是用模型在挑选,一般情况下,只要模型有了 Function Calling 的能力对应的模型 API 也必将有 Function Calling 的能力,毕竟模型 API 与模型之间也仅仅只有一些转换逻辑而已
由于大部分情况下我们调用的都是模型的 API 而并非直接调用模型,因此呢我们关注的更多是模型的 API 是否支持 Function Calling,鉴于这个原因,在本文中我们着重讲解的就是模型 API 的 Function Calling 能力,也就是上图中蓝色框的内容,而对于红色框中与真实模型进行的一些交互我们暂时不涉及,所以呢我们就先把这一部分去掉以简化流程,我们现在终于得到了 Function Calling 的作用环节了,就是下图蓝色框显示的这一部分,不过这个推导过程并不简单,如果你还是不太明白的话,可以把这段多看几遍

OK,Function Calling 呢作用在蓝色框这一环节,如果像很多人所说的那样 Function Calling 和 MCP 是互相替代的关系的话,那 MCP 也必然是作用在蓝色框这一环节的,但实际上并不是这样子的
如果你看过 UP 的 MCP 终极指南的基础篇的话,你就会明白 MCP 本质上就是一套函数的发现与调用协议,它只作用在服务器和函数之间,也就是网络搜索函数与 OpenAI 服务器这里,MCP 呢是规定了服务器如何发现函数、如何调用函数,MCP 本身呢其实跟模型并没有什么直接的关系,跟模型有直接关系的是 Function Calling

注意看上图中蓝色框这一部分是 Function Calling 作用的区域,红色框这一部分是 MCP 作用的区域,从这里就可以看出 MCP 与 Function Calling 分别作用在链路的两个环节,发挥的作用各不相同,根本就没有互相替代这一说
另外本文着重讲的是 Function Calling,MCP 的使用和相关原理将一带而过,如果你对 MCP 的运行链路一知半解的话,可以看看 UP 的 MCP 终极指南的基础篇、进阶篇和番外篇三个视频
4. Function Calling 协议内容分析
Function Calling 背后是有一套协议的,这套协议规定了工具列表要怎么传给模型 API,而模型 API 又如何返回所挑选的工具以及对应的参数,各个大模型厂商的 Function Calling 协议大致相同不过有些细微的区别,想要彻底搞懂的话那肯定还是要看一下这个协议到底写了些什么,搞懂协议的最好办法就是截获模型 API 的输入和输出
不过对于前面的示例我们是没有办法截取模型 API 的输入和输出的,毕竟 ChatGPT 应用又没有开源,所以呢 UP 这边借助大模型的力量写了一个与大模型聊天的应用 MarkChat,相关代码开源在 UP 的仓库中:https://github.com/MarkTechStation/VideoCode
在这个应用里面截取了模型 API 的输入输出并且打印到了外部文件中,所以通过分析这个外部文件我们就可以得知 Function Calling 协议到底是个什么样子的,首先来给大家演示一下 MarkChat 的使用方法
先启动服务器:

然后打开 127.0.0.1:5000 这个地址,输入我们的问题:纽约明天的天气怎么样,稍等一会儿我们就拿到了答案

可以看到 MarkChat 的返回一共是包含了两部分的内容,第一部分是调用了一个叫做 search 的工具,在网上搜索了下纽约明天的天气预报并且拿到了工具的调用结果:

第二部分则是模型根据工具的返回结果做出了最终的解答,整个流程很简单,而且跟我们在 ChatGPT 里面看到的流程基本一致,先调工具再做总结
既然了解了整个流程,我们不妨就进去看看代码,分析一下它到底写了些什么,MarkChat 的代码量其实还挺大的,毕竟前后端都有,不过好在大部分的代码我们根本就不用管,因为这里面绝大多数代码跟 Function Calling 都没什么关系,我们着重要看的就是其中的 backend.py 文件
打开这个文件之后我们再找到其中的 process_user_query 函数,当用户问题给到 MarkChat 之后,MarkChat 便会去调用 process_user_query 函数来获取答案,这个函数里面的代码便是 Function Calling 的核心逻辑了,所以我们来仔细研究一下:
class LLMProcessor:
def __init__(self):
self.api_key = OPENROUTER_API_KEY
self.base_url = "https://openrouter.ai/api/v1/chat/completions"
self.headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
self.history = []
def process_user_query(self, query):
self.history.append({"role": "user", "content": query})
first_model_response = self.call_model()
first_model_message = first_model_response["choices"][0]["message"]
self.history.append(first_model_message)
# 检查模型是否需要调用工具
if "tool_calls" in first_model_message and first_model_message["tool_calls"]:
tool_call = first_model_message["tool_calls"][0]
tool_name = tool_call["function"]["name"]
tool_args = json.loads(tool_call["function"]["arguments"])
result = self.execute_tool(tool_name, tool_args)
self.history.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"name": tool_name,
"content": result
})
second_response_data = self.call_model_after_tool_execution()
final_message = second_response_data["choices"][0]["message"]
self.history.append(final_message)
return {
"tool_name": tool_name,
"tool_parameters": tool_args,
"tool_executed": True,
"tool_result": result,
"final_response": final_message["content"],
}
else:
return {
"final_response": first_model_message["content"],
}
pass
首先这里面的 query 参数代表的含义是用户的问题,比如在我们这个例子里面 query 的值就应该是:纽约明天的天气怎么样?,在接到用户的问题之后这个函数会把用户的请求内容放到 self.history 列表里面
从这个变量的名字里面你也可以看出 self.history 代表的就是历史信息,现在 self.history 中只有用户的问题,看起来内容比较少,但是呢后面你就可以看到我们还会把模型 API 的工具选择结果,工具的执行结果都放到 self.history 里面,并在请求模型 API 的时候再发给它
因为模型本身是没有记忆的,所以呢如果模型想要了解之前发生了什么,它就必须要从历史信息里面找,在处理完了 self.history 之后我们调用 call_model 函数,这个函数主要是把用户问题发送给了模型 API 让它来解答
模型 API 呢是从 self.history 这个变量里面获取到用户问题的,具体实现呢我们后面再看,然后呢我们把模型 API 的返回放入到 first_model_response 这个变量中,first_model_response 值呢大致是下面这个样子的:

它代表的含义是第一次调用模型时模型的返回结果,其中的核心内容是这里面的 message,它里面包含了模型想要调用的函数以及对应的调用参数,所以呢我们在代码里面先把 message 提取出来然后再放到历史信息中,然后我们检查下模型是否想要调用工具
如果想的话我们把工具名称和工具参数提取出来然后调用 call_model_after_tool_execution 方法执行工具,再把执行结果放入到历史消息列表里,再次请求模型 API
这次请求的时候模型 API 就可以从历史消息列表里面获取到工具的执行结果,并且给出一个最终答案,我们把这个最终答案抽取出来之后放入到 final_message 变量中,再把 final_message 也加入到历史消息列表里,最后我们把所有的消息都放在一起打包返回
我们之前讲的是有工具的场景,这里的 else 分支呢便是处理没有工具调用的情况,我们的例子暂时不涉及所以就不用管它了,到这里整个方法就告一段落了
让我们先复习一下,总结整个链路里面最重要的几个部分:第一调用模型 API,第二提取工具名称和参数,第三调用工具,第四再次调用模型 API,这次调用的时候模型 API 会拿到工具的执行结果并根据结果做出判断,第五返回模型 API 给出的最终答案,不知道你是否对这五部分有一种似曾相似的感觉呢,想想我们之前画的链路图:

这五步其实跟我们前面所聊的这个链路图是一样的,不过我们用的不是 OpenAI 的服务器而已,我们用的是 MarkChat 的本地服务器,那再回来看看代码,在之前梳理链路的过程中我们漏掉了三个函数的实现,现在来看看这三个函数里面写了些什么,第一个是 call_model 用于在第一次调用模型 API 的时候使用:
def call_model(self):
request_body = {
"model": MODEL_NAME,
"messages": self.history,
"tools": TOOLS,
"stream": False,
}
response = requests.post(
self.base_url,
headers=self.headers,
json=request_body
)
logger.log(f"第一次模型请求:\n{json.dumps(request_body, indent=2, ensure_ascii=False)}\n")
logger.log(f"第一次模型返回:\n{json.dumps(response.json(), indent=2, ensure_ascii=False)}\n")
if response.status_code != 200:
raise Exception(f"API request failed with status {response.status_code}: {response.text}")
return response.json()
它这里面主要是用 post 方法请求了 openrouter 的接口,openrouter 呢是个大模型调用平台,在其中可以调用各个类型的模型 API,包括 OpenAI 的 、Claude 的、Gemini 的等等,UP 在 MCP 终极指南的基础篇里面讲过,当然这里直接换成 OpenAI 的 api 也是可以的,我们的方案与平台无关。因为 openrouter 可以非常方便的切换模型,所以 up 用 openrouter 比较多,这里博主用的 deepseek 进行的演示
这里的 self.base_url 对应的就是 openrouter 的 url,在这个类创建的时候 self.base_url 就已经设置好了,而请求体呢也就是这里面的 request_body,一共是包含四个部分,分别是 model、messages、tools 和 stream,这些字段的含义我们后面会详细阐述,你先不用管
之后呢我们会把模型 API 的请求和返回单独打印到一个文件中,后面呢我们便会查阅这个文件的内容来了解 Function Calling 协议的具体细节,最后我们返回模型 API 给出的最终答案,整个方法就结束了,这是我们漏掉的第一个函数
第二个漏掉的函数叫做 execute_tool:
def execute_tool(self, function_name, args):
if function_name == "search":
# 正常情况下,这里应该调用相关 API 做搜索,为了减少代码的复杂度,
# 这里我们返回一段假的工具执行结果,用以测试
return "纽约市今天的天气是晴天,明天的天气是多云。"
else:
raise ValueError(f"未知的工具名称:{function_name}")
它是用来执行工具的,为了减少代码的复杂度,UP 这里直接造了一个假的数据返回,正常情况下这里是应该调用相应的搜索引擎 API 来做查询,比如可以调个 http 之类的
第三个漏掉的函数是 call_model_after_tool_execution:
def call_model_after_tool_execution(self):
second_request_body = {
"model": MODEL_NAME,
"messages": self.history,
"tools": TOOLS,
}
# Make the second POST request
second_response = requests.post(
self.base_url,
headers=self.headers,
json=second_request_body
)
logger.log(f"第二次模型请求:\n{json.dumps(second_request_body, indent=2, ensure_ascii=False)}\n")
logger.log(f"第二次模型返回:\n{json.dumps(second_response.json(), indent=2, ensure_ascii=False)}\n")
# Check if the request was successful
if second_response.status_code != 200:
raise Exception(f"API request failed with status {second_response.status_code}: {second_response.text}")
# Parse the second response
return second_response.json()
它是在第二次请求模型 API 的时候使用的,跟我们前面所见的 call_model 函数差不多,它呢也是先请求模型 API 再记录日志最后返回,我们就不细看了
回想一下,在每次调用模型 API 的时候我们都会打印两行日志,一个是给模型 API 的请求,另外一个呢是模型 API 的返回,所以呢我们到对应的日志文件里面就应该能够找到四个日志记录,分别对应第一个模型 API 的请求和返回和第二个模型 API 的请求返回

这个就是日志文件的内容了,你现在所看到的部分就是第一次的模型 API 请求,它一共有四个字段,分别是 model、messages、tools 还有一个是 stream,我们的 model 设为了 deepseek-chat 代表我们用的就是这个模型了,messages 代表的是目前发送或者接收到的消息列表
第一次发送请求给模型 API 的时候只有一个用户问题在消息列表里面,后面的 tools 呢则是我们讨论的重点,它是一个列表,其中包含了所有的可用工具,我们只有一个叫做 search 的工具用于搜索网络,后面的 parameters 呢则是一段 Json Schema 用于描述工具的入参
Json Schema 这个概念我们在 MCP 终极指南的进阶篇里面聊到过,这里再简单说下,首先我相信大家一定听说过 Json,Json Schema 呢其实就是对另外一个 Json 结构的描述,比如我们看看下面这个例子:
{
"query": "纽约的天气"
}
它代表的 Json 呢是一个对象,里面有一个属性叫做 query,query 这个属性的值必须是 string,代表的含义是需要搜索的内容,而且 query 这个属性还必须存在不可缺少
对应的 Json 有很多,比如说下面这个:
{
"query": "明天的温度"
}
这些 Json 都满足我们前面所说的那个 Json Schema 的要求,换句话说如果模型 API 像上面这两个例子这样返回工具的参数的话那都是没有问题的
最后的 stream 为 false 代表我们不要流式返回内容,流式的返回结果太复杂会对我们造成干扰,如果你不知道流式返回是什么意思,那也没有关系,这跟我们学习 Function Calling 无关,建议你不用管这个选项,我也只是顺带给你提一下
模型 API 的请求到这里就结束了,那它会返回什么呢,就是下面这个样子:

这个消息比较长,不过大部分都没什么用,我们重点看看其中的 tool_calls 这一部分,这里表明模型想要调用一个叫做 search 的工具,调用的参数如图中所示,模型想要表达的信息呢就仅此而已了,我们前面也说过模型是不会真的去调用这个工具的,它只是会发出调用工具的请求而已,真正调用函数的是我们 MarkChat 服务器也就是代码里面 self.execute_tool 这一部分的内容
执行完毕之后我们把结果放到了 self.history 变量中,并在下一次调用模型 API 的时候带给了它,我们再回到日志文件里面找到第二组模型 API 请求看看模型是不是真的拿到了工具的执行效果:

第二次模型 API 的请求结构与第一次类似,model 是模型名称,messages 是历史消息列表,这两个都是我们在代码里面给它塞进去的,其中第一个多出来的消息呢是之前模型所选择的工具,后面的部分是所挑选的工具名称以及对应的参数,跟之前第一次模型 API 返回结果中的对应部分是一样的,第二个多出来的消息呢则是工具的执行结果
对于模型来说,这也是一个非常关键的消息,因为只有有了它模型才能够回答用户的问题,tools 就是工具列表了,它跟我们第一次请求模型 API 的时候是一样的,这里就不再赘述了,不过要注意一点 tools 虽然传给了模型但是呢模型也可以选择不用工具,实际上模型第二次的返回结果中就不会再选择使用工具了
我们再来看看模型 API 对第二次请求的响应:

同样大部分的内容都跟我们无关,我们就看下面这里的 message,其中的 content 所代表的就是模型给出的最终答案,这个答案呢就是根据之前工具的执行结果总结出来的,整个日志到这里也就结束了
所以呢总结一下什么是 Function Calling,Function Calling 就是模型能够使用工具的一种能力,想要更具体一点的话我们可以用刚才这个日志内容来举例,如果模型能够解析请求参数中的工具列表、字段,能够从中挑选出要调用的工具名称和工具参数,能解析工具的执行结果,那么我们就称这个模型是具有 Function Calling 的能力
当然正如我前面所说,你把这个定义中的模型换成模型 API 也是可以的,这两个定义的本质是一样的,只不过是站在不同的角度去定义 Function Calling
5. 同时使用 Function Calling 和 MCP
那如果说我们希望让这条链路也用上 MCP 的话,应该修改代码里面的哪个部分呢
def execute_tool(self, function_name, args):
if function_name == "search":
# 正常情况下,这里应该调用相关 API 做搜索,为了减少代码的复杂度,
# 这里我们返回一段假的工具执行结果,用以测试
return "纽约市今天的天气是晴天,明天的天气是多云。"
else:
raise ValueError(f"未知的工具名称:{function_name}")
我们直接修改项目里面的 execute_tool 这个函数就可以了,之前说过为了简化代码,我们这里是返回了一个假的天气信息,但是呢在真实的链路里面这里可能是调用了一个 http 接口,可能是调用了一个外部程序,也可能是调用了一个 SDK 的函数
但不管怎么样 Function Calling 呢是没有规定这个地方到底是如何来写的,那究竟是谁规定了呢,相信你已经想到答案了,对没错,就是 MCP
MCP 规定了一套统一的工具发现与调用协议,如果你想要让这个链路用上 MCP 的话,你只需要按照 MCP 的规范在这个地方调用 MCP 的工具就可以了
其实这个调用 MCP 工具的函数 UP 也帮我们写好了叫做 execute_tool_with_mcp,我们把原来的这个 execute_tool 换成 execute_tool_with_mcp 就行了,execute_tool_with_mcp 内部呢会启动一个 MCP Client,这个 MCP Client 呢会连接我们项目目录里面写好的 MCP server,对应的就是 mcp_server.py 这个文件:
from mcp.server.fastmcp import FastMCP
# Initialize FastMCP server
mcp = FastMCP("search_mcp_server", log_level="ERROR")
# Constants
@mcp.tool()
async def search(query: str) -> str:
"""搜索网络
Args:
query: 搜索内容
"""
# 正常情况下,这里应该调用相关 API 做搜索,为了减少代码的复杂度,
# 这里我们返回一段假的工具执行结果,用以测试
return "来自 MCP Server 的答案:纽约市今天的天气是晴天,明天的天气是多云。"
if __name__ == "__main__":
# Initialize and run the server
mcp.run(transport='stdio')
当然这个 MCP server 的返回数据呢也是假的,但是前面有一个标识表明这个是从 MCP Server 这个地方调用过来的,注意,虽然说这个数据是假的,但是呢这个链路确实是真的
我们改成 MCP 工具调用之后呢,再次运行一下这个程序,然后再回到页面中,再问一下刚才的问题

可以看到模型正常返回了,看一下工具的调用信息可以发现调用的确实是 MCP server,这样呢我们就实现了在一个链路里面同时使用 MCP 和 Function Calling 的效果
你可能会对 MCP Clinet 和 MCP Server 如何实现比较好奇,篇幅原因我们这里就不细说了,大家感兴趣的可以自己看看,代码并不复杂
结语
这篇文章我们跟随 UP 学习了 Function Calling,并分析了 Function Calling 和 MCP 的区别及关系
Function Calling 就是模型能够使用工具的一种能力,如果模型能够挑选函数、解析函数、执行结果,并且根据结果给出最终答案的话,那么我们就称模型有 Function Calling 的能力,而 MCP 则规定了一套统一的工具发现与调用的协议,Function Calling 和 MCP 可以在同一个链路中使用,它们各自发挥的功能并不相同,
OK,以上就是本篇文章的全部内容了
大家感兴趣的可以看看 UP 的讲解,还是非常不错的🤗
参考
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)