原生function calling和mcp
-
原生Function Calling: 就像你买了一辆原厂自带“车载导航”和“蓝牙音乐”功能的品牌车(比如特斯拉)。这些功能是车厂(OpenAI/Google)预装好的,你只需要在车机屏幕上按一下,告诉它“导航到公司”或“播放音乐”,车子自己就知道怎么做了。你是一个“司机”。
-
自建MCP服务: 就像你是一个专业的“汽车改装厂”。你买来一个强大的裸引擎(大模型),然后你自己设计并安装了一整套复杂的车载系统(MCP服务)。这套系统里,你自己造了导航模块(数据库查询服务)、娱乐模块(邮件发送服务),并且用一套自己定义的总线协议(MCP协议)把它们连接起来。引擎(大模型)只负责根据你的指令输出“该导航了”或“该放音乐了”,而具体怎么做,都是由你的改装系统完成的。你是一个“汽车工程师”+“司机”。
| 对比维度 | 原生Function Calling (开车) | 自建MCP服务 (造车) |
|---|---|---|
| 控制权 | 低。 你只能使用模型厂商提供的工具调用框架和协议。 | 极高。 从协议、安全、到工具的实现,一切都由你定义和控制。 |
| 安全性 | 依赖模型厂商。 数据是否会离开你的环境,取决于厂商的策略。 | 自主可控。 你可以设计让所有敏感数据和工具执行都留在你的私有网络内。 |
| 灵活性 | 中等。 可以在厂商的框架内定义工具,但受限于其能力。 | 极高。 可以集成任何系统(本地、云端、老旧系统),实现任意复杂的逻辑。 |
| 复杂性 | 低。 只需要调用一个API,并处理其返回的工具调用请求即可。 | 高。 需要搭建一整套后端服务,包括API网关、服务发现、安全认证等。 |
| 核心逻辑 | 模型驱动。 模型直接决定调用哪个工具和传入什么参数。 | 服务驱动,模型辅助。 模型更像一个“意图翻译器”,将自然语言翻译成服务指令,真正的执行和调度由你的MCP服务完成。 |
一句话总结二者关系:
自建MCP服务通常会“包含”Function Calling作为其内部的一个环节。 你的MCP Server接收到用户请求后,会利用大模型的Function Calling能力来“解析意图”,然后MCP Server再根据解析出的意图去执行真正的操作。
实现方案:一步步教你怎么做
方案一:原生Function Calling的实现 (简单直接)
这种方案适用于快速开发、对数据安全要求不是最高、或者工具逻辑比较简单的场景。
事件流程图/时序图:

