SpringBoot中使用MCP和通义千问来处理和分析数据
文章目录
- 前言
- 一、正文
- 1.1 项目结构
- 1.2 项目环境
- 1.3 完整代码
- 1.3.1 spring-mcp-demo的pom文件
- 1.3.2 sse-server 的pom文件
- 1.3.3 ChatRequest
- 1.3.4 ChatResponse
- 1.3.5 ChatClientConfig
- 1.3.6 ServiceProviderConfig
- 1.3.7 ChatController
- 1.3.8 Student
- 1.3.9 StudentManager
- 1.3.10 StudentService
- 1.3.11 StudentServiceImpl
- 1.3.12 SseServerApplication
- 1.3.13 application,yml
- 1.4 调用聊天接口
- 1.4.1 请求1
- 1.4.2 请求2
- 1.4.3 请求3
- 1.4.4 请求4
- 1.4.5 请求5
前言
近来,AI大模型的应用在各种场景下出尽了风头,那么不爱学习的java程序员,不是一个好程序员。这里我学习并简单使用了SpringBoot 和 MCP ,大模型使用通义千问。
MCP(Model Context Protocol,模型上下文协议)是一种用于大语言模型(LLM)与外部环境交互的标准化协议。它主要用于管理模型的上下文信息,使AI模型能够在多步骤、有状态的交互中保持上下文连贯。
相比传统API每次请求独立处理的方式,MCP通过统一的协议支持更高效、复杂的交互流程,提升了AI应用的智能性和实用性。
本文将以最基础的【学生查询功能】来搭配MCP 和 通义千问,实现智能对话功能。
另外,顺便标记了一个【创建学生】的功能,也可以使用自然语言来触发。
一、正文
1.1 项目结构
项目父模块是一个pom打包方式的模块,主要管理依赖和版本。另外配置了spring未发布的jar仓库地址。
核心代码在【sse-server】模块中。起初项目是想做成sse连接对话式的,后来做成了同步。所以这里的项目名也懒的改了。(SSE 的方式以后再说,后续如果再写一版,我会补充进来)
PS:这里以 manager 层作为 DAO 层,做数据访问,使用内存数据进行模拟数据库。
1.2 项目环境
项目采用 java 21 + springboot 3.4.2 版本来进行开发,没有前端页面。没有数据库,只提供一个对话接口。
内部整合了 通义千问。需要在阿里的申请页面进行申请,有免费的token。这个是申请页面:https://help.aliyun.com/zh/model-studio/get-api-key
1.3 完整代码
1.3.1 spring-mcp-demo的pom文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.mcp</groupId><artifactId>spring-mcp-demo</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><name>spring-mcp-demo</name><url>http://maven.apache.org</url><modules><module>sse-server</module></modules><properties><java.version>21</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository><repository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><releases><enabled>false</enabled></releases></repository><repository><id>maven2</id><name>maven2</name><url>https://repo1.maven.org/maven2/</url><snapshots><enabled>false</enabled></snapshots></repository><repository><name>Central Portal Snapshots</name><id>central-portal-snapshots</id><url>https://central.sonatype.com/repository/maven-snapshots/</url><releases><enabled>false</enabled></releases><snapshots><enabled>true</enabled></snapshots></repository></repositories><dependencyManagement><dependencies><!-- MCP 服务器支持 - WebMVC版本 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId><version>1.0.0-M6</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>3.4.2</version></dependency><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter</artifactId><version>1.0.0-M6.1</version></dependency></dependencies></dependencyManagement>
</project>
这里需要额外注意一点,如果下载包有问题,需要在 maven 的 setting 配置文件中调整镜像配置:
<mirror> <id>alimaven</id> <name>aliyun maven</name> <url>https://maven.aliyun.com/repository/public</url> <!-- 表示除了spring-milestones、maven2其它都走阿里云镜像 --> <mirrorOf>*,!spring-milestones,!maven2</mirrorOf>
</mirror>
1.3.2 sse-server 的pom文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.mcp</groupId><artifactId>spring-mcp-demo</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>sse-server</artifactId><packaging>jar</packaging><name>sse-server</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.34</version><optional>true</optional></dependency><!-- mcp-mvc的starter--><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId></dependency><!-- 阿里ai的starter--><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>3.4.2</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><inherited>true</inherited><configuration><source>${java.version}</source><target>${java.version}</target><parameters>true</parameters><showWarnings>true</showWarnings></configuration></plugin></plugins></build>
</project>
1.3.3 ChatRequest
package org.mcp.beans;import lombok.Data;/*** 聊天请求体*/
@Data
public class ChatRequest {/*** 用户消息内容,不能为空*/private String message;
}
1.3.4 ChatResponse
package org.mcp.beans;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 聊天响应*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ChatResponse {/*** 响应内容*/private String content;
}
1.3.5 ChatClientConfig
package org.mcp.config;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 聊天对话配置*/
@Configuration
public class ChatClientConfig {@Autowiredprivate ToolCallbackProvider toolCallbackProvider;/*** 配置ChatClient,注册系统指令和工具函数*/@Beanpublic ChatClient chatClient(ChatClient.Builder builder){return builder.defaultSystem("你是一个学生管理助手,可以帮助用户查询学生信息。" +"你可以根据学生名字模糊查询学生,根据性别查学生信息,查询全部学生信息,并做出一些基本的统计"+"回复时,请使用简洁友好的语言,并将学生信息整理为易读的格式。")// 注册工具方法.defaultTools(toolCallbackProvider).build();}
}
1.3.6 ServiceProviderConfig
package org.mcp.config;import org.mcp.service.StudentService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 服务提供者配置*/
@Configuration
public class ServiceProviderConfig {@Autowiredprivate StudentService studentService;@Beanpublic ToolCallbackProvider serverTools() {return MethodToolCallbackProvider.builder()// 可以注册多个服务.toolObjects(studentService).build();}
}
1.3.7 ChatController
package org.mcp.controller;import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import org.mcp.beans.ChatRequest;
import org.mcp.beans.ChatResponse;
import org.springframework.ai.chat.client.ChatClient;import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/api/chat")
public class ChatController {@Resourceprivate ChatClient chatClient;@PostMapping("/chatting")@SneakyThrowspublic ResponseEntity<ChatResponse> chatting(@RequestBody ChatRequest request) {String userMessage = request.getMessage();// 调用聊天String content = chatClient.prompt().user(userMessage).call().content();return ResponseEntity.ok(new ChatResponse(content));}
}
1.3.8 Student
package org.mcp.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {private String name;private int age;private String sex;private String className;private String phone;private String email;private String address;
}
1.3.9 StudentManager
package org.mcp.manager;import org.mcp.entity.Student;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;@Component
public class StudentManager {private static final List<Student> STUDENTS = new ArrayList<>();static {init();}private static void init() {// 初始化学生数据STUDENTS.add(new Student("张三", 18, "男", "1班", "12345678901", "12345678901@qq.com", "北京"));STUDENTS.add(new Student("李四", 19, "女", "2班", "12345678902", "12345678902@qq.com", "上海"));STUDENTS.add(new Student("王五", 20, "男", "3班", "12345678903", "12345678903@qq.com", "广州"));STUDENTS.add(new Student("赵六", 21, "女", "4班", "12345678904", "12345678904@qq.com", "深圳"));STUDENTS.add(new Student("孙七", 22, "男", "5班", "12345678905", "12345678905@qq.com", "杭州"));STUDENTS.add(new Student("钱八", 23, "女", "6班", "12345678906", "12345678906@qq.com", "南京"));STUDENTS.add(new Student("李九", 24, "男", "7班", "12345678907", "12345678907@qq.com", "西安"));}public List<Student> findStudentByName(String name) {List<Student> result = new ArrayList<>();for (Student student : STUDENTS) {if (student.getName().contains(name)) {result.add(student);}}return result;}public List<Student> findStudentBySex(String sex) {List<Student> result = new ArrayList<>();for (Student student : STUDENTS) {if (student.getSex().equals(sex)) {result.add(student);}}return result;}public List<Student> findAllStudents() {return STUDENTS;}public void createStudent(Student student) {STUDENTS.add(student);}
}
1.3.10 StudentService
提供基本的查询功能。
package org.mcp.service;import org.mcp.entity.Student;import java.util.List;public interface StudentService {List<Student> findStudentByName(String name);List<Student> findStudentBySex(String sex);List<Student> findAllStudents();void createStudent(Student student);
}
1.3.11 StudentServiceImpl
服务实现层,实现基本的查询功能。
使用spring-ai的注解 @Tool 将方法标记为 可供大模型调用的工具。
@ToolParam 注解,可对方法参数进行描述,便于模型正确传递参数。
- name:定义该工具在模型侧使用的名称。
- description:提供工具功能的简要说明,帮助模型理解何时以及如何使用它。
package org.mcp.service.impl;import jakarta.annotation.Resource;
import org.mcp.entity.Student;
import org.mcp.manager.StudentManager;
import org.mcp.service.StudentService;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class StudentServiceImpl implements StudentService {@Resourceprivate StudentManager studentManager;@Override@Tool(name = "findStudentByName", description = "根据姓名查询学生信息,支持模糊查询")public List<Student> findStudentByName(@ToolParam(description = "学生名字关键字,或学生全名") String name) {return studentManager.findStudentByName(name);}@Override@Tool(name = "findStudentBySex", description = "根据性别查学生信息,性别只有男、女")public List<Student> findStudentBySex(String sex) {return studentManager.findStudentBySex(sex);}@Override@Tool(name = "findAllStudents", description = "查找当前的全部学生信息,不过滤任何条件")public List<Student> findAllStudents() {return studentManager.findAllStudents();}@Override@Tool(name = "createStudent", description = "创建学生信息")public void createStudent(@ToolParam(description = "创建学生信息") Student student) {studentManager.createStudent(student);}}
1.3.12 SseServerApplication
package org.mcp;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class SseServerApplication {public static void main(String[] args) {SpringApplication.run(SseServerApplication.class, args);}
}
1.3.13 application,yml
server:port: 8081
spring:application:name: mcp-serverai:mcp:server:enabled: truetype: SYNC # 同步name: mcp-server # MCP服务器名称version: 0.0.1 # 服务器版本号dashscope:api-key: sk-xxxxx你自己在阿里那边申请的keychat:options:model: qwen-plus
1.4 调用聊天接口
1.4.1 请求1
POST http://localhost:8081/api/chat/chatting
Content-Type: application/json{"message" : "你好,在全部的学生中,找一下年龄在22岁之上的学生信息,并简单整理其信息,给我一个json数据"
}
响应结果为:
{"content": "根据你的要求,我整理了年龄在22岁及以上的学生信息。以下是符合条件的学生的JSON数据:\n\n```json\n[\n {\"name\":\"钱八\",\"age\":23,\"sex\":\"女\",\"className\":\"6班\",\"phone\":\"12345678906\",\"email\":\"12345678906@qq.com\",\"address\":\"南京\"},\n {\"name\":\"李九\",\"age\":24,\"sex\":\"男\",\"className\":\"7班\",\"phone\":\"12345678907\",\"email\":\"12345678907@qq.com\",\"address\":\"西安\"}\n]\n```\n\n以上学生信息展示了他们的姓名、年龄、性别、班级、电话、邮箱和地址。如果你需要进一步的信息或者其他帮助,请随时告诉我!"
}
1.4.2 请求2
POST http://localhost:8081/api/chat/chatting
Content-Type: application/json{"message" : "你好,我找名字是张的学生"
}
响应结果为:
{"content": "我找到了一位名字包含\"张\"的学生,信息如下:\n\n- 姓名:张三\n- 年龄:18岁\n- 性别:男\n- 班级:1班\n- 电话:12345678901\n- 邮箱:12345678901@qq.com\n- 地址:北京\n\n如果还有其他需要,请告诉我!"
}
1.4.3 请求3
POST http://localhost:8081/api/chat/chatting
Content-Type: application/json{"message" : "你好,我找来自北上广的学生,并使用表格列出他们的基本信息"
}
响应结果为:
{"content": "根据查询结果,我筛选出来自北京、上海和广州的学生,并以表格形式列出他们的基本信息如下:\n\n| 姓名 | 年龄 | 性别 | 班级 | 电话号码 | 邮箱 | 地址 |\n|------|------|------|------|------------------|----------------------|--------|\n| 张三 | 18 | 男 | 1班 | 12345678901 | 12345678901@qq.com | 北京 |\n| 李四 | 19 | 女 | 2班 | 12345678902 | 12345678902@qq.com | 上海 |\n| 王五 | 20 | 男 | 3班 | 12345678903 | 12345678903@qq.com | 广州 |\n\n如有其他需求,请随时告诉我!"
}
1.4.4 请求4
POST http://localhost:8081/api/chat/chatting
Content-Type: application/json{"message" : "你好,创建一个学生,名字叫 冯宝宝,性别是女,别的属性你随便加,不要为空就行"
}
响应结果为:
{"content": "好的,已经为你创建了学生信息:\n\n姓名:冯宝宝 \n性别:女 \n年龄:18岁 \n班级:高一班 \n地址:北京市朝阳区 \n电话:13812345678 \n邮箱:fengbaobao@example.com"
}
1.4.5 请求5
在创建了学生【冯宝宝】之后,使用了查询功能,可以看到已经创建到“数据库”中了。并筛选出来的数据也是一致的。
POST http://localhost:8081/api/chat/chatting
Content-Type: application/json{"message" : "查一下有没有叫冯宝宝的学生"
}
响应结果:
{"content" : "找到一位叫冯宝宝的学生,信息如下:\n\n姓名:冯宝宝 \n年龄:18岁 \n性别:女 \n班级:高一班 \n电话:13812345678 \n邮箱:fengbaobao@example.com \n地址:北京市朝阳区"
}