一文读懂 Function Calling 与 MCP

引言

如果说 Function Calling 是让 LLM 长出了“双手”,那么 MCP (Model Context Protocol) 就是赋予了它一套通用的“神经系统”。

在过去,我们通过 Function Calling 教会了 LLM 如何去按计算器、查天气、读数据库。但很快,我们发现这双“手”极其笨拙:每拿起一件新工具(接入一个新的 API),我们都要像做外科手术一样修改核心代码来适配。此时的模型,依然是一个被孤立在数据孤岛中的“缸中之脑”,它虽然聪明,却与现实世界隔着厚厚的玻璃墙。

随着 MCP 的发布,进化的齿轮再次转动。它不再要求模型去适应千奇百怪的工具,而是让全世界的数据和工具学会了“自我介绍”。这一转变,标志着 AI 从单打独斗的“工具使用者”,正式进化为万物互联的“生态主宰者”。

1 Function Calling 的本质与原理

在探讨 MCP 之前,我们必须先理解最基础的原子能力——Function Calling。我们需要厘清一个核心误区:LLM 并不是自己去点击了按钮、调用了API或运行了代码,它所做的一切,依然是“预测下一个 token ”。

1.1 与LLM的“两次握手”

Function Calling 的本质,是自然语言到结构化指令(JSON)的翻译器。为了完成一次工具调用,系统实际上需要在幕后进行至少两次 LLM 调用和多次上下文拼接:

  1. 第一次调用 (Intent & Param Extraction):
    • 用户提问:“北京天气怎么样?”
    • 应用程序将这个问题,连同可用的工具定义(如 get_weather(location)),一起发给模型。
    • 模型决策: 模型并没有回答“天气不错”,而是生成了一个结构化的 JSON 对象(如 {"name": "get_weather", "args": {"location": "Beijing"}})。同时,模型会打上一个“停止生成”的标记,告诉应用程序:“我话没说完,但我需要你帮我跑个腿。”
  2. 中间层执行 (Execution):
    • JSON 的准确性保障: 这是一个关键挑战。虽然模型经过后训练能输出 JSON,但仍可能出错(比如漏了括号)。现代框架(如 LangChain)和模型提供商(如 OpenAI 的 Structured Outputs)会在这一步利用 JSON Schema 进行校验。如果格式错误,应用程序会报错甚至让模型重试;如果格式正确,应用程序会在本地运行真正的 Python/JS 代码(调用天气 API)。
  3. 第二次调用 (Synthesis & Response):
    • 应用程序拿到 API 返回的结果({"temp": "25C", "condition": "Sunny"})。
    • 关键动作——上下文组装: 应用程序将“用户的原始问题” + “模型的 JSON 指令” + “API 的执行结果”三者拼接在一起,作为新的上下文,再次发送给模型。
    • 模型最终回答: 这一次,模型看到了数据,于是它根据上下文生成最终回复:“北京今天天气晴朗,气温 25 度。”

1.2 原理解析

这里存在一个常见的认知混淆:Function Calling 到底是模型后训练(Post-training)的能力,还是提示词工程(Prompt Engineering)的结果?答案是:能力靠训练,知识靠提示。

  • LLM 训练后获得的能力(SFT 阶段):通过监督微调(SFT),模型学会了两个核心本能:
    1. 意图识别: 知道何时该停止闲聊,转而输出调用指令。这是通过 Toolformer 提出的自我监督学习方法,让模型在训练阶段通过“试错”筛选出高质量的工具调用样本进行微调而习得的。
    2. 格式遵循: 学会了输出符合严苛语法的 JSON,而不是乱写文本。Gorilla 论文通过检索增强训练,教会模型理解复杂的 API 文档,并通过 AST(抽象语法树)匹配来评估生成的代码结构是否正确,从而大幅减少幻觉。
  • 提示词解决的问题(Inference 阶段):模型虽然懂 JSON 结构,但它不知道你今天具体有哪些工具。开发者需要在 System Prompt 中将具体的 API 文档(如 get_weather 的参数定义)注入进去。
  • 应用程序逻辑解决的问题:解析 JSON、发起网络请求、处理 API 报错、将结果回填给模型。这些是传统的工程代码逻辑(Python/Java/JS),与 AI 无关,是程序的“躯干”。

2 工程困境

尽管 Function Calling 赋予了模型行动能力,但在实际工程落地中,却陷入了泥潭。

2.1 “胶水代码”的地狱

最大的痛点在于 M x N 连接难题。

