MCP 和 Function Calling 有什么区别:一次讲清怎么选和怎么落地

编程狮(w3cschool.cn) 2026-10-20 07:04:00 浏览数 (31)
反馈

一句话区别:Function Calling 解决"模型怎么表达它想调用某个工具",MCP 解决"工具从哪来、怎么被发现、怎么跨应用复用"。两者不是替代关系,而是上下游——MCP 负责把工具标准化地暴露出来,客户端再把这些工具翻译成各家模型能理解的 Function Calling 格式送进去。

MCP 和 Function Calling到底差在哪?

所以真正的问题不是"选哪个",而是"你现在卡在哪一层"。工具只给自己写的一个脚本用,直接用 Function Calling 就够了;工具要在多个客户端之间复用,才需要引 MCP 这层协议。

本文先讲清两者的分工边界,再给一个能跑起来的最小落地示例,最后列出四个高频失败分支与验证办法。

先看结论

只接一个模型、工具列表固定不变 → 用 Function Calling,代码最少。一次工具调用要经过"声明工具、模型表达意图、执行、回传"四步,Function Calling 只规范了第二步。工具要跨多个客户端复用,或者除了函数还想给模型提供数据资源与提示词模板 → 用 MCP 协议。

一个容易忽略的事实:用了 MCP 之后,Function Calling 并没有消失,它仍在底层工作。MCP server 暴露工具,客户端负责把它们翻译成当前模型的工具调用 schema,模型返回的调用请求再回到 server 执行。这两层各管一段。

协议细节想对照原文看的,可以翻 MCP 协议文档 一节一节核。

一、先分清两层:谁负责什么

把一次工具调用拆开看,链路是这样的:

用户提问 → 客户端把工具列表塞进请求 → 模型输出调用意图 → 客户端执行工具 → 结果回传模型 → 模型作答

Function Calling 管的是中间那一步"模型输出调用意图"的格式:模型不再返回一段自然语言,而是返回一个带函数名和参数的结构化对象。

MCP 管的是两端:工具列表怎么被客户端发现(服务端声明),以及工具怎么被执行(服务端实现)。

维度 Function Calling MCP 协议
解决的问题 模型如何表达调用意图 工具如何被发现与复用
规范归属 各模型厂商自定 开放协议,跨厂商
是否跨客户端复用 否,换模型要改代码 是,一次实现多处接入
除函数外还能提供 通常没有 资源、提示词模板
引入成本 低 中,需跑一个服务端

理解这张表,选型就清楚了:Function Calling 是能力,MCP 是管道。

一次工具调用在客户端模型与服务端之间的完整链路

二、Function Calling:最小可用形态

先看不用 MCP 时,工具调用长什么样。

import json
from openai import OpenAI

TOOLS = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "查询指定城市的当前天气",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]

client = OpenAI()

def run(question: str) -> str:
    messages = [{"role": "user", "content": question}]
    resp = client.chat.completions.create(
        model="gpt-4o-mini", messages=messages, tools=TOOLS
    )
    msg = resp.choices[0].message
    if not msg.tool_calls:
        return msg.content
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = {"city": args["city"], "temp": 26, "unit": "C"}
        messages.append(msg)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result, ensure_ascii=False),
        })
    final = client.chat.completions.create(model="gpt-4o-mini", messages=messages)
    return final.choices[0].message.content

这段代码做的是:声明一个工具的 JSON Schema 传给模型;如果模型返回了调用请求,就解析参数、执行本地函数、把结果作为 tool 角色消息追加回对话;最后再请求一次拿到最终回答。

这段代码的局限也很明显:工具定义写死在业务代码里,换个模型要改 schema 细节,换个应用要重写一遍。这正是 MCP 要解决的接入方式问题。

三、MCP:把工具变成可复用的服务

MCP 把工具从业务代码里搬到一个独立进程,用统一协议对外声明。

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather")

@mcp.tool()
def get_weather(city: str) -> dict:
    """查询指定城市的当前天气"""
    return {"city": city, "temp": 26, "unit": "C"}

if __name__ == "__main__":
    mcp.run(transport="stdio")

这段做的是:创建一个 MCP 服务端,用装饰器把一个普通 Python 函数注册成工具,函数签名与文档字符串会自动转成工具描述,最后以 stdio 方式启动。客户端连接后就能拿到工具列表并调用它。

