Spring AI使用tool Calling和MCP

深入探索 Spring AI

Spring AI版本1.0.0.M6

在人工智能与软件开发深度融合的时代,Spring AI 作为一个强大的框架,持续为开发者提供着高效且便捷的工具,以实现与大语言模型(LLM)的无缝交互。Spring AI 的最新版本引入了一系列令人瞩目的特性,其中 Function Calling 到 Tool Calling 的转换以及模型上下文协议(MCP)的应用,标志着该框架在 AI 集成领域的又一次重大飞跃。

聊天接口示例

在今天的内容之前我们回忆下如何使用SpringAI实现一个简单的聊天接口,使用千问API实现聊天功能:

  1. 添加依赖
xml 复制代码
<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-starter</artifactId>
</dependency> 
  1. 配置
yaml 复制代码
spring:
  ai:
    ## Alibaba
    dashscope:
      api-key: ${DASH_SCOPE_API_KEY}
      chat:
        enable: true
        options:
          model: qwen-max
  1. 实现
java 复制代码
@Bean
public ChatClient chatClient(ChatClient.Builder chatClientBuilder) throws IOException {

    var chatClient = chatClientBuilder
            .defaultSystem("You are a helpful assistant.")
            .defaultAdvisors(new SimpleLoggerAdvisor()) // LOG
            .build();
    return chatClient;
}

/**
 * 调用
 * @param message
 * @return
 */
public String completion(String message) {
    return chatClient
            .prompt().user(message)
            .call().content();
}

当进行下面的提问时:

现在北京时间几点了?

很遗憾,我无法直接获取实时的北京时间。你可以查看设备上的时间或者通过网络搜索来获取准确的北京时间。如果你使用的是电脑或手机,通常在屏幕的一角会显示当前的时间。

Function Calling

在早期的 AI 交互中,Function Calling 是一种常见的机制,允许模型在生成回复时调用外部函数以获取额外信息。然而,这种方式在扩展性和灵活性上存在一定的局限性。而 Spring AI 最新版本引入的 Tool Calling 则是对 Function Calling 的进一步演进。Tool Calling 将函数调用抽象为工具调用,将工具视为可复用的资源,模型可以根据需求动态调用这些工具,以完成更复杂的任务。在新版本中已经被改为Tool Calling。

工具主要用于:

信息检索

此类工具可用于从外部来源(例如数据库、Web 服务、文件系统或 Web

搜索引擎)检索信息。其目标是增强模型的知识,使其能够回答原本无法回答的问题。因此,它们可用于检索增强生成 (RAG)

场景。例如,可以使用工具检索给定位置的当前天气、检索最新新闻文章或查询数据库中的特定记录。

采取行动

此类别中的工具可用于在软件系统中采取行动,例如发送电子邮件、在数据库中创建新记录、提交表单或触发工作流。其目标是自动化原本需要人工干预或明确编程的任务。例如,可以使用工具为与聊天机器人交互的客户预订航班、在网页上填写表单,或在代码生成场景中基于自动化测试 (TDD) 实现 Java 类。

尽管我们通常将工具调用称为模型功能,但实际上工具调用逻辑是由客户端应用程序提供的。模型只能请求工具调用并提供输入参数,而应用程序负责根据输入参数执行工具调用并返回结果。

Spring AI 提供了便捷的 API 来定义工具、解析来自模型的工具调用请求以及执行工具调用。

为了解决上面关于时间问题的解决方案,我们可以定义一个工具,并嵌入到模型中...

java 复制代码
public class TimeTools {

    private static final Logger logger = LoggerFactory.getLogger(TimeTools.class);

    @Tool(description = "Get the time of a specified city.")
    public String getCityTimeMethod(@ToolParam(description = "Time zone id, such as Asia/Shanghai") String timeZoneId) {
        logger.info("The current time zone is {}", timeZoneId);
        return String.format("The current time zone is %s and the current time is " + "%s", timeZoneId, ZoneUtils.getTimeByZoneId(timeZoneId));
    }
}
java 复制代码
public ChatClient chatClient(ChatClient.Builder chatClientBuilder) throws IOException {
    // ...
    chatClientBuilder.defaultTools(timeTool);
    // ...
}

