侧边栏壁纸
博主头像
牧云

怀璧慎显,博识谨言。

  • 累计撰写 129 篇文章
  • 累计创建 13 个标签
  • 累计收到 8 条评论

目 录CONTENT

文章目录

工具调用与MCP

秋之牧云
2026-03-16 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

工具调用(亦称函数调用)。

AI 大模型中的 Function Call(函数调用), 它是一个极其重要的功能,它极大地扩展了大语言模型的能力边界,使其从单纯的文本生成器升级为能够感知、决策并操作外部世界的智能代理。

其优势与价值如下:

  1. 访问实时信息: 获取股票、天气、新闻、航班等最新数据。

  2. 操作外部系统: 发送邮件、创建日历事件、控制 IoT 设备、操作数据库。

  3. 执行精确计算/转换: 调用计算器、货币转换器等。

  4. 检索私有/专有数据: 连接企业内部数据库、知识库、CRM 系统。

  5. 增强可靠性: 将需要精确性的任务(如计算、数据查询)交给专门工具处理,减少模型“幻觉”。

  6. 构建智能代理(Agents):Function Call 是实现 AI Agent 自动规划和执行复杂任务序列(如“查天气-订机票-发通知”)的基础技术

  7. 简化开发: 开发者只需定义好函数接口,模型能理解如何调用,无需复杂的自然语言解析。

  8. 提升用户体验: 用户可以用自然语言无缝触发复杂的后端操作。

1. 调用工具查询日期和天气

1.1. 定义 Tool 工具

import org.springframework.ai.tool.annotation.Tool;

@Slf4j
public class DateTimeTools {

    @Tool(description = "获取当前日期和时间")
    String getCurrentDateTime() {
        return LocalDateTime.now().toString();
    }
}

1.2. ChatClient 使用工具调用

并不是所有 AI 大模型都支持工具调用功能的

/**
 * 流式对话
 * @param message
 * @return
 */
@GetMapping(value = "/generateStream", produces = "text/html;charset=utf-8")
public Flux<String> generateStream(@RequestParam(value = "message", defaultValue = "你是谁?") String message,
                                   @RequestParam(value = "chatId") String chatId) {

    // 流式输出
    return chatClient.prompt()
            .tools(new DateTimeTools()) // Function Call
            .user(message) // 提示词
            .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
            .stream()
            .content();

}

1.2.1. 定义多个 Tool 工具

import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;

@Slf4j
public class WeatherTools {

    @Tool(description = "获取当日的天气情况,时间参数需为 ISO-8601 格式")
    String getWeather(String time) {
        log.info("## time: {}", time);

        // TODO 调用第三方接口,获取当日天气情况

        return "今天天气晴朗,最低温度 18℃,最高温度 38℃";
    }
}
/**
 * 流式对话
 * @param message
 * @return
 */
@GetMapping(value = "/generateStream", produces = "text/html;charset=utf-8")
public Flux<String> generateStream(@RequestParam(value = "message", defaultValue = "你是谁?") String message,
                                   @RequestParam(value = "chatId") String chatId) {

    // 流式输出
    return chatClient.prompt()
            .tools(new DateTimeTools(), new WeatherTools()) // Function Call
            .user(message) // 提示词
            .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
            .stream()
            .content();

}

1.3. ChatModel 使用工具调用

@RestController
@RequestMapping("/v13/ai")
public class ToolCallingController {

    @Resource
    private DeepSeekChatModel chatModel;

    /**
     * 流式对话
     * @param message
     * @return
     */
    @GetMapping(value = "/generateStream", produces = "text/html;charset=utf-8")
    public Flux<String> generateStream(@RequestParam(value = "message") String message) {
        // 将 DateTimeTools 注册到工具集中
        ToolCallback[] tools = ToolCallbacks.from(new DateTimeTools());

        // 构建聊天选项配置,设置工具回调功能
        ChatOptions chatOptions = ToolCallingChatOptions.builder()
                .toolCallbacks(tools)
                .build();

        // 构建提示词
        Prompt prompt = new Prompt(new UserMessage(message), chatOptions);

        // 流式输出
        return chatModel.stream(prompt)
                .mapNotNull(chatResponse -> {
                    // 获取响应内容
                    DeepSeekAssistantMessage deepSeekAssistantMessage = (DeepSeekAssistantMessage) chatResponse.getResult().getOutput();
                    // 推理内容
                    String reasoningContent = deepSeekAssistantMessage.getReasoningContent();
                    // 推理结束后的正式回答
                    String text = deepSeekAssistantMessage.getText();

                    return StringUtils.isNotBlank(reasoningContent) ? reasoningContent : text;
                });
    }
}

