模型三剑客:工具、函数调用与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 Calling MCP
本质 模型的输出能力 连接工具与应用的协议
作用层 模型层 传输/协议层
解决的问题 模型怎么表达调用意图 应用怎么发现和连接工具
工具发现 开发者写死工具列表 运行时动态发现
跨平台 各家格式不兼容 统一标准,一次接入到处可用

简单说:‌**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还会自动维护多轮调用的上下文状态,不需要开发者手动传递参数。

核心差异对比

维度 Tool Function Calling MCP
所处层级 最底层执行层 模型侧交互层 中间协议层
核心目标 完成具体业务动作 让模型准确表达调用意图 统一工具接入标准,实现跨应用复用
核心能力 执行具体逻辑 输出结构化调用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 的订单详情如下:

相关推荐
天国梦1 小时前
英语听说教学三大痛点与AI解决方案:从课堂困境到落地成效
人工智能
西索斯coding1 小时前
grok-4.7 调用一直 429 怎么办?不是额度用完——是 RPM 和 TPM 双桶限流,附响应头读取代码和退避策略
大数据·人工智能·机器学习·ai
ccstuck1 小时前
AI安全系列:给最小 Agent 做提示注入测试
人工智能·安全·ai安全
Dovis(誓平步青云)1 小时前
多个链接不等于多份证据,新闻核验看板怎样合并来源
java·服务器·前端·javascript·人工智能·pdf·电脑
连涨- AI脑波英语1 小时前
教育集团新增脑机单词速记产品线,怎样把教练工时算进试点成本?
人工智能·脑机单词速记
打工仔折腾 AI1 小时前
从BPE到SentencePiece:Transformer分词原理与Python实战对比
android·人工智能·python·深度学习·langchain·transformer·ai agent 实战
sunneo1 小时前
每周GitCode开源项目推荐
人工智能
AI砖家1 小时前
实测 AI提示词网站 AI妙词:一个「看过效果再用」的中文提示词库,这次更新有点东西
人工智能·ai提示词·ai特效提示词·国内好用的ai提示词
量子-Alex2 小时前
【大模型后训练SFT】LIMA: Less Is More for Alignment
人工智能