news 2026/10/3 13:52:12

模型三剑客:工具、函数调用与MCP协同之道

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
模型三剑客:工具、函数调用与MCP协同之道

这三个概念不在同一个层级,是配合起来用的:‌Function Calling 是模型“决定调用哪个工具”的能力,Tool 是具体可执行的功能,MCP 是统一连接工具和模型的标准协议‌。‌‌

可以把模型想象成一个聪明的助理:

  • ‌Function Calling‌:助理的“决策和表达”,听懂需求后,判断该用哪个工具、需要什么参数,并生成结构化指令(比如{"name": "getWeather", "arguments": {"city": "成都"}})。它本身不执行,只负责“说清楚要干什么”。
  • ‌Tool(工具)‌:助理实际能用的“家伙什”,比如查天气的接口、发邮件的功能、计算器。开发者把工具定义好,告诉模型“你有这些可以用”。
  • ‌MCP‌:连接助理和工具的“标准插座”(像 USB-C)。以前每家模型对接工具的方式都不一样,MCP 统一了接口标准,让同一个工具能被不同模型直接使用,不用反复适配。‌‌

它们怎么配合工作

  1. ‌注册工具‌:开发者定义好工具(Tool),比如getWeather,用 JSON 描述它的功能和参数。
  2. ‌模型决策‌:你问“成都明天天气”,模型通过 Function Calling 判断需要调用天气工具,并生成指令:{"name": "getWeather", "arguments": {"city": "成都", "date": "明天"}}。
  3. ‌执行与返回‌:你的程序(或 MCP Client)收到指令,真正去执行工具,拿到天气数据后再交给模型,由模型整理成自然语言回复你。‌‌

关键点:模型不直接执行代码,它只负责“发号施令”,真正的执行由后端完成。‌‌

Function Calling 和 MCP 的核心区别

对比维度Function CallingMCP
本质模型的输出能力连接工具与应用的协议
作用层模型层传输/协议层
解决的问题模型怎么表达调用意图应用怎么发现和连接工具
工具发现开发者写死工具列表运行时动态发现
跨平台各家格式不兼容统一标准,一次接入到处可用

简单说:‌Function Calling 是“大脑”的决策,MCP 是让决策能落地的“高速公路”‌,两者互补,不是替代关系。‌‌

什么时候用哪个

  • ‌只服务单一模型:直接用 Function Calling 就够了,更简单直接。
  • 小项目、仅需3-5个固定工具:直接用Function Calling对接Tool即可,代码量小、链路简单,完全不需要引入MCP
  • ‌要支持多个模型、工具会动态增减‌,或涉及数据库、支付等敏感操作:建议上 MCP,协议层有更好的标准化和安全机制。
  • 实际项目里,两者通常‌结合使用‌:模型用 Function Calling 表达意图,MCP 负责把意图路由到正确的工具并执行。‌‌
  • 企业级多Agent系统、需要对接十几个内部业务系统:用MCP统一封装所有Tool,再通过Function Calling触发调用,能减少60%以上的重复开发代码,工具支持热插拔,迭代效率大幅提升。

各自的核心定位

  • ‌Tool(工具)‌:是最底层的“执行单元”,就是一个个能完成具体任务的功能,比如查天气的接口、发邮件的函数、查询数据库的能力,本身是一段可执行的代码或API,不直接和模型交互。
  • ‌Function Calling‌:是大模型的‌原生输出能力‌,它的核心作用是让模型在生成文本时,能输出结构化的JSON调用指令,明确告诉应用“我要调用哪个工具、传什么参数”,解决的是“单次调用怎么准确表达意图”的问题。
  • ‌MCP(模型上下文协议)‌:是Anthropic推出的‌标准化中间层协议‌,相当于工具调用领域的“USB-C通用接口”,它不替代任何一方,而是把所有外部工具统一封装成可被自动发现、连接的标准服务,解决的是“工具怎么跨应用复用、统一治理”的问题。

三者的完整协作链路

在实际生产系统中,三者会按固定流程配合工作,完全不存在谁替代谁的情况:

  1. ‌工具标准化封装‌:开发者把所有Tool(天气查询、数据库操作、文件读取等)封装成独立的MCP Server,通过统一的JSON-RPC协议暴露工具清单、参数规则。
  2. ‌工具自动发现‌:AI应用作为MCP Client,连接各个MCP Server,自动拉取所有可用工具的完整描述,不需要开发者手动为每个工具写重复的Schema定义。
  3. ‌模型生成调用指令‌:用户发起请求后,大模型通过Function Calling能力,判断需要调用的工具,输出标准的结构化调用JSON,明确工具名和入参。
  4. ‌协议层路由执行‌:应用侧把模型输出的Function Calling指令,转发给对应的MCP Server,由MCP Server完成参数校验、实际Tool执行,再把结果格式化返回给模型。
  5. ‌结果整合输出‌:大模型拿到工具返回的结果,整理成自然语言回复给用户,MCP还会自动维护多轮调用的上下文状态,不需要开发者手动传递参数。