2. MCP 协议

通过标准化接口,让AI模型能像使用 “USB接口” 一样,连接各类外部资源,如数据库、第三方 API、文件系统等等,从而突破静态训练数据的限制,实现动态任务执行。

下面列举一些 MCP 的应用场景:

场景类型

案例

智能开发

程序员在 IDE 中用自然语言即可操作 GitHub:如 “ 修复 main

分支的 CI 错误并提交 PR ”,自动执行代码检查→提交→PR创建全流程

跨平台数据整合

电商大促监控:实时聚合天猫销量、京东库存、拼多多评价,生成跨平台分析报告

企业服务智能化

银行客服通过 MCP 连接专网 ERP 系统,实时调取客户持仓数据生成投资建议

个人助理

“整理上周会议记录并同步待办至日历”:AI 直接操作文件系统 + 日历 API

学术研究

“查找气候变暖最新研究”:AI 通过 MCP 服务器访问学术数据库并生成文献综述

2.1. MCP 架构

MCP 采用经典的 “客户端-服务端” 架构

https://img.quanxiaoha.com/quanxiaoha/175210541772851

  • Host 主机:通常为 AI 应用,比如 Cursor,Trae 等代码编辑器或其他应用。主机负责接收用户的问题与指令,再调用 AI 大模型,当大模型觉得需要调用外部工具时,Host 主机会主动调用 MCP 客户端。

  • MCP 客户端客户端通常内置于主机中,负责与 MCP 服务端建立连接、发送请求和接收请求。我们可以把 MCP 客户端理解为公司 “前台小姐姐”,为外来人员提供服务,有啥事都可以咨询她,由她将任务转交给不同部门去处理。

  • MCP 服务端:MCP 服务器可以看做是一个 “工具箱” 或 “数据源”,供 MCP 客户端来调用,例如访问文件系统、数据库查询、操作浏览器等等。MCP 服务器负责执行具体的操作,如调用工具或访问数据,再将结果返回给 MCP 客户端。

多个 MCP 服务器可以同时运行,当大模型觉得自己无法完成某项任务时,就可以让这些 MCP 服务器来协助。

举个栗子,我们向主机提问 “我的 E:/ 盘下,有哪些文件?”,比如 Cursor 编辑器,整体的处理流程大致如下:

  1. 我们向 Cursor 提问;

  2. Cursor 调用大模型来分析我们的问题,由大模型决定是否需要访问本地文件系统;

  3. 若需要,主机内置的 MCP 客户端被激活,与本地文件系统的 MCP 服务器建立连接;

  4. MCP 客户端向 MCP 服务端发送请求,想要获取 E:/ 盘下的文件列表;

  5. 本地文件系统 MCP 服务器执行访问操作,获取文件列表,并将结果返回给 MCP 客户端;

  6. MCP 客户端再将结果发送给 AI 大模型;

  7. AI 大模型再以自然语言的方式,告诉我们最终结果;