假设你有 3 个模型(GPT-5.1, Gemini 3, Llama)需要连接 5 个工具(Linear, Slack, GitHub, Postgres, Google Drive)。你不仅需要为每个工具编写 API 封装器,还要为每个模型调整 Prompt 格式。这意味着你需要维护 3*5 = 15 套适配代码。每一套代码都在做着无聊且脆弱的格式转换和路由分发工作。

2.2 脆弱的生态系统

这种紧耦合导致系统极度脆弱。一旦 Slack 更新了 API 的某个字段名,或者你决定把文件存储从 Google Drive 迁移到 Dropbox,你的整个 Agent 代码就需要重构。

2.3 手写 ReAct 循环的“上下文黑洞”

除了代码维护的噩梦,更深层的问题在于运行逻辑的非标准化

Function Calling 只是提供了一个“单次调用”的能力。但在真实的复杂任务中(例如“帮我分析这个代码库并修复 Bug”),模型不能只有一次行动,它必须经历 ReAct (Reason + Act) 的完整循环:Thought(思考)-> Act(行动)-> Obs(观察结果)-> Thought(再思考)。

但在简单的 Function Calling 模式下,这个 “Obs(观察)” 环节是缺失标准的,导致了两个关于“语境(Context)”的致命问题:

  1. “感知”与“行动”的混淆:
    • 问题现状: 在没有 MCP 时,如果模型想“看”一个文件,它必须“调用”一个工具(比如 read_file())。这对模型来说,“看”(感知)变成了一种“做”(行动)
    • 后果: 这导致了上下文的混乱。模型执行了“读文件”操作,返回了 5000 行代码。这 5000 行代码被当作“工具执行结果”塞进了对话历史(Chat History)中。当对话继续,历史记录变得极度臃肿,模型很难区分哪些是“背景资料”(本应一直存在),哪些是“操作反馈”(临时性的)。
    • 缺乏统一语境: 这种混杂使得模型失去了对“当前环境”的清晰认知——它就像一个蒙着眼睛的人,只能通过不断伸手(调用工具)来摸索世界,而无法睁开眼睛直接看到世界(获取 Context)。
  2. 上下文管理的黑洞:开发者被迫在应用程序中编写复杂的逻辑来处理这些“读取工具”的返回值。应该全量保留吗?还是截断?如果是二进制图片怎么办?这种手工作坊式的上下文拼接,极其容易导致模型幻觉。

3 使用 MCP 重构“神经系统”

MCP 能解决上述问题吗?答案是肯定的。MCP 的核心思想之一就是将“感知”从“行动”中剥离出来。它引入了 Resources(资源)原语,专门负责“看”(构建上下文),而让 Tools(工具)专注于“做”(执行操作)。这意味着,数据不再是工具调用的“副作用”,而是模型思考的“前置背景”。

3.1 什么是 MCP?

MCP 是一种开放标准,它规定了 AI 如何发现数据、调用工具和读取资源。

这就好比 USB 接口。在 USB 之前,连接鼠标、键盘需要不同的接口。MCP 就是 AI 界的 USB——它不再关心你是 Postgres 数据库还是 Linear 工单系统,只要插上(Connect),系统就能自动识别设备的能力。

3.2 核心架构:Host、Client 与 Server

MCP 的架构将系统拆分为三个各司其职的角色。要理解 MCP 的运行机制,首先要搞懂这“三驾马车”是如何配合的。

1. Host

  • 身份: 它是直接与用户交互的应用程序,比如 Claude DesktopCursorVS Code 或者你自己开发的 AI Agent 应用。
  • 职责: 它是系统的“总指挥”。它负责加载 LLM(大脑),管理用户界面,以及决定何时连接哪些外部能力。
  • 关键特性: Host 是多对一的聚合器。一个 Host 可以同时连接成百上千个不同的 Server,将它们的能力汇聚在一起提供给 LLM。

2. Client

  • 身份: 它是运行在 Host 内部的一个模块(通常是 SDK 的一部分)。
  • 职责: 负责“打电话”。Host 不直接跟 Server 说话,而是通过 Client 与Server建立连接、发送请求、接收响应。
  • 关系: 1:1 连接。每一个 Client 实例只负责连接一个具体的 Server。如果你在 Host 里连接了 Git 和 Postgres,那么 Host 内部其实启动了两个独立的 Client 实例,分别去维护这两条线路。

3. Server

  • 身份: 它是独立运行的进程,由工具提供方编写(如 Linear 官方或开源社区)。它就像是一个“外设”。
  • 职责: 干脏活累活。它负责去连接底层的 SQL 数据库、调用真实的 GitHub API,然后把结果转换成 MCP 标准格式返回给 Client。
  • 独立性: 这是 MCP 最具革命性的设计。Server 是轻量级、沙盒化的。Server A 崩了,不影响 Server B,更不会导致 Host 崩溃。

