ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

从 Function Calling 到 MCP:大模型的“工具调用”到底是怎么回事

从 Function Calling 到 MCP:大模型的“工具调用”到底是怎么回事

这篇讲什么:模型能写诗、能改代码,却不知道你的数据库里有什么。工具调用(Tool Calling)就是给它装上“手”的那一环。本文沿一条主线讲清两件事——Function Calling 到底是什么,MCP 又为什么非出现不可。

适合谁:刚接触 Agent 开发、被 Function Calling / Tool Calling / MCP 几个词绕晕的同学;想把大模型接进 Java 后端项目的开发者。

一、先看问题:模型被关在一个房间里

大模型最擅长的事情是语言:听懂你说了什么,然后组织出一段像样的回答。但它的世界到“文字”就为止了——它读不到你库里那三百万条订单,打不开你本地的配置文件,也调不动你写了半年的下单接口。

打个比方:它像一个被关在房间里、知识极其渊博的顾问。你隔着门缝问它问题,它能给你非常漂亮的建议,但它没法自己下楼去档案室取资料,也没法替你签字盖章。

要让它真正办事,就必须有人替它跑腿。这个“跑腿的人”,就是我们自己写的后端代码。而“模型怎么告诉代码该跑哪一趟”,正是工具调用要解决的问题。

围绕这件事,行业里有两个词经常被混着用:Function Calling 和 MCP。它们不在同一个层面上——一个属于模型能力,一个属于通信协议。下面逐个拆开。

二、Function Calling:让模型“开口要东西”

一句话:Function Calling 是大模型的一项能力——在需要外部数据时,它不再输出自然语言,而是输出一段结构化 JSON,告诉程序“我要调哪个工具、传什么参数”。

2.1 它到底是一段什么样的输出

平时我们让模型回答,它吐出来的是人话。但如果你在提问之前先告诉它“你手上有这些工具”,它就有可能换成另一种输出:一段结构化的 JSON,内容是“我要调 get_weather,城市传武汉”。

这段 JSON,就是我们常说的tool_call(工具调用指令)。

这里顺手说清两个会反复出现的词:JSON是一种用“键 + 值”描述数据的文本格式,机器读起来毫不费力;JSON Schema则是用 JSON 写的一份“参数说明书”,用来告诉模型这个工具需要哪些参数、各是什么类型、哪些必填。

2.2 关键认知:模型只“点单”,不下厨

这是初学阶段最容易搞错的一点,值得单独强调。

模型不执行任何代码。它只输出“我要调什么、参数是什么”这段意图。真正去查数据库、跑业务逻辑、把结果拿回来的,永远是你后端的代码。

用餐厅打比方:模型是坐在桌边的客人,它负责看菜单、点菜;你的代码是后厨,负责真刀真枪做出来。客人说得再清楚,菜也不会自己出现。

2.3 一次工具调用的完整五步

把链路摊开看,其实非常朴素:

  1. 先交底:提前把工具名单交给模型,每个工具带上名字、用途描述和参数 Schema。
  2. 提问:用户问“武汉今天多少度”,模型判断——这个我编不出来,得去查。
  3. 下单:模型输出 tool_call,指名道姓说调哪个工具、传什么参数。
  4. 办事:后端捕获这段结构,解析出工具名和参数,执行对应的业务代码。
  5. 回话:把执行结果放回对话上下文,模型据此组织成最终回答。

注意第 2、3、5 步是模型在做,第 1、4 步是你的代码在做。这个分工是后面所有概念的骨架——MCP 也是在这套骨架上做加法。

2.4 从 Function Calling 改名叫 Tool Calling

这项能力最早由 OpenAI 推出,当时叫 Function Calling,因为它确实就是调“函数”。后来能调的东西越来越多:搜索引擎、数据库、文件系统、HTTP 接口……“函数”这个词兜不住了,行业便慢慢改口叫Tool Calling。

名字换了,思想没变:让模型用一种结构化的方式,表达“我要用某个外部能力”。

三、原生 Function Calling 的痛点:每家厂商一套方言

一句话:Function Calling 只有“能力”,没有“标准”。tool_call 长什么样,各家模型自己定——字段名、嵌套层级、参数类型全都不一样。

3.1 两家最典型的写法

同样是“查武汉天气”,OpenAI 和 Claude 返回的结构是这样的:

{ "tool_calls": [ { "id": "call_123", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\":\"武汉\"}" } } ] }

特点是:顶层是tool_calls 数组,函数信息被包在function 对象里,而arguments 是一个JSON字符串——你得自己再反序列化一次才能真正用。

{ "content": [ { "type": "tool_use", "id": "tu-123", "name": "get_weather", "input": {"city": "武汉"} } ] }

Claude 这边压根没有 tool_calls 字段:调用块混在content 数组里,类型标记为tool_use,参数字段叫input,而且直接就是 JSON 对象。

3.2 三个要命的差异

差异点

OpenAI 风格

Claude 风格

对代码的影响

顶层容器

tool_calls 数组

content 数组里的 tool_use 块

去哪里找调用块,写法不同

参数载体

arguments 是 JSON 字符串

input 直接是 JSON 对象

一个要二次反序列化,一个拿来就用

类型标识

type: function

type: tool_use

判断分支的条件不同