2.2. 和 Fucntion Call 的区别

  • 定义上:MCP 是一种标准化协议,为 AI 大模型与外部系统之间的交互,提供了规范化的接口,有点类似于 “USB 接口” ,能够连接不同的外接设备,如键盘、鼠标、音箱等等。它能让不同的系统之间能够高效的数据传输与工具调用;而 Function Call 是大语言模型本身提供的一种能力,通过预定义函数,当用户提问后,AI 大模型理解用户的提示词,并判断是否需要调用这些预定义的函数。

  • 技术实现上:MCP 采用了 “客户端-服务端” 架构,标准化处理 MCP 客户端与 MCP 服务端之间的通信,包括请求、响应等等。此架构能够更好的应用复杂的网络环境,和多元化的场景需求;而 Function Call 实现相对简单,它在大模型运行时环境直接执行,但需要开发者提前将函数定义好,并提交给 AI 大模型。

  • 功能与应用场景:MCP 能够处理复杂、异步的任务,例如,你可以将公司内部的系统,封装成 MCP 服务提供调用;而 Function Call 则更适合处理简单、低延迟的任务,例如获取天气数据、实时翻译等。

  • 整合方式:MCP 服务一般由第三方提供好,我们只需要对接即可,无需额外编写代码;而 Function Call 则需要自己编写预定义函数,并提交给 AI 大模型。

3. 整合 MCP Client: 调用高德地图 MCP 服务

让 AI 大模型推荐最近 1000m 内,好评率较高的烧烤店

3.1. MCP.so 介绍

