MCP与Function Calling怎么选:大模型工具调用三种方式的面试级对比

三种工具调用方式根本不在一个层级:Function Calling(函数调用)是模型输出结构化参数的能力,内置工具是平台送的现成货,MCP(Model Context Protocol,模型上下文协议)是把外部程序接进来的通信协议。选错的典型代价是延迟变高、上下文被撑爆,而不是「谁更先进」。


目录

    1. 三种方式站在不同的楼层
    1. 内置工具:平台给的现成货
    1. 本地 tools(工具):自己厨房现做
    1. MCP(模型上下文协议):给外部程序装的标准插座
    1. 选错方式的五类症状
    1. 一段能直接跑的本地 tool(工具)分发代码
    1. 面试高频追问三连
    1. 小结:先分层,再选型

1. 三种方式站在不同的楼层

面试官问「为什么不直接用本地 tools(工具),非要上 MCP(模型上下文协议)」,陷阱在于:这道题默认三者是竞品。它们不是竞品,是三层不同的东西。

  • Function Calling(函数调用) :模型层能力。模型不执行任何代码,它只是吐出一段符合约定格式的 JSON(JavaScript 对象表示法),告诉你「我想调 get_weather,参数是 {"city": "杭州"}」,真正的执行发生在你的应用里。
  • 内置工具:平台层能力。搜索、代码解释器这类能力由平台或模型厂商自己实现,你只能开关,改不了实现。
  • MCP(模型上下文协议):协议层能力。它规定的是「一个工具服务端怎么把能力描述给客户端、怎么接收调用、怎么回传结果」,跟模型本身没有绑定关系。

一句话记住:Function Calling(函数调用)管「模型怎么开口」,MCP(模型上下文协议)管「工具怎么接进来」。一个是嘴,一个是插座,不冲突,还经常一起用。

🤔 顺带吐槽:把这三个当成「本地版 vs 云端版」来背,就像把「说中文」和「用微信」当成两种语言,说出来面试官只能礼貌微笑。


2. 内置工具:平台给的现成货

内置工具是成本最低的一档。你不需要写代码,也不需要部署,勾一下就多一个能力。

它的边界也很清楚:

  • 能改的少:参数、返回格式、超时策略基本由平台定,你只能整体接受或整体关掉。
  • 黑盒:内部怎么检索、怎么截断、怎么计费,你看不到。
  • 适合谁:验证想法、做 demo(演示)、跑通链路。原型阶段用它最省事。

但一旦业务需要「把公司内部知识库接进来」或者「调用我们自己的订单系统」,内置工具就无能为力了------它不可能认识你的私有系统。


3. 本地 tools(工具):自己厨房现做

本地 tools(工具)就是写在你自己服务进程里的函数,通过 Function Calling(函数调用)暴露给模型。模型输出 tool_call(工具调用)意图,你的代码解析后直接调本地函数。

它的优势非常实在:

  • 快:函数调用就是同进程内的一次跳转,没有网络往返,延迟通常在毫秒级。
  • 好调试:断点一打,栈一跟,问题在哪一目了然。
  • 依赖少:拿个当前时间、做个四则运算、查一下本地缓存,这类工具写成 MCP(模型上下文协议)server(服务端)纯属给自己加戏。

它的天花板也很明确:

  • 跟着应用走:换一个客户端,工具定义就得重写一遍。
  • 同生共死:工具和应用在一个进程里,工具崩了应用一起遭殃。
  • 权限混在一起:所有工具共享应用的凭证,出了问题很难界定是谁越权。

所以本地 tools(工具)适合 简单、高频、依赖少、不需要跨应用复用 的场景。这不是「低配版」,而是正确的工具。


4. MCP(模型上下文协议):给外部程序装的标准插座

MCP(模型上下文协议)解决的是另一类问题:工具根本不在你的进程里,它可能是一个独立部署的服务、一个第三方程序、一个别人的系统。

