摘要:MCP、Function Calling、OpenAPI三种AI工具调用方式的深度对比,从协议规范、架构模型、通信方式、跨厂商复用、双向交互等八个维度逐项拆解,附选型决策表。
MCP vs Function Calling vs OpenAPI 协议对比与选型
上周有个同事问我,给模型接外部工具到底该用 Function Calling、OpenAPI 还是 MCP。我让他把同一个天气查询工具用三种方式各写一遍,写完他自己就明白了。这篇把这个对比做透,从协议规范到开发体验逐项拆解,最后给一张选型表,让你面对新项目能快速判断。
三种方式各是什么
Function Calling 是大模型厂商提供的原生能力。你在对话请求里塞一段工具的 JSON Schema,模型判断需要调用时直接吐出结构化的函数名和参数,你的代码拿到后去执行,再把结果喂回模型。它没有独立的服务端概念,工具定义和调用都揉在一次对话请求里,绑定具体厂商的 API 格式。
OpenAPI 是描述 REST API 的业界规范,本来跟大模型没关系。一个 OpenAPI 文档把 HTTP 接口的路径、方法、参数、响应写清楚,任何 HTTP 客户端都能照着调用。现在很多 Agent 框架会把 OpenAPI 文档自动转成模型能理解的工具定义,让模型直接调用现成的 REST 接口。
MCP 是专为 LLM 设计的开放协议。它定义了客户端和服务端的架构,服务端独立运行,暴露工具、资源、提示三类原语,客户端动态发现并调用。MCP 有完整的生命周期、传输层抽象和双向通信能力,服务端写一次可以被任意 MCP 客户端复用。
多维度对比
下面这张表从八个维度横向对比三者。
| 维度 | Function Calling | OpenAPI | MCP |
|---|---|---|---|
| 协议性质 | 厂商私有能力 | REST 描述规范,面向 HTTP | 面向 LLM 的开放协议 |
| 架构模型 | 模型内嵌,无服务端 | HTTP 端点,无状态 | 客户端-服务端,独立进程 |
| 工具定义 | JSON Schema 塞进请求 | OpenAPI 文档 | 运行时动态发现(list_tools) |
| 通信方式 | 单次请求响应 | HTTP 请求响应 | 双向,支持通知、流式、采样 |
| 状态管理 | 无状态 | 无状态(REST) | 有会话和生命周期 |
| 跨厂商复用 | 差,格式各家不同 | 好,但需转换层 | 好,一次实现多客户端复用 |
| 双向交互 | 不支持 | 不支持 | 支持,服务端可反向请求客户端 |
| 长任务进度 | 无 | 无 | 有进度通知 |
| 生态成熟度 | 各厂商各自实现 | 极成熟,工具链丰富 | 新生态,增长快 |
| 安全模型 | 客户端自行实现 | 标准 HTTP 安全 | 能力协商加传输层安全加 Origin 校验 |
| 扩展方式 | 改 prompt 改 schema | 改文档 | 服务端独立扩展,客户端无感 |
几条关键差异展开说。复用性上 Function Calling 最弱,OpenAI 和 Anthropic 的工具定义格式细节不同,换模型经常要改。MCP 最强,服务端跟模型解耦,Claude、Cursor、自研客户端都能接同一个服务端。双向交互是 MCP 独有的,服务端能通过采样反向让客户端的模型干活,Function Calling 和 OpenAPI 都做不到。长任务进度上报也只有 MCP 原生支持,另外两者要自己在外部加一套机制。
开发体验上三者各有脾性。Function Calling 上手最快,一个请求里塞 schema 就能跑,但工具多了请求体会膨胀,调试也全靠看模型输出的 JSON。OpenAPI 有成熟的编辑器和文档工具,写接口顺手,可它本来是给人看的,转成模型工具时要裁剪参数和描述,转换层得自己维护。MCP 配合 FastMCP 装饰器,写普通函数就能发布工具,schema 自动生成,还自带 Inspector 可视化调试,工具多了也不乱。学习曲线 MCP 稍陡,要理解客户端服务端和生命周期这些概念,但换来的是后续扩展省心。
同一个工具的三种写法
我用一个天气查询工具做对照,三种方式各写一遍,差异一目了然。
完整代码
先装依赖。
pipinstallopenai httpx fastmcpFunction Calling 方式,用 OpenAI SDK 把工具定义塞进请求。
# weather_function_calling.py# Function Calling 方式,工具定义揉在对话请求里importjsonfromopenaiimportOpenAI# 初始化客户端,API key 从环境变量读client=OpenAI()# 工具定义,JSON Schema 格式,绑定 OpenAI 的 tools 字段tools=[{"type":"function","function":{"name":"get_weather","description":"查询指定城市的天气",# 参数 schema,模型据此生成参数"parameters":{"type":"object","properties":{"city":{"type":"string","description":"城市名"},},"required":["city"],},},}]defget_weather(city:str)->str:"""本地实现,真实项目换成调用气象 API。"""# 简化实现,返回固定字符串returnf"{city}今天晴,25 度"defmain():# 第一轮对话,把工具定义一起发过去response=client.chat.completions.create(model="gpt-4o-mini",messages=[{"role":"user","content":"北京天气怎么样"}],tools=tools,)msg=response.choices[0].message# 模型决定调用工具时,tool_calls 里会有调用信息ifmsg.tool_calls:call=msg.tool_calls[0]# 解析模型生成的参数args=json.loads(call.function.arguments)# 本地执行工具result=get_weather(args["city"])# 把结果喂回模型做第二轮follow=client.chat.completions.create(model="gpt-4o-mini",messages=[{"role":"user","content":"北京天气怎么样"},msg,{"role":"tool","tool_call_id":call.id,"content":result},],)print(follow.choices[0].message.content)else:print(msg.content)if__name__=="__main__":main()OpenAPI 方式,先用 FastAPI 起一个带 OpenAPI 文档的 HTTP 服务,再用 httpx 照着文档调用。
# weather_openapi_server.py# OpenAPI 方式,工具就是一个标准 REST 接口fromfastapiimportFastAPI app=FastAPI(title="Weather API")@app.get("/weather",summary="查询城市天气")defweather(city:str):"""GET 接口,FastAPI 自动生成 OpenAPI 文档。"""# 真实项目换成气象 API 调用return{"city":city,"condition":"晴","temperature":25}# 启动后访问 /openapi.json 能拿到完整 OpenAPI 文档# uvicorn weather_openapi_server:app --port 8000# weather_openapi_client.py# OpenAPI 方式的客户端,照着文档调 HTTP 接口importhttpxdefmain():# 直接按文档定义的路径和方法调用# Agent 框架会读 /openapi.json 自动转成模型工具withhttpx.Client()asclient:resp=client.get("http://127.0.0.1:8000/weather",params={"city":"北京"},)# 解析 JSON 响应data=resp.json()print(f"{data['city']}{data['condition']},{data['temperature']}度")if__name__=="__main__":main()MCP 方式,用 FastMCP 把工具包成独立服务端。
# weather_mcp_server.py# MCP 方式,工具作为独立服务端运行,可被任意 MCP 客户端复用fromfastmcpimportFastMCP# 创建服务端,工具定义由装饰器自动生成mcp=FastMCP("WeatherServer")@mcp.tooldefget_weather(city:str)->str:"""查询指定城市的天气。 Args: city: 城市名 """# 真实项目换成气象 API 调用returnf"{city}今天晴,25 度"if__name__=="__main__":# 默认 stdio 传输,Claude Desktop 等客户端可直接接入mcp.run()# weather_mcp_client.py# MCP 方式的客户端,动态发现并调用工具importasynciofromfastmcpimportClientasyncdefmain():# 连接服务端,自动走 stdioasyncwithClient("weather_mcp_server.py")asclient:# 动态列出可用工具,不需要预先知道有哪些tools=awaitclient.list_tools()print("发现工具:",[t.namefortintools])# 调用天气工具result=awaitclient.call_tool("get_weather",{"city":"北京"})print(result.data)if__name__=="__main__":asyncio.run(main())选型建议
不同场景的选型我整理成一张表。
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 单一模型、少量简单工具 | Function Calling | 零额外架构,最快上线 |
| 已有大量 REST API 想给模型用 | OpenAPI 加转换层 | 复用现有接口,不用重写 |
| 多模型多客户端、要共享工具 | MCP | 一次实现多方复用,解耦模型 |
| 长任务需要进度上报 | MCP | 原生支持进度通知 |
| 需要服务端反向调用模型(采样) | MCP | 独有双向能力 |
| 纯本地工具、桌面集成 | MCP(stdio) | 标准化、安全可控 |
| 快速原型验证 | Function Calling | 门槛最低 |
| 企业级多团队工具市场 | MCP | 服务端独立部署、便于统一管理 |
实际项目里三者经常组合用。用 MCP 做工具服务端的统一出口,内部工具可以是 Function Calling 风格的封装,也可以是包了一层的 OpenAPI 接口。MCP 服务端充当适配层,对上屏蔽模型差异,对下兼容遗留接口。三者可以共存,按需混搭。
常见问题与避坑
1. Function Calling 换模型工具定义要重写。OpenAI 的 tools 字段、Anthropic 的 tool_use、Gemini 的 functionDeclarations 格式细节都不同,必填字段和枚举处理也有差异。我有个项目从 GPT 迁到 Claude,工具定义改了一整天。用 MCP 把工具抽到服务端,模型侧只管调 MCP 客户端,迁移成本就低多了。
2. OpenAPI 文档直接喂模型 token 爆炸。一个中型项目的 OpenAPI 文档动辄几万字,全塞进上下文既贵又乱。实际用要先按需筛选端点,再做参数裁剪,只暴露模型用得上的接口。别图省事把整个文档丢给模型。
3. MCP 服务端被多客户端复用时工具命名冲突。不同服务端的工具可能撞名,比如都叫 search。客户端聚合多个服务端时加前缀区分,FastMCP 的多服务端配置会自动加服务端名前缀,自己拼要注意。
4. Function Calling 没有资源概念,文件类上下文只能塞 prompt。想让模型读一个大文件,Function Calling 只能把内容拼进消息,token 占用高。MCP 用资源原语让模型按需读取,配合分页能省大量 token。
5. OpenAPI 做不了长任务进度。REST 是无状态请求响应,长任务只能轮询或靠 WebSocket 自己造一套。MCP 的进度通知是协议内置的,省掉自己造轮子。
小结
Function Calling 适合单一模型快速上手,OpenAPI 适合复用现成 REST 接口,MCP 适合多模型多客户端共享工具生态和需要双向交互的场景。三者可以混搭,MCP 常作为统一出口把另外两者整合进来。选型核心看复用性、双向交互和长任务需求这三个点。下一篇进入 Python MCP SDK 的实操,看 FastMCP 怎么把服务端开发做到几行代码搞定。
相关推荐
- MCP协议全景:Host、Client、Server架构详解
- Tools原语深度解析:从定义到调用全流程
- MCP是什么?为什么2026年每个AI开发者都需要了解它