它们如何协作?想象一个 “智能家居中控系统”

  • Host (中控屏): 用户在这里发号施令(“打开电影模式”)。
  • Server (智能设备): 比如“米家台灯”是一个 Server,“Bose 音响”是另一个 Server。它们各自独立,互不干扰。
  • Client (驱动程序): 中控系统内部加载了“台灯驱动”和“音响驱动”。
  • 流程: 用户对 Host 说话 -> Host 调度内部的 Client -> Client 通过标准协议指令(MCP)控制外部的 Server。

3.3 解构 MCP 的三大“原语” (Primitives)

理解了物理架构后,我们再来看 Server 到底向 Host 提供了什么。为了讲清楚 MCP 到底能做什么,我们以 Git MCP Server 作为案例。想象一下,你有一个 AI 编程助手(Host),它通过 MCP 连接到一个 Git 代码仓库(Server),MCP Server 向 AI 暴露了三种核心原语:Resources(资源)Tools(工具)Prompts(提示词)

1. Resources

  • 定义(被动感知 / State): 它是 Server 端持有的、可被直接读取的数据。它类似于 HTTP 的 GET 请求,代表只读的信息。
  • 解决了什么痛点?
    • 旧模式: 在没有 MCP 时,模型要看代码必须调用 read_file()。这不仅消耗一次对话轮次,更糟糕的是,返回的 5000 行代码会被作为“工具执行结果”追加到对话历史(History)末尾。每读一次,历史就膨胀一次,迅速撑爆上下文窗口。
    • MCP 模式: Resource 将数据定义为“状态”而非“事件”。Host 应用(如 IDE)可以在用户提问之前,就直接通过 Resource 协议把这些文件的内容加载并“钉”在 System Prompt 或独立的 Context Window 中。当文件发生变更时,Host 会原地更新这部分内容,而不是在历史记录里追加新的副本。
    • 本质区别: 它是环境。对模型来说,Resources 是它“睁开眼就能看到的东西”,是思考的前置语境
  • 例子: 代码文件内容、数据库 Schema、系统错误日志。

2. Tools

  • 定义(主动行动 / Action): 这就是标准化后的 Function Calling。它代表可执行、有副作用的操作。
  • 关系: 它是 AI 改变世界的方式。模型在“阅读”了 Resources(理解了现状)之后,使用 Tools 来改变现状。
  • 例子: git_commit(提交代码)、create_issue(创建问题)、run_test(运行测试)。

3. Prompts (提示词模板)

  • 定义(方法论 / Guidance): 这是 Server 端提供的最佳实践流程。它是一段预设好的 Prompt 结构。
  • 为什么需要它? 它是连接 Resources 和 Tools 的桥梁。如果说 Resources 是“材料”,Tools 是“工具”,那么 Prompts 就是“施工指南”。它告诉模型:“在处理这类任务时,你应该先关注哪些 Resource,并以什么样的步骤使用 Tools。”
  • 交互与应用:
    • 场景: Git Server 提供了一个名为 generate_pr 的 Prompt。
    • 流程: 当用户选择这个 Prompt 时,Server 会告诉 Host:“请自动抓取 git diff 这个 Resource 的内容放入上下文,并使用‘请生成符合 Conventional Commits 规范的描述’作为系统提示词。”这让普通用户也能一键获得专家的提问能力。

三者的关系:

  • Resources: 模型决策的依据。解决“我在哪?我看到了什么?”的问题。
  • Prompts: 告诉模型“该关注哪些资源”以及“该设定什么目标”。解决“我该怎么开始任务?”的问题。
  • Tools: 模型为了达成目标而执行的动作。解决“我该如何改变现状?”的问题。

一个完整的 MCP 工作流往往是这样的:

用户选择一个 Prompt(如“修复 Bug”) -> Host 根据 Prompt 指引自动加载相关的 Resources(如错误日志和代码)到上下文中 -> 模型阅读上下文,思考后调用 Tools(如修改代码) -> Tools 修改了文件,进而触发 Resources 的自动更新 -> 循环结束。

3.4 架构优势:解耦与自描述

这种 Host-Client-Server 的分层架构,配合三大原语,带来了两个巨大的工程优势:

  1. 自描述 (Self-describing): Server 启动后,Host 不需要硬编码它有什么功能。Client 只需要发一个 tools/list 请求,Server 就会回复:“我有 git_commit 工具,我有 git:// 资源。” 这种动态发现能力是 MCP 的核心。
  2. 通信透明 (Transport Agnostic): Client 和 Server 之间的通信可以通过 Stdio(本地进程管道,像传纸条一样快且私密)进行,也可以通过 SSE(Server-Sent Events,像打电话一样保持长连接)进行远程通信。Host 并不关心 Server 在本地还是云端,它只管调用标准接口。