MCP.so(官网:https://mcp.so/zh)是全球领先的MCP(Model Context Protocol,模型上下文协议)服务器资源平台,专注于提供 MCP 服务的导航、托管与调试功能。其核心定位是成为连接 AI 模型与外部工具/数据源的标准化枢纽,推动AI应用开发的效率与可扩展性,目前已收录 1万+ 的 MCP 服务。

进入 MCP.so 官网后,在 Featrued 精选菜单下,可以找到高德地图官方提供的 MCP Server,它包含的工具包括:

  1. 生成专属地图:将出行规划导入高德地图,生成专属地图;

  2. 导航到目的地:根据用户传入经纬度,启动导航;

  3. 打车:根据用户输入起终经纬度坐标,发起打车请求;

  4. 地理编码:将详细的结构化地址转换为经纬度坐标;

  5. 逆地理编码:将一个高德经纬度坐标转换为行政区划地址信息;

  6. IP 定位:IP 定位根据用户输入的 IP 地址,定位 IP 的所在位置;

  7. 天气查询:根据城市名称或者标准adcode查询指定城市的天气;

  8. 骑行路径规划:用于规划骑行通勤方案,规划时会考虑天桥、单行线、封路等情况。最大支持 500km 的骑行路线规划;

  9. 周边搜索:根据用户传入关键词以及坐标,搜索出规定半径范围内的 POI 地点;

TIP: 关于高德地图 MCP Server 更多功能,可阅读官方文档:https://lbs.amap.com/api/mcp-server/summary

3.2. 申请 API Key

在使用高德 MCP 服务之前,需要登录到高德开发平台:https://lbs.amap.com/,创建 Apikey

3.3. 依赖

<!-- MCP Client -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>

3.4. 配置

在 MCP.so 网站中,点击进入高德地图 MCP 服务的详情页中,复制配置。

/resources 目录下,新建一个 mcp-servers-config.json 配置文件,文件名可以随意命名:

{
  "mcpServers": {
    "amap-maps": {
      "command": "cmd",
      "args": [
        "/c",
        "npx",
        "-y",
        "@amap/amap-maps-mcp-server"
      ],
      "env": {
        "AMAP_MAPS_API_KEY": "填写你申请的高德地图 API Key"
      }
    }
  }
}

解释一下:

  • 根对象 mcpServers: 定义一组 MCP 服务器配置;

  • 服务标识 amap-maps: 服务名称,表示这是针对 高德地图(AMap) 的配置。

  • command: 指定要运行的基本命令。这里使用的是 cmd(Windows 命令提示符)。

  • args: 是一个数组,包含传递给命令的参数。在这个例子中:

  • /c 表示执行后面的命令然后终止。

  • npx 是 Node.js 的一个工具,用于执行 npm 包中的命令。注意,需要确保你的电脑已安装好 Node.js。

  • -y 是 npx 的一个选项,表示如果遇到提示(比如是否安装包)都自动回答 yes

  • @amap/amap-maps-mcp-server是要通过 npx 运行的包,即高德地图的 MCP 服务包。

  • env: 是一个环境变量对象,用于设置运行时的环境变量。

  • AMAP_MAPS_API_KEY: 需要填写用户申请的高德地图API Key。

编辑 application.yml 配置文件,添加配置项如下:

spring:
  ai:
    mcp:
      client:
        stdio:
          servers-configuration: classpath:/mcp-servers-config.json # 指定 MCP 服务的配置文件路径
        toolcallback:
          enabled: true # 开启 MCP 客户端的工具回调(Tool Callback)功能

3.5. 配置 ChatClient

编辑 ChatClientConfig 配置类,将 MCP 工具配置上

import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatClientConfig {

    @Resource
    private ChatMemory chatMemory;

    /**
     * 初始化 ChatClient 客户端
     * @param chatModel
     * @return
     */
    @Bean
    public ChatClient chatClient(DeepSeekChatModel chatModel, ToolCallbackProvider tools) {
        return ChatClient.builder(chatModel)
                .defaultToolCallbacks(tools) // MCP
                .defaultAdvisors(
                    new SimpleLoggerAdvisor(), // 添加 Spring AI 内置的日志记录功能
                    MessageChatMemoryAdvisor.builder(chatMemory).build())
                .build();
    }
}

3.6. 调用

@RestController
@RequestMapping("/mcp/ai")
public class McpChatClientController {

    @Resource
    private ChatClient chatClient;

    /**
     * 流式对话
     * @param message
     * @return
     */
    @GetMapping(value = "/generateStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<AIResponse> generateStream(@RequestParam(value = "message") String message,
                                           @RequestParam(value = "chatId") String chatId) {

        // 流式输出
        return chatClient.prompt()
            .user(message) // 提示词
            .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
            .stream()
            .chatResponse()
            .mapNotNull(chatResponse -> {
                    Generation generation = chatResponse.getResult();
                    String text = generation.getOutput().getText();
                    return AIResponse.builder().v(text).build();
            });
    }
}

3.6.1. 场景1:IP 定位

36.57.99.213 这个 IP 的具体位置在哪里

3.6.2. 场景2:天气情况

合肥今天的天气怎么样

3.6.3. 场景3:推荐附近高评分门店

我的坐标是东经117°12′24.754″,北纬31°56′0.982″,附近 1000m 有什么好评率较高的烧烤店,推荐一下。需要附带上门店的图片,以 markdown 图片格式展示出来

4. 搭建自定义 MCP Server:获取 QQ 信息

4.1. 新建 MCP Server 项目

4.2. 依赖

<!-- Spring AI 依赖管理   -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>${spring-ai.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <!-- MCP Server WebFlux -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
    </dependency>
</dependencies>

4.3. MCP Server 三种部署模式

对比维度

STDIO 模式

WebMVC SSE 模式

WebFlux SSE 模式

依赖项

spring-ai-starter-mcp-server

spring-ai-starter-mcp-server-webmvc

spring-ai-starter-mcp-server-webflux

传输机制

标准输入/输出(进程间通信)

基于 Servlet 的 SSE(HTTP 长连接)

响应式 SSE(非阻塞 HTTP 流)

线程模型

同步阻塞(SYNC)

同步阻塞(SYNC)

异步非阻塞(ASYNC)

适用场景

命令行工具、桌面应用、无网络环境

传统 Spring MVC 应用

高并发微服务、云原生架构

配置要点

需禁用 Web 服务: spring.main.web-application-type=none

自动启用 Web 服务(Tomcat/Jetty) 支持混合 STDIO(需显式开启)

需移除 spring-boot-starter-web

依赖 使用 Netty 服务器

性能特点

低延迟,轻量级

中等并发能力,依赖线程池

高并发,低资源消耗(线程复用)

4.4. 配置

server:
  port: 8000 # 启动端口号
spring:
  ai:
    mcp:
      server:
        type: sync # 处理模式,SYNC 同步或 ASYNC 异步
        name: mcp-server-qq # MCP Server 名称
        version: 1.0.0 # MCP Server 版本号

# 奶思猫 API 密钥(替换为你自己的)
api-key: xxx

根据 QQ 号查询 QQ 信息,需要调用奶思猫第三方平台的 API, 需要先申请 API Key。如何申请可参考《前后端分离博客2期》的《6.2节

4.5. 配置 RestTemplate

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(10000); // 连接超时时间:10秒
        factory.setReadTimeout(10000); // 读取超时时间:10秒
        return new RestTemplate(factory);
    }
}