它把这件事标准化了:外部程序按协议描述自己的能力,客户端按协议发现和调用,中间不关心对面用什么语言写的。

什么时候值得上 MCP(模型上下文协议),判断条件其实只有三条:

  1. 依赖重、环境复杂:工具要装一堆运行时依赖,塞进主应用只会让依赖打架。
  2. 要独立部署、独立伸缩:工具自己想扩容、想灰度、想单独重启,不该拖着主应用一起。
  3. 要隔离权限和故障:外部工具崩了不能把主链路带崩,凭证也不该共享。

反过来说,MCP(模型上下文协议)不是免费午餐:多一层协议就多一次序列化、多一跳网络、多一份要维护的部署和鉴权。把高频小工具全搬上去,延迟和故障点都会跟着涨。

🍜 打个比方:本地 tools(工具)像自家厨房,想吃什么现做,快但只能喂自己;MCP(模型上下文协议)像外卖平台,谁都能点、商家可以独立经营,但你要等骑手,还要接受商家偶尔把汤洒了。


5. 选错方式的五类症状

症状(Symptom) 原因(Cause) 解法(Fix)
一个查时间的工具也要走网络,延迟从毫秒涨到几十毫秒 把协议层用在了内部高频短路径上 简单、高频、依赖少的工具留在本地 Function Calling(函数调用)
工具一多,prompt(提示词)被工具描述撑爆,token(词元)成本飙升 MCP(模型上下文协议)会把成批的 tool schema(工具模式)塞进上下文 只暴露当前任务需要的工具,其余按需加载
某个外部服务挂了,整个 agent(智能体)跟着不可用 工具与应用同进程,没有故障边界 把不稳定依赖拆成独立 MCP(模型上下文协议)server(服务端)
同一套工具要接三个客户端,改一次要改三份代码 每个应用各写一遍 Function Calling(函数调用) 抽成 MCP(模型上下文协议)server(服务端)统一复用
出事后说不清是哪个工具越权 所有工具共享应用凭证 MCP(模型上下文协议)server(服务端)单独持权、单独审计

这张表的用法是倒着看:先看自己有没有出现左边某一栏的症状,有症状才改架构,没症状就别动。为了「技术先进」而提前引入协议层,是典型的过度设计。


6. 一段能直接跑的本地 tool(工具)分发代码

不管走哪条路,本地这一侧的骨架都一样:注册工具、解析模型的调用意图、分发执行。下面这段只用标准库,复制就能跑。

python 复制代码
import json                                                    # 解析模型吐出的 JSON 调用意图
from datetime import datetime, timedelta, timezone              # 拼当前时间,演示无依赖小工具
from typing import Any, Callable                                # 类型标注,说明注册表存的是可调用对象

REGISTRY: dict[str, Callable[..., str]] = {}                    # 工具注册表:工具名 → 函数

def tool(name: str):                                            # 装饰器工厂,用于注册工具
    def register(fn: Callable[..., str]) -> Callable[..., str]:  # 真正执行注册的内层函数
        REGISTRY[name] = fn                                     # 把函数按名字存进注册表
        return fn                                               # 原样返回函数,便于链式使用
    return register                                             # 返回装饰器

@tool("get_time")                                               # 注册「查当前时间」工具
def get_time() -> str:                                          # 无参数,返回格式化时间字符串
    return datetime.now(timezone(timedelta(hours=8))).strftime("%Y-%m-%d %H:%M")  # 东八区当前时间

@tool("add")                                                    # 注册「两数相加」工具
def add(a: float, b: float) -> str:                             # 接收两个数字参数
    return str(a + b)                                           # 计算结果转字符串回传

def dispatch(tool_call: str) -> str:                            # 分发器:处理模型给的调用意图
    call: dict[str, Any] = json.loads(tool_call)                # 反序列化成字典
    fn = REGISTRY.get(call["name"])                             # 按名字查注册表
    if fn is None:                                              # 工具不存在时兜底
        return f"unknown tool: {call['name']}"                  # 返回明确错误,不静默失败
    return fn(**call["arguments"])                              # 用命名参数调用目标工具