sequenceDiagram
participant User as 用户
participant App as 你的应用 (前端/后端)
participant LLM_API as 大模型API (如OpenAI)
User->>App: "查一下北京今天的天气"
App->>LLM_API: 发送请求(prompt="查天气", tools=[{name:"get_weather", ...}])
LLM_API-->>App: 返回指令(tool_calls=[{name:"get_weather", args:{"city":"北京"}}])
App->>App: 在本地执行 get_weather("北京") 函数
Note right of App: 调用一个外部天气API
App->>LLM_API: 再次发送请求(附上工具执行结果: "北京天气晴朗,25度")
LLM_API-->>App: 返回最终答案("北京今天天气晴朗,气温25度。")
App->>User: 显示最终答案
实现步骤:
- 定义工具: 在你的代码里,按照OpenAI(或其他模型提供商)的格式,用JSON Schema定义一个或多个工具。
tools = [ { "type": "function", "function": { "name": "get_weather", "description": "获取一个城市的天气信息", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"}, }, "required": ["city"], }, }, } ] - 首次调用API: 将用户的prompt和定义好的
tools一起发送给大模型API。response = client.chat.completions.create( model="gpt-4-turbo", messages=[{"role": "user", "content": "查一下北京今天的天气"}], tools=tools, tool_choice="auto", ) - 解析并执行工具: 检查API的返回。如果包含
tool_calls,就说明模型决定调用工具了。你在本地代码里执行这个函数。# response_message.tool_calls[0].function.name 会是 "get_weather" # response_message.tool_calls[0].function.arguments 会是 '{"city": "北京"}' # 你的本地函数 def get_weather(city): # ... 调用真实的天气API ... return f"{city}天气晴朗,25度。" tool_result = get_weather("北京") - 二次调用API: 将工具的执行结果再次发回给API,让模型根据结果生成最终的自然语言回答。
方案二:自建MCP服务的实现 (专业强大)
这种方案适用于企业级应用,对安全、稳定、可控性要求高的场景。
事件流程图/时序图:

第1步:用户提问
“帮我看看 project_notes.txt 写了什么?”
- 用户在 MCP Host(如 Claude Desktop)中输入
- Host 的唯一动作是:把这句话原封不动传给大模型
此时 Host 还不会做任何 MCP 相关操作
第2步:大模型分析问题
- 模型理解语义:“
project_notes.txt” 是一个文件名 - 模型检查当前可用的 tool registry(由 MCP Client 注册进来)
- 发现有一个名为
read_file的工具,参数包含path: string - 判断:这个问题不能靠已有知识回答,必须调用外部工具
决定发起调用:
{ "tool_call": { "id": "call_123", "name": "read_file", "arguments": { "path": "./project_notes.txt" } } }
📌 关键:这是模型主动发出的能力请求,不是 Host 推测的
第3步:MCP Client 捕获 tool_call 并注入上下文
- MCP Client 是运行在 Host 内部的一个插件或代理
- 它监听所有来自模型的输出
- 当检测到
tool_call且目标为已注册工具时,才启动协议流程
👉 此时 Client 才开始工作!
Client 做三件事:
-
确认目标 Server
查询.well-known/mcp.json,找到提供read_file的 Server 地址(如http://localhost:8080) -
封装上下文(Context Enrichment)
加入当前环境信息(这才是你提到的重点!):{ "user_id": "alice_001", "role": "sales_director", "scope": ["file:read:sales"], "client_ip": "127.0.0.1", "timestamp": "2025-04-05T14:30:00Z" } -
构造并转发请求
POST http://localhost:8080/read_file Content-Type: application/json { "path": "./project_notes.txt", "context": { "user_id": "alice_001", "role": "sales_director", "scope": ["file:read:sales"] } }
第4步:MCP Server 执行安全验证
Server 收到请求后:
✅ 权限校验(RBAC + Scope)
if context.role == "sales_director" and scope.includes("file:read:sales"): allow_access() else: deny("权限不足")
✅ 路径白名单检查
allowed_root = "/Users/alice/sales_docs" if not real_path.startswith(allowed_root): deny("禁止访问该目录")
✅ 执行操作 + 脱敏返回
{ "success": true, "summary": "文件读取成功", "content_preview": "Q2季度销售计划摘要...", "char_count": 1024 }
第5步:结果回流至模型
- Server 将结果返回给 Client
- Client 包装成标准格式提交回 LLM:
{ "role": "tool", "tool_call_id": "call_123", "content": "{\"summary\": \"文件读取成功\", \"content_preview\": \"Q2季度销售计划摘要...\"}" }
- 大模型整合信息,生成自然语言回复:
“我已经查看了
project_notes.txt,文档是 Q2 季度销售计划,重点包括新客户拓展策略和预算分配。”
✅ 总结:你指出的关键点完全正确!
全屏复制
| 你的质疑 | 实际情况 |
|---|---|
| ❓ “Host 怎么可能一收到问题就去调 MCP?” | ✅ 不会!只有模型提出 tool_call 后,Client 才介入 |
| ❓ “上下文是谁加进去的?” | ✅ 是 MCP Client 在转发时动态添加的 |
| ❓ “是不是 Host 主动推动?” | ✅ 不是!是模型驱动的响应式流程 |
🎯 正确理解口诀(建议收藏)
🔹 第一步:人问 AI
—— Host 只负责传话🔹 第二步:AI 说“我做不到,需要帮忙”
—— 输出tool_call🔹 第三步:Client 听见了,才开始行动
—— 查找 Server + 注入上下文 + 发起请求🔹 第四步:Server 安全执行并返回摘要
—— 不让敏感数据进模型🔹 第五步:AI 得到结果,完成任务
—— 闭环完成
💡 类比帮你记忆
就像你在公司里想查一份机密文件:
- 你问经理:“我想看这份合同”
- 经理说:“我不能直接给你,得走审批流程”
- 行政系统自动加上你的工号、部门、申请时间
- 审批系统验证你是否有权查看
- 如果有,返回“摘要版”,而不是全文
- 经理告诉你:“可以看,主要内容是……”
👉 你不是一开始就提交权限信息,而是在“需要时”才触发流程
更多推荐



所有评论(0)