cover_image

Spring AI、DeepSeek 与 MCP:对话驱动的接口查询新范式

glmapper 磊叔的技术博客
2025年04月04日 07:43

 

开篇

随着人工智能技术的飞速发展,我们正步入一个全新的应用开发时代。传统的接口调用方式,即通过硬编码或配置文件进行静态调用,正逐渐被更为智能、动态的方式所取代。本文将不是探讨 Spring AI、DeepSeek 以及 MCP(Model Context Protocol)这三项前沿技术,而是结合一个实际的案例工程,展示如何利用对话驱动的方式实现接口查询。 Spring AI 作为一个新兴的框架,旨在简化 AI 应用的开发流程,提供了一套统一的抽象层,使得开发者能够轻松集成各种大语言模型。DeepSeek 作为国内领先的大语言模型,在自然语言处理方面表现出色,为我们提供了强大的语义理解和生成能力。而 MCP,作为一种模型上下文协议,则为大语言模型与外部工具和数据源之间的交互提供了标准化的方式。 本文将通过一个实际的案例,演示如何将这三项技术结合起来,构建一个能够通过自然语言对话查询接口数据的应用。我们将从环境搭建、代码实现到效果展示,一步步地剖析其原理和实现细节,帮助读者深入理解并掌握这项技术。

案例

本案例工程采样 SpringBoot 3.2.4 + JDK21 + Spring AI MCP 1.0.0-M6 版本完成,主要依赖如下:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    <version>1.0.0-M6</version>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
    <version>1.0.0-M6</version>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId>
    <version>1.0.0-M6</version>
</dependency>
<!-- Optional: For using the OpenAI API -->
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.2.1</version>
</dependency>

备注:这里引入 httpclient5 主要是因为在测试中发现 cannot retry due to server authentication, in streaming mode 一场报错,通过查阅资料,发现这里https://stackoverflow.com/questions/78121525/spring-ai-openai-error-extracting-response-of-type-openaiapiembeddinglist 给出的解决方案是可以满足测试运行要求的。笔者没有细究问题产生原因,与本篇写作意图偏差较多。

首先是构建一个 SpringBoot + JDK21 的工程脚手架,然后在引入上述依赖以后,我们的所需实现的功能的三方依赖就几本满足了。Spring AI 、deepseek 以及 mcp 集成核心来说就是:使用 spring ai 的编程范式,底座使用 deepseek 作为 spring ai chat 的底层支撑模型,mcp 则是将当前工程的一些服务能力作为 chat 的工具用于回调。

spring ai 集成 deepseek

spring ai deepseek integration
spring ai deepseek integration

这里参考 https://docs.spring.io/spring-ai/reference/1.0/api/chat/deepseek-chat.html 官方文档即可,上手非常简单。核心是完成 deepseek api 所有的 api_key 的申请,网上很多,不再赘述。配置如下:

spring:
  ai:
    openai:
      api-key: ${your-deepseek-api-key}
      chat:
        base-url: https://api.deepseek.com
        completions-path: /v1/chat/completions
        options:
          model: deepseek-chat