4 工程落地全链路深度解析

为了彻底讲清楚边界和流程,我们举一个具体的例子。

4.1 案例场景

用户请求: “请帮我分析一下‘英伟达’最近一周的股价趋势,并把分析报告保存到我的本地笔记中。”

环境配置:

  • Host: Claude Desktop(集成了 MCP Client 代码的宿主应用)。
  • Server A: finance-server(独立进程,提供股价查询)。
  • Server B: filesystem-server(独立进程,提供文件写入)。

4.2 全流程时序拆解

阶段一:启动与握手 (Host Startup & Handshake)

这个阶段发生在用户提问之前。

  1. Host 启动: Claude Desktop 启动,读取配置文件,发现需要加载 Server A 和 Server B。
  2. 建立连接: Host 通过 Stdio 启动这两个 Server 的子进程。
  3. 动态发现/自描述: Host 向两个 Server 发送 tools/list 请求。
    • Server A 回复:JSON Schema { name: "get_stock", args: ... }
    • Server B 回复:JSON Schema { name: "write_file", args: ... }
    • 注: 此时 LLM 尚未介入,这是纯代码层面的协议交互。Prompts 原语(如果 Server 有提供)也会在此时被列出,供用户在 UI 上选择,但在本例中未涉及。

阶段二:用户交互与上下文注入 (Interaction & Injection)

  1. 用户提问: 用户输入请求。
  2. 构建上下文: Host 开始组装发给 LLM 的消息:
    • System Prompt: “你是一个助手… 你可以使用以下工具:[Server A 和 B 返回的 JSON Schema]…”
    • User Message: “请帮我分析英伟达…”
    • 注: 这就是 LLM “知道”有哪些工具可用的时刻。

阶段三:第一轮推理与路由 (Reasoning & Routing – Round 1)

  1. LLM 思考: LLM 处理上下文。它通过 SFT 训练获得的能力判断出需要先查数据。
  2. LLM 输出: LLM 生成文本(Thought)和指令(Function Call JSON):Call get_stock(symbol="NVDA")
  3. Client 拦截: Host 的代码检测到停止符和 JSON。它查找内部注册表,发现 get_stock 属于 Server A。
  4. 协议传输: Host 将 JSON 封装成 MCP 协议格式,通过管道发给 Server A。

阶段四:执行与反馈 (Execution & Observation)

  1. Server A 执行: Server A 收到请求,调用 Yahoo Finance API(这是 Server A 内部的逻辑)。
  2. 结果回传: Server A 拿到数据,返回 JSON 结果给 Host。
  3. 更新历史: Host 将执行结果封装为 ToolMessage,追加到对话历史中。此时上下文包含:用户问题 + LLM 的调用指令 + 工具的返回结果。

阶段五:第二轮推理与路由 (Reasoning & Routing – Round 2)

  1. 再次调用 LLM: Host 将更新后的上下文再次发给 LLM。
  2. LLM 思考与生成: LLM 看到数据了,开始分析并在内存中生成报告文本。
  3. LLM 决策: LLM 再次输出指令:Call write_file(path="./report.md", content="...")
  4. 安全拦截: Host 识别出 write_file 是敏感操作(基于 Client 的安全策略)。Host 弹窗:“Server B 请求写入文件,是否允许?”
  5. 用户确认 & 执行: 用户点击允许。Server B 执行写入,返回 “Success”。

阶段六:最终响应

  1. 第三次调用 LLM: Host 将 “Success” 结果再次喂给 LLM。
  2. 最终回复: LLM 输出:“分析报告已生成并保存。”

5 展望:从“应用”到“生态”

MCP 的出现,不仅仅是一个技术标准的升级,它预示着 AI 应用生态的重构。

5.1 提示词工程的进化

MCP 引入了 Prompts 原语。这意味着工具开发者(最懂数据的人)可以将“如何最好地查询这个数据库”的经验,写成 Prompt 模板固化在 Server 中。用户不再需要学习复杂的提示词技巧,只需调用 Server 提供的模板。这是 Prompt Engineering 从“用户侧”向“供给侧”的转移。

5.2 连接的价值

既然 MCP Server 是标准化的,给 AI 增加能力就像给浏览器安装插件一样简单。点击“安装 Linear 能力包”,你的 AI 助手瞬间就能处理 Linear 的工单,而无需你编写一行代码,即插即用,Agent 可以使用海量的工具完成各种复杂度和差异化的任务。

AI 的强大不仅仅在于它的参数量(智商),更在于它能连接多少数据与工具(生态),MCP 则抹平了连接的成本。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部