print(dispatch('{"name": "add", "arguments": {"a": 2, "b": 3}}'))   # 输出 5
print(dispatch('{"name": "get_time", "arguments": {}}'))            # 输出当前时间

真实场景里,传给 dispatch 的那段 JSON 不是人手写的,而是模型返回的 tool_call(工具调用)。MCP(模型上下文协议)这一侧替换的只是「谁来发现工具、谁来传输调用」,dispatch 这种执行逻辑照样存在。


7. 面试高频追问三连

追问一:MCP(模型上下文协议)能替代 Function Calling(函数调用)吗?

不能。模型仍然要靠 Function Calling(函数调用)来决定调哪个工具、传什么参数,MCP(模型上下文协议)只负责把工具服务和客户端连起来。两者是上下游关系,不是替代关系。

追问二:为什么有的团队上了 MCP(模型上下文协议)又迁回 API(应用程序接口)直调?

因为上下文开销和维护成本被低估了。工具描述、返回结构、调用轨迹都会进上下文,工具一多就是持续的 token(词元)消耗;再加上每个 server(服务端)都要部署、鉴权、监控、处理故障,团队规模撑不住时,直调 API(应用程序接口)反而更划算。

追问三:怎么判断该不该上 MCP(模型上下文协议)?

看三个条件:工具是否需要独立部署、是否要被多个客户端复用、是否需要权限与故障隔离。三个都是「否」,就老实写本地 tools(工具);有一个是「是」,再考虑抽 MCP(模型上下文协议)server(服务端)。


8. 小结:先分层,再选型

回到那道面试题。「为什么不直接用本地 tools(工具)」这个问法本身就有问题------本地 tools(工具)不是落后者,它是最短路径。真正要回答的是:这个工具属于哪一层。

  • 简单、高频、进程内能解决 → 本地 Function Calling(函数调用)。
  • 想快速验证、不挑实现 → 内置工具。
  • 跨应用复用、独立部署、权限与故障隔离 → MCP(模型上下文协议)。

先把层级分清,选型就变成填表题;分不清层级,才会在「本地还是外部」这种伪命题里绕圈。面试官真正想听的,不是名词解释,而是你能说清楚 每个选择背后的代价。


参考资料:

最后更新时间:2026-10-24

相关推荐
kyle~1 小时前
MCP(Model Context Protocol)
人工智能·ai·ai编程·mcp
BreezeJiang1 小时前
闭包和 useState 是两个知识点?你可能只学了半条作用域链
前端·面试
祥Sir的SRE笔记1 小时前
CPU 100% 不一定是故障:压测打满、业务饱和、突变打满的判断与排查
linux·面试·性能优化·dba·sre·故障定位·性能排查
moshuying2 小时前
DeppInfer系列之 - 矩阵、注意力与 GPU:拆解 DeepGEMM 与 FlashMLA
前端·javascript·面试
Rocky Ding*2 小时前
【三年面试五年模拟】2026-10-20_淘天AI应用研发一面面经全解析
论文阅读·人工智能·深度学习·机器学习·面试·aigc·ai-native
利威尔xu2 小时前
Go IM 源码实战(2):WebSocket 握手 11 步,少 1 个参数连不上
后端·websocket·微服务·面试·golang
古法搬砖工2 小时前
MCP:给 AI Agent 装一个“万能 USB 接口”
人工智能·skill·mcp·搬砖工
跨境Jacky3 小时前
亚马逊选品数据怎么批量拉? 我用 CLI 写了套采集脚本
跨境电商·mcp·sorftime
polarissmartai3 小时前
麦当劳官方 MCP 实战:让 AI 一句话点餐,价格走当次真实核价
mcp