下面来构建 chat 对象和一个用于对话的 ChatController

  • • ChatClientConfig
    @Configuration
    public class ChatClientConfig {
        @Bean
      public ChatClient chatClient(ChatClient.Builder builder) {
          return builder.build();
      }
    }
  • • ChatController
    /**
     * 聊天控制器,处理AI聊天请求
     */

    @RestController
    @RequestMapping("/api/chat")
    @RequiredArgsConstructor
    publicclassChatController {

        privatestaticfinalLoggerLOGGER= LoggerFactory.getLogger(ChatController.class);

        @Resource
        private ChatClient chatClient;

        /**
         * 处理聊天请求,使用AI和MCP工具进行响应
         *
         * @param request 聊天请求
         * @return 包含AI回复的响应
         */

        @PostMapping
        public ResponseEntity<ChatResponse> chat(@RequestBody ChatRequest request) {
            try {
                // 创建用户消息
                Stringmessage= request.getMessage();
                // 使用非流式 API 调用聊天
                Stringcontent= chatClient.prompt()
                        .user(message)
                        .call()
                        .content();
                return ResponseEntity.ok(newChatResponse(content));
            } catch (Exception e) {
                LOGGER.error("Chat request failed", e);
                return ResponseEntity.ok(newChatResponse("处理请求时出错: " + e.getMessage()));
            }
        }

此时我们启动服务,通过此对话就可以和 deepseek 进行对话了;此时我问它张三有几本书,它是无法回答我的。

image-20250404145728622
image-20250404145728622

将应用服务作为 mcp server

此案例参考了网上小伙伴的部分代码,在此表示感谢技术社区同学们的无私奉献

这里我提供一个 BookService ,主要提供 根据书名模糊查询图书根据作者精确查询图书根据图书分类精确查询图书 三个方法

package com.glmapper.mcp.service;

import com.glmapper.mcp.model.Book;
import com.glmapper.mcp.repository.BookRepository;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Classname BookService
 * @Description BookService
 * @Date 2025/4/3 14:14
 * @Created by glmapper
 */

@Service
publicclassBookService {

    @Autowired
    private BookRepository bookRepository;
        
    @Tool(name = "findBooksByTitle", description = "根据书名模糊查询图书,支持部分标题匹配")
    public List<Book> findBooksByTitle(@ToolParam(description = "书名关键词") String title) {
        return bookRepository.findByTitleContaining(title);
    }

    @Tool(name = "findBooksByAuthor", description = "根据作者精确查询图书")
    public List<Book> findBooksByAuthor(@ToolParam(description = "作者姓名") String author) {
        return bookRepository.findByAuthor(author);
    }

    @Tool(name = "findBooksByCategory", description = "根据图书分类精确查询图书")
    public List<Book> findBooksByCategory(@ToolParam(description = "图书分类") String category) {
        return bookRepository.findByCategory(category);
    }
}

这里相比于我们传统方式的 service方法来说,每个method上增加了一个@Tool注解,该注解的作用就是将一个方法标记为一个Spring AI工具。这里可能有对Spring AI熟悉的小伙伴可能会疑惑,这玩意不就是个 function call 吗?没毛病,@Tool注解是为整个Spring AIFunction Calling机制提供“工具定义”的;但是它也可以被MCP使用,Spring AIMCP基于 Agent 架构构建的,而Agent 在执行推理过程中会判断:

  • • 1、当前的对话上下文是否足够回答用户问题?
  • • 2、是否需要调用某个工具(即 @Tool 方法)来获取额外信息?
  • • 3、如果需要,就调用这些函数(由 @Tool 提供),获取结果,再继续推理。

所以,在 MCP 流程中,@Tool 提供的函数是 Agent 推理链中的一部分。言归正传,当完成这个工具之后,就需要将这个工具赋能给 chat。这里需要做两件事:

  • • MCP 服务器配置类,负责注册 MCP 工具
    /**
     * MCP服务器配置类,负责注册MCP工具
     */

    @Configuration
    public class McpServerConfig {

        /**
         * 注册工具回调提供者,将BookService中的@Tool方法暴露为MCP工具
         *
         * @param bookService 图书服务
         * @return 工具回调提供者
         */

        @Bean
        public MethodToolCallbackProvider bookToolCallbackProvider(BookService bookService) {
            return MethodToolCallbackProvider.builder().toolObjects(bookService).build();
        }
    }
  • • bookToolCallbackProvider 绑定给 chat
    @Configuration
    publicclassChatClientConfig {

        @Autowired
        private ToolCallbackProvider bookToolCallbackProvider;

        /**
         * 配置ChatClient,注册系统指令和工具函数
         */

        @Bean
        public ChatClient chatClient(ChatClient.Builder builder) {

            //return builder.build();
            return builder
                    .defaultSystem("你是一个图书管理助手,可以帮助用户查询图书信息。" +
                            "你可以根据书名模糊查询、根据作者查询和根据分类查询图书。" +
                            "回复时,请使用简洁友好的语言,并将图书信息整理为易读的格式。")
                    // 注册工具方法
                    .defaultTools(bookToolCallbackProvider)
                    .build();
        }
    }

到上面基本逻辑已经有了,最后就是准备一些测试数据。

测试数据准备

方便起见,笔者在测试时就是使用 List 来存储数据模拟数据库的。这里贴出来部分主要为了透出数据,便于接下来测试时看效果。

// 准备示例数据
List<Book> sampleBooks = Arrays.asList(
        newBook(null"Spring实战(第6版)""编程""Craig Walls",
                LocalDate.of(2022115), "9787115582247"),
        newBook(null"深入理解Java虚拟机""编程""周志明",
                LocalDate.of(2019121), "9787111641247"),
        newBook(null"Java编程思想(第4版)""编程""Bruce Eckel",
                LocalDate.of(200761), "9787111213826"),
        newBook(null"算法(第4版)""计算机科学""Robert Sedgewick",
                LocalDate.of(2012101), "9787115293800"),
        newBook(null"云原生架构""架构设计""张三",
                LocalDate.of(2023315), "9781234567890"),
        newBook(null"微服务设计模式""架构设计""张三",
                LocalDate.of(2021820), "9789876543210"),
        newBook(null"领域驱动设计""架构设计""Eric Evans",
                LocalDate.of(2010410), "9787111214748"),
        newBook(null"高性能MySQL""数据库""Baron Schwartz",
                LocalDate.of(2013525), "9787111464747"),
        newBook(null"Redis实战""数据库""Josiah L. Carlson",
                LocalDate.of(2015930), "9787115419378"),
        newBook(null"深入浅出Docker""容器技术""李四",
                LocalDate.of(20221120), "9787123456789")
);

测试验证

在文档开始,当我们没有使用 MCP 将服务接口暴露出 chat 时,我们问 作者张三有几本书 ,它的回答其实是不明确的。当将服务作为 MCP server 后,看到的结果如下:

image-20250404152456429
image-20250404152456429

关于 MCP 协议,笔者在 深入探讨模型上下文协议(MCP)这篇文章中有过一些介绍,感兴趣的同学可以跳转过去阅读。另外本篇所有代码,如有所需,可以私信我,无偿提供(没有上传 github 是因为代码不复杂,没有必要单独建仓库,另外笔者也不希望提交代码时因为疏忽将 api-key 等信息暴露出去,望见谅)

结尾

通过本文的介绍和案例演示,我们不难发现,Spring AI、DeepSeek 和 MCP 的结合,为接口查询带来了全新的可能性。这种基于对话驱动的方式,不仅提高了接口调用的灵活性和智能化水平,也为开发者提供了更为便捷和高效的开发体验。 随着技术的不断演进,我们有理由相信,这种对话驱动的接口查询方式将在未来得到更广泛的应用。无论是智能客服、数据分析还是自动化运维,我们都可以借助这些技术,构建出更为智能、高效的应用系统。 希望本文能够帮助读者深入理解 Spring AI、DeepSeek 和 MCP 的原理和应用,并启发大家在实际项目中进行更多的探索和创新。

 


MCP · 目录
上一篇深入探讨模型上下文协议(MCP)
继续滑动看下一个
磊叔的技术博客
向上滑动看下一个