Function Calling实现了大语言模型(LLM)与外部函数或工具进行交互的能力。这一机制赋予了 AI 系统更强大的功能和灵活性,使其能够处理更加复杂和动态的任务。

注意不是所有模型都支持FunctionCalling

MCP

MCP(Model Context Protocol,模型上下文协议) 是的一种开放协议,旨在统一大语言模型(LLM)与外部数据源、工具和服务之间的交互标准,推动 AI 应用的标准化和去中心化发展。

MCP 提供了一种统一的接口,使得不同的工具和服务可以以标准化的方式与模型进行交互。

核心功能

  1. 标准化交互

    MCP 提供了一套通用的通信协议、数据格式和规则,使 LLM 能够以统一的方式与外部资源(如数据库、API、文件系统等)进行交互,无需为每个工具单独开发适配接口。

  2. 增强模型能力

    通过 MCP,LLM 可以动态调用外部工具或数据源,例如实时获取天气信息、查询数据库、调用第三方服务等,从而扩展模型的功能边界。

  3. 安全与合规

    MCP 内置了安全机制,确保数据传输的安全性,并支持细粒度的权限控制,避免数据泄露和滥用。

  4. 降低开发成本

    开发者无需重复造轮子,可直接基于 MCP 协议构建 AI 应用,显著减少开发时间和成本。

在1.0.0-M6版本中引入了MCP,使得可以基于Spring AI实现各种扩展

此时聊天应用作MCP服务的调用者,也就是客户端,需要调用外部的MCP服务,首先对聊天服务改造:

  1. 添加必要的依赖:
xml 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
</dependency>
  1. 通过配置ChatClient完成集成:
java 复制代码
    @Bean
    public ChatClient chatClient(ToolCallbackProvider toolsProvider) throws IOException {
        var chatClient = chatClientBuilder
                // ...
                .defaultTools( toolsProvider.getToolCallbacks() ) //mcp
                // ...
                .build();
        return chatClient;
    }

SpringAI中,MCP 客户端支持两种传输方式:STDIO 和 SSE。

标准启动器通过STDIO(进程内)和/或SSE(远程)传输同时连接到一个或多个 MCP 服务器。SSE 连接使用基于 HttpClient 的传输实现。每个与 MCP 服务器的连接都会创建一个新的 MCP 客户端实例。

STDIO

其实就是通过本地命令进行调用的实现,需要注意的是,返回的数据结果必须遵循MCP规范,我们可以基于Spring开发一个可执行的jar程序包,然后由客户端调用。

  1. 添加依赖
xml 复制代码
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
</dependency> 
  1. 实现Tool并注册
java 复制代码
@Service
public class TranslationService {
    
    @Tool(description = "将内容翻译成英文")
    public String translate(String content) {
        return "hello";
    }
}

@Bean
public ToolCallbackProvider weatherTools(TranslationService translationService) {
   return MethodToolCallbackProvider.builder().toolObjects(translationService).build();
}
  1. 添加配置,注意这里要关掉所有日志相关的输出
yml 复制代码
spring:
   main:
      web-application-type: none
      banner-mode: off
   ai:
      mcp:
         server:
            name: translation-server
            version: 0.0.1
logging:
   level:
      root: off 
  1. 打包,记得使用spring-boot-maven-plugin插件打包,下面的mcpServers引用的就是这里的jar

  2. 修改聊天应用配置,并且重启

yaml 复制代码
spring:
  ai:
    mcp:
      client:
        type: SYNC
        stdio:
          servers-configuration: classpath:mcp-stdio-servers.json

mcp-stdio-servers.json

json 复制代码
{
   "mcpServers": {
      "weather": {
         "command": "java",
         "args": [
            "-Dspring.ai.mcp.server.stdio=true",
            "-Dspring.main.web-application-type=none",
            "-Dlogging.pattern.console=",
            "-jar",
            "your_jar_path/mcp-stdio-server-1.0.1-SNAPSHOT.jar"
         ],
         "env": {}
      }
   }
}

提问: 翻译单词运势

