MCP vs Function Calling:AI工具调用的两种方案
本文对比了两种AI调用外部工具的主流方案:Function Calling和MCP(Model Context Protocol)。Function Calling是OpenAI提供的集成方案,特点为内嵌式、零配置,适合快速开发;MCP则是Anthropic推出的开放协议,采用客户端-服务器架构,支持复杂交互。通过天气查询场景的代码示例,展示了两种方案的具体实现:Function Calling通
·
让AI调用外部工具是构建智能应用的关键能力。本文对比两种主流方案:Function Calling和MCP,帮助你快速选择适合的技术方案。
一、核心概念
Function Calling是什么?
Function Calling是OpenAI等LLM提供商推出的能力,让模型能够理解何时需要调用外部函数,并生成符合格式的调用指令。
核心特点:
- 📦 内嵌式集成,直接在API调用中传递函数定义
- 🚀 零配置启动,无需额外服务器
- 🎯 简单直接,适合快速开发
MCP是什么?
**MCP(Model Context Protocol)**是Anthropic推出的开放协议标准,提供客户端-服务器架构的AI工具调用方案。
核心特点:
- 🌐 开放标准,跨平台跨模型
- 🔌 插件化架构,高度可复用
- 🔄 持久连接,支持复杂交互
二、架构对比
Function Calling架构
用户请求 → LLM(含函数定义) → 生成函数调用 → 应用执行 → 结果返回LLM → 生成响应
特点:请求-响应模式,无状态
MCP架构
AI应用
↓
MCP客户端 ⟷ MCP服务器 ⟷ 外部资源
↑ (持久连接)
JSON-RPC 2.0
特点:客户端-服务器模式,有状态连接
三、快速代码对比
示例场景:天气查询助手
Function Calling实现
import OpenAI from "openai";
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// 1. 定义函数
const tools = [
{
type: "function",
function: {
name: "get_weather",
description: "获取指定城市的天气信息",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "城市名称,如:北京、上海"
},
unit: {
type: "string",
enum: ["celsius", "fahrenheit"],
default: "celsius"
}
},
required: ["location"]
}
}
}
];
// 2. 实现函数逻辑
async function getWeather(location: string, unit: string = "celsius") {
// 实际应用中调用天气API
return {
location,
temperature: 15,
condition: "晴",
unit
};
}
// 3. 主流程
async function chat(userMessage: string) {
const messages = [
{ role: "user", content: userMessage }
];
// 第一次调用:让LLM决定是否调用函数
const response = await openai.chat.completions.create({
model: "gpt-4",
messages: messages,
tools: tools // 每次都要传递函数定义
});
const message = response.choices[0].message;
// 如果需要调用函数
if (message.tool_calls) {
for (const toolCall of message.tool_calls) {
const functionName = toolCall.function.name;
const args = JSON.parse(toolCall.function.arguments);
// 执行函数
const result = await getWeather(args.location, args.unit);
// 将结果添加到消息历史
messages.push(message);
messages.push({
role: "tool",
tool_call_id: toolCall.id,
content: JSON.stringify(result)
});
}
// 第二次调用:生成最终响应
const finalResponse = await openai.chat.completions.create({
model: "gpt-4",
messages: messages
});
return finalResponse.choices[0].message.content;
}
return message.content;
}
// 使用
await chat("北京今天天气怎么样?");
MCP实现
MCP服务器代码:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
// 创建MCP服务器
const server = new Server(
{
name: "weather-server",
version: "1.0.0"
},
{
capabilities: {
tools: {} // 声明支持工具能力
}
}
);
// 1. 列出可用工具
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_weather",
description: "获取指定城市的天气信息",
inputSchema: {
type: "object",
properties: {
location: {
type: "string",
description: "城市名称"
},
unit: {
type: "string",
enum: ["celsius", "fahrenheit"],
default: "celsius"
}
},
required: ["location"]
}
}
]
};
});
// 2. 处理工具调用
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "get_weather") {
const { location, unit = "celsius" } = request.params.arguments;
// 实际应用中调用天气API
const weather = {
location,
temperature: 15,
condition: "晴",
unit
};
return {
content: [
{
type: "text",
text: JSON.stringify(weather)
}
]
};
}
});
// 3. 启动服务器
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("天气MCP服务器已启动");
}
main();
MCP客户端使用:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { spawn } from "child_process";
// 启动MCP服务器进程
const serverProcess = spawn("node", ["./weather-server.js"]);
const transport = new StdioClientTransport({
reader: serverProcess.stdout,
writer: serverProcess.stdin
});
const client = new Client(
{ name: "weather-client", version: "1.0.0" },
{ capabilities: {} }
);
await client.connect(transport);
// 一次连接后可以多次使用
const tools = await client.listTools();
console.log("可用工具:", tools);
// 调用工具
const result = await client.callTool({
name: "get_weather",
arguments: { location: "北京" }
});
console.log("天气信息:", result);
四、多维度差异对比
1. 架构差异
| 维度 | Function Calling | MCP |
|---|---|---|
| 架构模式 | 内嵌式 | 客户端-服务器 |
| 连接方式 | HTTP请求-响应 | 持久连接(Stdio/SSE/WebSocket) |
| 状态管理 | 无状态 | 有状态会话 |
| 协议标准 | 专有API | 开放标准(JSON-RPC 2.0) |
2. 功能差异
| 维度 | Function Calling | MCP |
|---|---|---|
| 能力范围 | 仅工具调用 | 工具+资源+提示 |
| 上下文注入 | 通过消息历史 | 原生资源机制 |
| 服务器主动调用LLM | ❌ 不支持 | ✅ 支持(采样) |
| 跨应用复用 | ❌ 困难 | ✅ 容易 |
| 实时推送 | ❌ 不支持 | ✅ 支持 |
3. 开发体验差异
| 维度 | Function Calling | MCP |
|---|---|---|
| 集成复杂度 | ⭐ 低 | ⭐⭐⭐ 中等 |
| 学习曲线 | ⭐ 平缓 | ⭐⭐⭐ 较陡 |
| 初期开发速度 | 🚀 快 | 🐢 慢 |
| 长期维护性 | 👎 一般 | 👍 好 |
| 调试工具 | LLM提供商工具 | MCP Inspector |
4. 性能差异
| 维度 | Function Calling | MCP |
|---|---|---|
| 单次调用延迟 | ✅ 低 | ⚠️ 略高(首次连接) |
| 多次调用开销 | ❌ 每次重新建立上下文 | ✅ 复用连接 |
| Token消耗 | ❌ 每次传递函数定义 | ✅ 仅首次握手 |
| 并发处理 | ✅ 无状态易扩展 | ⚠️ 需要连接管理 |
五、使用场景对比
Function Calling适合的场景
✅ 快速原型开发
// 场景:快速验证想法,几个简单API调用
// 优势:直接写函数定义,立即可用
const tools = [weatherTool, calculatorTool];
✅ 轻量级集成
// 场景:调用几个RESTful API
// 优势:无需额外服务器,代码简洁
async function callExternalAPI() {
const response = await fetch("https://api.example.com/data");
return response.json();
}
✅ 简单工具调用
- 天气查询
- 单位转换
- 简单计算
- 数据库单表查询
✅ 个人项目/小型应用
- MVP阶段快速验证
- 个人开发者项目
- 无需跨应用复用
MCP适合的场景
✅ 企业级系统集成
// 场景:连接多个企业系统(CRM、ERP、数据库)
// 优势:标准化接口,一次实现多处使用
// MCP服务器可以被多个AI应用共享
✅ 复杂文档处理
// 场景:大量文档的读取、分析、搜索
// 优势:资源机制按需加载,不占用上下文
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: documentsList.map(doc => ({
uri: `file:///${doc.path}`,
name: doc.name,
mimeType: "text/plain"
}))
};
});
✅ 需要服务器主动决策
// 场景:工具执行过程中需要LLM辅助决策
// 优势:服务器可以主动请求LLM(Function Calling无法实现)
const aiSuggestion = await client.request({
method: "sampling/createMessage",
params: {
messages: [{ role: "user", content: "分析这个数据..." }]
}
});
✅ 构建工具生态
- 跨多个AI应用复用
- 标准化集成方案
- 开源工具库
✅ 实时数据流处理
- 日志监控
- 数据流分析
- 实时通知
六、实战案例:数据库查询助手
需求
构建一个AI助手,能够查询数据库、分析数据、生成报表。
Function Calling实现的问题
// ❌ 问题1:每次都要传递大量函数定义
const databaseTools = [
listTablesFunction, // 列出表
describeTableFunction, // 查看表结构
queryFunction, // 执行查询
analyzeFunction, // 分析数据
generateChartFunction // 生成图表
// ... 可能有几十个函数
];
// ❌ 问题2:多次往返LLM
// 第1次:列出表 → 第2次:查看结构 → 第3次:执行查询 → 第4次:生成图表
// 每次都是完整的LLM调用,延迟高、成本高
// ❌ 问题3:无法提供数据库架构上下文
// LLM必须先调用函数才能知道有哪些表和字段
MCP实现的优势
// ✅ 优势1:数据库架构作为资源暴露
server.setRequestHandler(ListResourcesRequestSchema, async () => {
// LLM可以直接"看到"整个数据库结构
return {
resources: [
{
uri: "db://schema",
name: "数据库架构",
description: "包含所有表和字段信息"
},
...tables.map(table => ({
uri: `db://table/${table.name}`,
name: table.name,
description: `表:${table.name}`
}))
]
};
});
// ✅ 优势2:一次工具调用完成复杂分析
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "analyze_sales") {
// 服务器端执行多步骤逻辑
const data = await queryDatabase(sql);
const chart = await generateChart(data);
const trend = calculateTrend(data);
// ✅ 优势3:服务器可以请求LLM生成洞察
const insights = await requestLLM({
prompt: `分析以下销售数据趋势:${JSON.stringify(trend)}`
});
// 一次返回所有结果
return {
content: [{
type: "text",
text: JSON.stringify({ data, chart, trend, insights })
}]
};
}
});
效果对比
| 指标 | Function Calling | MCP |
|---|---|---|
| LLM调用次数 | 4-5次 | 1次 |
| 总延迟 | ~8-10秒 | ~2-3秒 |
| Token消耗 | 高(每次传递函数定义) | 低(仅首次) |
| 用户体验 | 等待时间长 | 快速响应 |
七、选型决策指南
快速决策流程图
开始选型
│
├─ 项目规模小、快速开发? → 选择 Function Calling
│
├─ 需要跨多个应用复用? → 选择 MCP
│
├─ 工具少于5个且简单? → 选择 Function Calling
│
├─ 需要处理大量文档/数据? → 选择 MCP
│
├─ 需要实时数据流? → 选择 MCP
│
├─ 首次接触AI开发? → 选择 Function Calling
│
└─ 构建企业级平台? → 选择 MCP
详细选型建议
| 你的情况 | 推荐方案 | 原因 |
|---|---|---|
| 个人开发者,首次项目 | Function Calling | 学习成本低,快速上手 |
| 创业公司MVP阶段 | Function Calling | 快速验证,降低初期成本 |
| 企业内部AI平台 | MCP | 标准化,长期可维护 |
| 开源工具开发 | MCP | 生态友好,易于集成 |
| 简单聊天机器人 | Function Calling | 无需复杂架构 |
| 智能文档管理系统 | MCP | 资源机制优势明显 |
| 数据分析平台 | MCP | 减少往返,性能更好 |
| 工具少于10个 | Function Calling | 简单直接 |
| 工具超过20个 | MCP | 避免上下文窗口压力 |
| 需要多模型支持 | MCP | 开放标准,不锁定 |
八、混合使用策略
实际项目中,两者可以结合使用:
策略1:核心用MCP,边缘用Function Calling
// MCP:频繁使用的核心功能
// - 数据库访问
// - 文档处理
// - 业务系统集成
// Function Calling:偶尔使用的简单功能
// - 天气查询
// - 单位转换
// - 简单API调用
策略2:渐进式迁移
// 阶段1:全部使用Function Calling快速开发
const tools = [tool1, tool2, tool3, ...];
// 阶段2:识别高频使用的工具
// 发现tool1和tool2使用频繁,且被多个应用需要
// 阶段3:将高频工具重构为MCP服务器
// 其他工具保持Function Calling
九、总结
Function Calling:快速简单的实用主义
适合:
- 🚀 快速原型开发
- 🎯 简单工具调用
- 👤 个人开发者
- 💡 首次AI项目
核心优势: 零配置、快速上手、无需基础设施
MCP:标准化的长期主义
适合:
- 🏢 企业级应用
- 🔄 工具生态构建
- 📚 复杂上下文管理
- 🌐 跨应用复用
核心优势: 开放标准、高度复用、强大扩展性
关键建议
- 没有绝对的"更好":根据场景选择合适的技术
- 从简单开始:不确定时先用Function Calling
- 必要时升级:发现复用需求时迁移到MCP
- 可以混用:不是非此即彼的关系
未来展望
- Function Calling会持续优化,与特定LLM深度整合
- MCP生态会快速增长,成为AI工具的"插件标准"
- 两者可能走向融合,出现统一的工具调用标准
参考资源
Function Calling
MCP
更多推荐
所有评论(0)