好处是同一个服务端可以被不同客户端接入——桌面应用、编辑器插件、你自己写的脚本都能连。工具只写一次。

代价也要说清:

  • 多一个进程要维护,调试链路变长;
  • 工具数量会挤占上下文,几十个工具的 schema 加起来很可观;
  • 协议本身不解决鉴权与审计,谁能在什么范围内调用,仍要自己在服务端实现。

按复用范围选择接入方式的决策路径

四、四个失败分支与验证清单

4.1 失败分支一:模型返回的参数过不了 schema 校验

模型有时会漏字段或类型不对。修复办法是在执行前用校验库过一遍,失败时把错误信息作为工具结果回传,让模型自己纠正——这比直接抛异常有效得多。

from pydantic import BaseModel, ValidationError

class WeatherArgs(BaseModel):
    city: str

def safe_call(raw: dict) -> str:
    try:
        args = WeatherArgs(**raw)
    except ValidationError as exc:
        return f"参数不合法,请修正后重试:{exc.errors()}"
    return json.dumps(get_weather(args.city), ensure_ascii=False)

这段做的是:用数据模型校验入参,校验失败时把具体错误作为字符串返回给模型,让它自行修正。预期结果(未在本机执行,依据 pydantic 的标准行为)是缺字段时返回可读错误而不是崩掉。

4.2 失败分支二:工具名冲突

多个 MCP 服务端提供同名工具时,客户端无法区分。修复办法是给工具名加前缀命名空间,并在接入前列出全部工具名检查一遍。

4.3 失败分支三:一次工具调用超时卡住整条链路

外部接口慢会让整条链路挂住。修复办法是在服务端实现里设超时并返回明确错误,让模型知道这次调用没成功,而不是无限等待。

4.4 验证清单

  • 检查:mcp.run() 启动后客户端能否列出工具
  • 检查:工具描述里是否写清了用途与参数含义
  • 运行:问一个必须调用工具才能回答的问题
  • 预期:模型返回调用请求,工具执行后给出最终答案
  • 失败:模型不调用工具 → 描述写得不够具体,补清用途与边界
  • 失败:执行报错 → 先单独运行函数本身,确认与协议无关
  • 修复:调整描述或补参数校验后重新连接
  • 验证:连续问三个不同问题,确认工具调用稳定触发

总结

MCP 和 Function Calling 是上下游而非二选一:Function Calling 让模型会表达调用意图,MCP 让工具能被发现、被复用。工具只在一个应用里用就直接 Function Calling,需要跨客户端复用再上 MCP,别为了用协议而用协议。

三条要点带走:

  • 用了 MCP 之后 Function Calling 仍在底层工作,两层各管一段;
  • 工具描述决定模型会不会调用,写清用途比写清参数更重要;
  • 参数校验失败时把错误回传给模型,比抛异常更能自我纠正。

下一步建议先用 Function Calling 跑通一个工具,把链路摸清之后再迁到 MCP 服务端。配置细节记不住时,翻 IDE 配置 MCP 教程 最快。

常见问题

Q:有了 MCP 还需要 Function Calling 吗?

A:需要。MCP 负责工具从哪来,Function Calling 负责模型怎么表达调用意图,客户端会把 MCP 的工具翻译成模型的调用格式。

Q:只有两三个工具,值得上 MCP 吗?

A:如果只在一个应用里用,直接用 Function Calling 更省事。工具需要在多个客户端之间复用时才体现出 MCP 的价值。

Q:MCP 协议能解决权限和安全问题吗?

A:协议本身不解决。服务端跑在你自己的环境里,谁能调用、能访问哪些数据,仍要在服务端自己实现鉴权与审计。

Q:工具越加越多会有什么后果?

A:工具 Schema 会占满上下文,模型选择工具的准确率也会下降。建议按场景分组接入,同一时刻只暴露必要的那几个。

延伸学习

想把工具调用这块补齐,可以按这个顺序来:

  1. 读一篇相关笔记 Function Calling 笔记,把模型调用外部工具的机制弄清;
  2. 想动手练,AI 开发实战课程 是边学边写的形式;
  3. 想看另一个客户端怎么接,可以看 Claude Code 接 MCP。

0 人点赞