核心差异对比

维度ToolFunction CallingMCP
所处层级最底层执行层模型侧交互层中间协议层
核心目标完成具体业务动作让模型准确表达调用意图统一工具接入标准,实现跨应用复用
核心能力执行具体逻辑输出结构化调用JSON工具自动发现、状态自动维护、跨模型兼容
复用范围仅在当前代码库可用绑定特定大模型的输出格式支持所有兼容MCP的AI应用/模型直接复用
适用场景所有需要外部操作的场景工具少、单应用的轻量场景多Agent协作、大量工具需要统一治理的复杂场景

简单总结:‌Tool是“干活的手”,Function Calling是“大脑发出的动作指令”,MCP是连接大脑和手的标准化“神经通路”‌,三者配合才能让大模型真正安全、高效地和真实业务系统交互。

生活场景:智能家居管家

  • ‌Tool‌:家里的空调、灯、扫地机器人——具体能执行动作的设备。
  • ‌Function Calling‌:管家(模型)的能力,你说"有点热",他能判断出该开除湿还是制冷、定几度,输出明确指令。
  • ‌MCP‌:家里的‌智能中控网关‌。以前小米、海尔、格力的设备协议各不相同,管家得分别学着控制;有了统一网关,管家只要对着网关说"把客厅降温",网关自动翻译成各品牌设备听得懂的信号。

于是流程变成:‌你说"到家了,有点热" → 管家(模型)判断要开空调+开灯 → 通过网关(MCP)分别下发指令 → 空调、灯(Tool)各自执行 → 管家告诉你"已开空调 26 度和客厅灯(模型整合结果,回复你)"

如果哪天你新买了一台新品牌的风扇,只要它接入了这个网关,管家‌自动就能控制它‌,你什么都不用改——这就是 MCP 带来的"热插拔"

代码示例

一、假设有一个订单服务正常运行

二、搭建一个MCP Server 去调用订单服务查询订单详情

1、引入依赖

<!--引入依赖 spring-ai-mcp-server-webflux-spring-boot-starter--> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp-server-webflux-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>

2、配置文件