4.6. 定义 Tool

新建一个 /tools 包,并通过 @Tool 注解声明一个 “根据 QQ 号获取 QQ 信息” 工具方法,模式和之前定义 Function Call 差不太多

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
@Slf4j
public class QQTool {

    @Resource
    private RestTemplate restTemplate;

    @Value("${api-key}")
    private String apiKey;

    @Tool(description = "根据 QQ 号获取 QQ 信息")
    public String getQQInfo(String qq) {
        log.info("## 获取 QQ 信息, qq: {}", qq);

        // 请求第三方接口
        String url = String.format("https://api.nsmao.net/api/qq/query?qq=%s&key=%s", qq, apiKey);
        String result = restTemplate.getForObject(url, String.class);

        log.info("## 返参: {}", result);
        return result;
    }

}

4.7. 注册工具

在启动类,注册工具回调(Tool Callbacks

import com.quanxiaoha.mcp.server.qq.tools.QQTool;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    /**
     * 注册工具回调(Tool Callbacks)
     * @param qqTool
     * @return
     */
    @Bean
    public ToolCallbackProvider qqTools(QQTool qqTool) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(qqTool)
                .build();
    }

}

5. 对接自定义 MCP Server

5.1. 添加配置

添加连接自定义 MCP Server 的相关配置项

spring:
  ai:
    mcp:
      client:
        type: sync # 客户端使用同步通信模式
        request-timeout: 20s # 设置单次请求超时时间
        sse: # SSE (Server-Sent Events) 流式响应配置
          connections: # 定义 SSE 连接端点
            qq-mcp-server: # 自定义连接名称(可任意命名)
              url: http://localhost:8000 # MCP 服务实际地址

5.2. 调用

@RestController
@RequestMapping("/mcp/ai")
public class McpChatClientController {

    @Resource
    private ChatClient chatClient;

    /**
     * 流式对话
     * @param message
     * @return
     */
    @GetMapping(value = "/generateStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<AIResponse> generateStream(@RequestParam(value = "message") String message,
                                           @RequestParam(value = "chatId") String chatId) {

        // 流式输出
        return chatClient.prompt()
            .user(message) // 提示词
            .stream()
            .chatResponse()
            .mapNotNull(chatResponse -> {
                    Generation generation = chatResponse.getResult();
                    String text = generation.getOutput().getText();
                    return AIResponse.builder().v(text).build();
            });
    }
}
870361626 这个 qq 号的信息展示一下,头像需要以 Markdown 格式图片格式返回

5.3. mcp-servers-config.json 对接多个 MCP Server

高德地图 MCP 服务,是直接定义在 json 文件中的,那么这种方式如何对接多个 MCP 服务呢?接下来,我们将尝试再添加一个新的 MCP 服务 —— @modelcontextprotocol/server-filesystem

5.3.1. 介绍

@modelcontextprotocol/server-filesystemModel Context Protocol (MCP) 生态中的官方参考服务器之一,专注于为大型语言模型(LLM)提供安全、标准化的文件系统访问能力。其核心作用是将本地文件系统的操作(如读取、搜索文件)封装为 AI 可调用的工具,从而增强模型对实时数据的处理能力。

5.3.2. 添加 MCP 服务配置

编辑 mcp-servers-config.json 文件,在 /mcpServers 节点下,额外添加 filesystem 的 MCP 服务配置:

{
  "mcpServers": {
    "amap-maps": {
      "command": "cmd",
      "args": [
        "/c",
        "npx",
        "-y",
        "@amap/amap-maps-mcp-server"
      ],
      "env": {
        "AMAP_MAPS_API_KEY": "725178d06255921fee4111576572ab13"
      }
    },
    "filesystem": {
      "command": "cmd",
      "args": [
        "/c",
        "npx",
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "D:\\",
        "E:\\"
      ]
    }
  }
}

TIP: 上述配置项中,D:\\E:\\ 表示授权能够访问的路径。

0

评论区