回答: 看起来在处理您的请求时发生了一些混淆。单词"运势"的正确英文翻译应为 "fortune" 而非 "hello"。如果您需要关于星座运势的信息,请告诉我您的星座名称。如果是其他类型的运势,请提供更多的细节。

因为我没有实现,全部返回的是hello,看样子模型对我们的结果进一步做了处理

SSE

这里提供一个简单的示例,主要实现星座运势获取的Mcp,这是一个单独的基于Spring开发的应用,与上面的聊天应用隔离:

  1. 引入相关依赖
xml 复制代码
<dependency>
   <groupId>org.springframework.ai</groupId>
   <artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId>
</dependency>
  1. 定义Tool实现
java 复制代码
@Service
public class HoroscopeService {

    private String url = "https://apis.tianapi.com/star/index?key=%s&astro=%s";
    private String key = "xx";

    private RestTemplate restTemplate = new RestTemplate();
    private ObjectMapper objectMapper = new ObjectMapper();

    @Tool(description = "Get constellation fortune by consName")
    public String getFortune(String consName) {
        Map map = restTemplate.getForObject(String.format(url, key, consName), Map.class);
        try {
            return objectMapper.writeValueAsString(map.get("result"));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return "获取失败:"+ e.getMessage();
        }
    }
}
  1. 配置文件
yml 复制代码
spring:
   ai:
      mcp:
         server:
            name: webmvc-mcp-server
            version: 1.0.0
            type: SYNC
            sse-message-endpoint: /mcp/messages

server:
   port: 8081
   servlet:
      encoding:
         charset: utf-8
         enabled: true
         force: true 
  1. 启动应用,访问:http://localhost:8081

  2. 修改聊天应用配置,并且重启

yaml 复制代码
spring:
  ai:
    mcp:
      client:
        type: SYNC
        sse:
           connections:
              constellation:
                 url: http://localhost:8081

提问: 白羊座的运势

回答: 今天白羊座的运势如下: - 综合指数:80% - 爱情指数:70% - 工作指数:55% - 财运指数:65% - 健康指数:80% - 幸运颜色:蓝色 - 幸运数字:5 - 贵人星座:射手座 今日概述:今天你可能会忍不住向另一半抱怨工作上遇到的问题,但这样也会给对方带来困扰。在感情中,要注意不要过多地向对方发泄负面情绪,而应该把温暖和幸福带给他们。

结束语

现阶段的AI技术,恰似一台功能强大却需精心调校的计算机系统。它并非"即插即用"的万能工具,而是需要开发者如同配置硬件般,根据特定业务场景的需求,按需增加"认知模块"与"计算资源"。这种灵活扩展的能力,与模块化计算平台(MCP,Modular Computing Platform)的设计理念不谋而合------通过标准化接口与可组合架构,让AI系统既能像积木般自由拼接算法能力,又能像云计算般弹性调度算力资源。开发者需像搭建乐高城堡般,将自然语言处理、视觉识别、决策推理等模块按需组合,再通过数据管道与反馈机制持续优化,最终让AI在医疗诊断、智能制造、智慧城市等垂直领域中,展现出接近专家水平的场景化智能

相关推荐
特立独行的猫a5 分钟前
11款常用C++在线编译与运行平台推荐与对比
java·开发语言·c++
louisgeek15 分钟前
Java 位运算
java
zskj_zhyl26 分钟前
AI健康小屋“15分钟服务圈”:如何重构社区健康生态?
大数据·人工智能·物联网
荔枝味啊~32 分钟前
相机位姿估计
人工智能·计算机视觉·3d
hweiyu001 小时前
Maven 私库
java·maven
Boilermaker19921 小时前
【Java EE】SpringIoC
前端·数据库·spring
Super Rookie1 小时前
Spring Boot 企业项目技术选型
java·spring boot·后端
写不出来就跑路1 小时前
Spring Security架构与实战全解析
java·spring·架构
陈纬度啊1 小时前
自动驾驶ROS2应用技术详解
人工智能·自动驾驶·unix
开开心心_Every2 小时前
全能视频处理工具介绍说明
开发语言·人工智能·django·pdf·flask·c#·音视频