#配置服务端口 server: port: 8084 spring: application: name: order-mcp-server #配置mcp的服务 ai: mcp: server: name: my-order-server version: 1.0.0
3、实现:往Spring 注册Bean(ToolCallbackProvider)
@Data public class Order implements Serializable { private String orderId; private String userId; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date orderTime; private BigDecimal totalAmount; /** * 订单状态 * 0:待付款 * 1:已付款 * 2:已发货 * 3:已完成 * 4:已取消 */ private Integer orderStatus; private String paymentMethod; private String shippingAddress; private String contactPhone; private List<OrderDetail> orderDetails; }
@Data public class OrderDetail { private Integer detailId; private String orderId; private String productId; private String productName; private Integer quantity; private BigDecimal unitPrice; private BigDecimal totalPrice; private String specification; }
public enum OrderStatus { /** * 待付款 (0) */ PENDING(0, "待付款"), /** * 已付款 (1) */ PAID(1, "已付款"), /** * 已发货 (2) */ SHIPPED(2, "已发货"), /** * 已完成 (3) */ COMPLETED(3, "已完成"), /** * 已取消 (4) */ CANCELLED(4, "已取消"); private final int code; private final String description; /** * 构造函数 * @param code 状态码 * @param description 状态描述 */ OrderStatus(int code, String description) { this.code = code; this.description = description; } /** * 获取状态码 * @return 状态码 */ public int getCode() { return code; } /** * 获取状态描述 * @return 状态描述 */ public String getDescription() { return description; } /** * 根据状态码获取枚举实例 * @param code 状态码 * @return 对应的枚举实例 * @throws IllegalArgumentException 如果找不到对应的枚举值 */ public static OrderStatus fromCode(int code) { for (OrderStatus status : values()) { if (status.code == code) { return status; } } throw new IllegalArgumentException("无效的订单状态码: " + code); } /** * 检查当前状态是否允许取消订单 * @return 是否允许取消 */ public boolean canBeCancelled() { return this == PENDING || this == PAID; } /** * 检查当前状态是否已完成(包括已完成和已取消) * @return 是否已完成 */ public boolean isFinalStatus() { return this == COMPLETED || this == CANCELLED; } }
@Service public class OpenOrderService { //订单服务访问地址 private static final String BASE_URL = "http://localhost:8083/orders"; private final RestTemplate restTemplate; public OpenOrderService() { this.restTemplate = new RestTemplate(); } @Tool(description = "获取所有订单信息" ) public List<Order> getOrders() { // 尝试远程调用 String url = BASE_URL+"/list"; return restTemplate.getForObject(url, List.class); } @Tool(description = "根据用户ID获取用户订单列表信息" ) public List<Order> getOrdersByUserId(String userId) { // 尝试远程调用 String url = BASE_URL+"/user/" + userId; return restTemplate.getForObject(url, List.class); } @Tool(description = "根据订单ID获取订单详情" ) public Order getOrderById(String orderId) { String url = BASE_URL+"/{orderId}"; return restTemplate.getForObject(url, Order.class, orderId); } @Tool(description = "根据订单ID取消订单" ) public boolean cancelOrder(String orderId) { String url = BASE_URL+"/cancel/{orderId}"; return restTemplate.postForObject(url, null, Boolean.class, orderId); } }
@SpringBootApplication public class OMSApplication { public static void main(String[] args) { SpringApplication.run(OMSApplication.class,args); } //通过ToolCallbackProvider 提供远程服务调用(mcp client) @Bean public ToolCallbackProvider orderTools(OpenOrderService openOrderService) { return MethodToolCallbackProvider.builder().toolObjects(openOrderService).build(); } }

三、搭建一个MCP Client 去获取订单服务详情数据

1、引入依赖

<dependencies> <!--引入依赖 spring-ai-mcp-client-webflux-spring-boot-starter--> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp-client-webflux-spring-boot-starter</artifactId> </dependency> <!--引入依赖 sspring-ai-alibaba-starter--> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter</artifactId> </dependency> </dependencies>

2、配置mcp的服务

spring: application: name: order-mcp-client main: web-application-type: none ai: ollama: base-url: http://localhost:11434 #本地ollama服务地址 chat: model: deepseek-r1:1.5b #本地大模型 #ai: #dashscope: #api-key: ${AI_DASHSCOPE_API_KEY} mcp: client: sse: connections: server1: url: http://localhost:8084 #连接服务 mandatory-file-encoding: UTF-8 # 调试日志 logging: level: io: modelcontextprotocol: client: DEBUG spec: DEBUG ai: user: input: 查询订单ID为ORD20250414001的订单详情

3、实现:通过ToolCallbackProvider 调用远程的服务

@SpringBootApplication(exclude = { org.springframework.ai.autoconfigure.mcp.client.SseHttpClientTransportAutoConfiguration.class }) public class OMCApplication { public static void main(String[] args) { SpringApplication.run(OMCApplication.class,args); } private String userInput = "查询订单ID为ORD20250414001的订单详情"; /** * @param chatClientBuilder * @param tools 通过ToolCallbackProvider 调用远程的服务 * @param context * @return */ @Bean public CommandLineRunner predefinedQuestions(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools, ConfigurableApplicationContext context) { return args -> { var chatClient = chatClientBuilder //通过tool方式接入 .defaultTools(tools) .build(); System.out.println("\n>>> QUESTION: " + userInput); System.out.println("\n>>> ASSISTANT: " + chatClient.prompt(userInput).call().content()); context.close(); }; } }

启动服务(数据库服务,本地大模型服务,应用服务)

启动应用服务顺序:1、订单服务 2、MCP Server 3、 MCP Client


MCP Client 端执行结果

执行查询命令

>>> QUESTION: 查询订单ID为ORD20250414001的订单详情

返回查询结果(大模型整合返回结果)

>>> ASSISTANT: 订单ID为 ORD20250414001 的订单详情如下:

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 13:48:20

聊天系统中五大ID的演进与实战解析

一、为什么聊天系统会出现越来越多的 ID刚开始开发聊天功能时&#xff0c;一个 Session ID 似乎已经足够。它可以区分不同聊天窗口&#xff0c;可以查询历史消息&#xff0c;也可以让前端刷新页面后重新定位到原来的会话。随着 Memory、并发请求、Tool Calling 和停止生成逐渐加…

作者头像 李华
网站建设 2026/10/3 13:44:19

猫抓浏览器资源嗅探教程:网页视频存到本地,只需点几下

猫抓浏览器资源嗅探教程&#xff1a;网页视频存到本地&#xff0c;只需点几下 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 上个月做汇报&#x…

作者头像 李华