这篇讲什么:模型能写诗、能改代码,却不知道你的数据库里有什么。工具调用(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 一次工具调用的完整五步
把链路摊开看,其实非常朴素:
- 先交底:提前把工具名单交给模型,每个工具带上名字、用途描述和参数 Schema。
- 提问:用户问“武汉今天多少度”,模型判断——这个我编不出来,得去查。
- 下单:模型输出 tool_call,指名道姓说调哪个工具、传什么参数。
- 办事:后端捕获这段结构,解析出工具名和参数,执行对应的业务代码。
- 回话:把执行结果放回对话上下文,模型据此组织成最终回答。
注意第 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 混成一团。