3.3 代价:M × N 的适配地狱

如果你手写解析,那这套逻辑只认一家。项目要换模型、或者要同时接多家,就得写多套分支——M 个模型 × N 个工具,代码里长满 if-else,每加一家都要把回归测试重跑一遍。

有人会说:“国产模型不都兼容 OpenAI 吗?”这是厂商主动做的兼容,不是标准。一旦碰上不兼容的(比如 Claude),问题立刻现形。

这类差异通常由应用层自己消化——写一层归一化,或者交给框架。但那属于实现细节。真正更难的是另一个问题:工具本身散落在各处,每个应用都要重新接一遍。

四、MCP:给工具定一个通用插座

一句话:MCP(Model Context Protocol,模型上下文协议)是一份「方法名 + 参数结构」的约定,跑在 JSON-RPC 上。它管工具怎么暴露、怎么被发现、怎么被调用,不管模型怎么理解。

4.1 它到底是一份什么约定

全部动作只有三类。

用途

方法

干什么

握手

initialize

交换协议版本,各自声明支持哪些能力

发现

tools/list

返回工具清单:名字、描述、参数 Schema

调用

tools/call

传工具名和参数,返回执行结果

加上 resources 和 prompts 的同构版本,就是它的全部。协议本体薄到一个下午能读完——它不是框架,不含调度循环、不含重试、不含上下文管理。

4.2 它不是什么

把常见的误认一次排掉,对照着记更牢:

容易误认成

实际是

Function Calling 的新版本

模型侧完全没变,还是输出 tool_call

一个框架

一纸约定加各语言 SDK,没有运行时

一种传输协议

底下是管道或 HTTP,它只管消息长什么样

必须联网的服务

stdio 模式就是本机两个进程走管道

4.3 为什么需要它:N × M 变成 N + M

没有统一协议时,每个 AI 应用都要为每个工具单独写一套对接逻辑:N 个应用 × M 个工具,就是 N × M 份代码。

有了 MCP:工具方写一次 Server,应用方写一次 Client,中间用协议对接,复杂度降到 N + M。

这个套路程序员很熟——LSP 就是这么解决「N 个编辑器 × M 门语言」的:语言方提供一个服务,所有编辑器都能接。MCP 想做的是同一件事,只是把语言换成了工具。

4.4 三个角色

角色

是什么

职责

Host

AI 应用本身

对接模型、管权限:哪些工具可调、调到什么程度

MCP Client

Host 内部的通信层

和 Server 收发 JSON-RPC 消息

MCP Server

独立进程或远程服务

暴露能力,本地走 stdio,远程走 HTTP

4.5 能暴露什么:三类原语

MCP 的能力边界比「工具调用」更宽,它定义了三类核心原语:

原语

含义

典型场景

由谁发起

Tools

带副作用的操作

写文件、发请求、改数据

模型决定调用

Resources

只读的上下文资源

本地 Markdown、数据库表结构

Host 直接注入上下文

Prompts

可参数化的提示模板

预制好的分析或总结模板

Host 主动拉取

最实际的区别在中间那行:Resources 不需要模型申请。Host 觉得有用就直接读出来塞进上下文,模型看到的是「已经摆在桌上的材料」,而不是「还要去要的材料」。省 token、控延迟都靠这个。

4.6 跑一次:六步

只有第 4 步归协议管——把调用送出去、把结果拿回来。第 3 步是模型自己完成的,MCP 一步都没碰。

它只是把「工具住在哪里、由谁来执行」抽了出去,变成独立服务。收益也就很直接:工具改一次,所有 Host 立刻可用;新工具上线,拉一下 tools/list 就发现,不用改代码发版。

4.7 和 Function Calling 的分工

解决什么

属于

Function Calling

模型怎么表达「我要调工具」

模型能力

MCP

工具怎么暴露、怎么被发现、怎么被复用

通信协议

一句话记住:Function Calling 是模型的表达能力,MCP 是工具的接入方式。两者配合,不是替代。

五、两者关系:一张图收尾

两层的分工可以一句话概括:MCP决定工具从哪来,Function Calling 决定模型怎么选。缺了上层,工具进不来;缺了下层,模型不会自己开口。

落到实际选型,判断标准很简单:

你的情况

该做什么

只有一个应用,工具都是自己写的

手写 Function Calling 就够,不必上 MCP

工具要给多个应用、多个团队复用

做成 MCP Server,一次开发、多处接入

要同时接多家模型

自己写一层归一化,或用现成框架,与 MCP 无关

六、写在最后

工具调用是 Agent 开发最核心的基础能力,而整个生态正在从“各家私有、零散实现”慢慢走向标准化。这个进程里,两层分工已经相当清晰:

  • Function Calling管“模型怎么表达”——能力层,由模型厂商定义;
  • MCP管“工具怎么接入”——协议层,由生态共同维护。

至于“各家格式怎么消化”,MCP 管不到,模型厂商也不会替你管。它通常落在应用层——写一层归一化,或者交给框架。但那是实现细节,不改变上面这两层的分工。

把这两层看清之后,再去看任何一份工具接入文档、任何一个 Agent 项目,你都能很快定位它在解决哪一段问题——而不是继续把 Function Calling、Tool Calling、MCP 混成一团。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进