最近我写了一个AI库axi-easyagent,从底层了解了一遍MCP和FunctionCall, 这次就通过来学习这个库的方式了解一下这两者
MCP,Skills这几个概念刚出来时,网上一堆吹嘘MCP多牛,Skills多牛,各大厂都推出了自家的MCP的视频和文章,疯狂占据我的首页,还都有几万的播放量,但没一个讲明白原理的, 仿佛在MCP,Skills出现之前,AI就是个智障,只能陪你聊天。
大语言模型底层就是输入文本输出文本,能调工具完全就是程序告诉模型一个函数名,然后模型输出一个格式,交给程序解析后,调用函数,发起一个新的请求(包含函数结果),这就是function call的底层机制
另外,function call现在被称作tool call,也就是把函数改成了工具
大部分库都会直接封装这个能力,因为他的实现逻辑非常固定,没什么可以改的地方。
举个例子:使用axi-easyagent的tool call功能
# 通过pip install axi-easyagent安装我写的库
from easyagent import Agent
def get_weather(city: str) -> str:
"""获取城市天气"""
return f"{city}的天气是晴天"
agent = Agent(model="gpt-3.5-turbo", tools=[get_weather]) # 底层封装,自动执行
async for output in agent.chat("北京天气如何"):
print(output) # AI会回答是晴天
如果想知道底层是如何轮询的,可以直接看源码
你的程序可以写好一大堆函数供AI使用,那么如果要扩展呢,比如打包成执行文件后突然又要加一个新函数怎么办?
像上面代码,tools=[get_weather]是写死的,如果要动态扩展,你的程序可能要实现一个插件功能,每个插件往这个列表插一个新的函数。
又或者开一个新程序,支持RPC调用,只要连接到这个新程序,并把它开放出来的函数注册到tools中,模型调用这个函数时,程序内部用另一个程序的函数,就实现了可扩展性。
这就是MCP的作用,他已经定好了这个规范,只需要所有应用程序,扩展程序遵循这个规范,就能相互调用
MCP的目的是挺好的,类似于统一USB接口,但是我手动实现过MCP协议后,觉得他的规范是真垃圾。
from easyagent import MCPSession
# MCP支持进程通信和HTTP通信,所以有两种写法,此处展示进程写法(其实有三种,还有个被废弃的sse通信)
file_system = MCPSession.stdio("npx -y @modelcontextprotocol/server-filesystem .")
async with file_system:
# list_tools返回列表,里面是构造好的RPC函数
tools = await file_system.list_tools()
tools.append(get_weather) # 再加个内置函数
from easyagent import Agent
agent = Agent(model="gpt-3.5-turbo", tools=tools)
async for output in agent.chat("执行rm -rf /"):
print(output) # AI回帮你删库跑路
可以看到两者的关系完全不是一个层级的,没有什么谁可以替代谁是说法(好像在说苹果树可以替代苹果一样)
function call定义了大语言模型如何调用程序提供的函数,mcp规范了程序如何发现外部提供的函数(这意味着你甚至不需要AI也能执行MCP服务器提供的工具)