Spring Boot 集成 Spring AI:实现大模型对话与MCP Client
上篇文章中(Spring Boot 集成 Spring AI:实现可被大模型调用的 MCP Server),我们构建了一个MCP Server,提供了查询个人信息、查询订单信息、下单等操作。但是,上篇文章我们使用的是Cherry Studio作为客户端来与后端进行交互。实际生产中,让客户去下载Cherry Studio,然后配置MCP Server显然不显示。因此,本篇文章将会教会你如何使用 Sp
项目代码: Github
文章目录
1. 本文概述
上篇文章中(Spring Boot 集成 Spring AI:实现可被大模型调用的 MCP Server),我们构建了一个MCP Server,提供了查询个人信息、查询订单信息、下单等操作。
但是,上篇文章我们使用的是Cherry Studio作为客户端来与后端进行交互。实际生产中,让客户去下载Cherry Studio,然后配置MCP Server显然不显示。
因此,本篇文章将会教会你如何使用 Spring AI 构建一个简单的聊天界面,并支持MCP Client.
通过本文学习,你将会学到如下内容:
- Spring AI 接入DeepSeek
- 如何使用Spring AI构建一个多轮对话(非流式)。
- 多轮对话如何实现MCP Server的调用
- 调用MCP Server时,如何通过传递token,来让MCP Server区分当前用户,以返回正确的信息。
效果展示(本文重点不在前端,因此使用Postman做交互):

2. 项目初始化
2.1 使用Spring Initializr初始化
如果你是新项目可以,使用 Spring Initializr 生成一个项目。

在构建项目时,需要增加3个主要依赖:
- Spring Web: 用于支持HTTP接口
- DeepSeek:用于接入DeepSeek
- Model Context Protocol Client:支持MCP Client.
2.2 增加项目依赖
如果你是现有项目,那么就需要手动增加上节提到的3个依赖。代码如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-deepseek</artifactId>
</dependency>
如果无法正常导入,请配置具体版本。本文使用的是1.1.0
3. 使用Spring AI实现多轮对话
3.1 多轮对话原理简介
大模型是没有记忆的,所谓的多轮对话就是每次调用都将前面所有的对话一并给到大模型。
例如,第一次调用大模型时,输入输出为:
输入:
User: 你好,简单介绍一下你自己
输出:
Assistant: 您好,我是DeepSeek.
若用户继续输入问题,则你需要将之前的聊天记录都作为输入传给大模型。输入输出为:
输入:
User: 你好,简单介绍一下你自己
Assistant: 您好,我是DeepSeek.
User: 重复下我的问题
输出:
Assistant:您的问题是“你好,简单介绍下你自己”
在与大模型交互时,有三个重要的角色:
- User:用户。对应用户输入的信息
- Assistant:大模型助手。其输出的内容就是用户在聊天界面上能看到的内容。
- System:系统。这部分通常是作为应用的全局提示词,不展示给用户。例如:“你是XXX公司的AI客服,公司的简介是XXX。在与用户聊天的过程中,不要透露你是AI”。
3.2 配置DeepSeek API Key
首先,你需要到 DeepSeek Platform 冲些钱,获取自己的API Key。不用太多,10块就能用好久。

当然,你也可以使用其他的大模型,Spring AI基本支持大部分主流大模型。
3.3 配置application.yaml
获取API Key后,需要在application.yaml中配置,配置如下:
spring:
ai:
deepseek:
api-key: "sk-**********1123"
3.4 实现单轮对话
Spring AI中,要实现与大模型对话,需要如下几步操作:
- 获取
ChatClient.Builder实例,Spring已经帮忙初始化了,只需要注入即可。 - 使用
ChatClient.Builder实例构造ChatClient对象。该过程可以设置全局的系统提示词,即给到system角色的内容。 - 给
ChatClient传入对话内容,调用call方法即可获取大模型回复。
示例代码如下:
import org.springframework.ai.chat.client.ChatClient;
@RestController
@RequestMapping(value = "/chat")
public class ChatController {
@Autowired
private ChatClient.Builder builder;
@PostMapping("/ask")
public String ask(@RequestParam("question") String question) {
ChatClient chatClient = builder
.defaultSystem("你是蜗牛公司的一名AI助手,你的名字叫小蜗。你可以帮助用户解答关于AI相关的知识").build();
return chatClient.prompt().user(question).call().content();
}
}
注:这里我是在方法内实例化的
ChatClient对象,但实践中通常需要将其注册为Bean,即使用单例。
使用效果:

3.5 实现多轮对话
实现多轮其实很简单,只需要将单轮对话中的user(...)方法替换为messages(...)方法即可,里面传入历史对话内容。
样例代码:
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
@RestController
@RequestMapping(value = "/chat")
public class ChatController {
@Autowired
private ChatClient.Builder builder;
@PostMapping("/ask2")
public String ask2(@RequestParam("question") String question) {
ChatClient chatClient = builder
.defaultSystem("你是蜗牛公司的一名AI助手,你的名字叫小蜗。你可以帮助用户解答关于AI相关的知识").build();
List<Message> messageList = new ArrayList<>();
messageList.add(new UserMessage("你好,我是小明。你是谁?"));
messageList.add(new AssistantMessage("你好!我是小蜗,蜗牛公司的AI助手。很高兴为你服务!有什么我可以帮你的吗?")); // AI的回答
// ... 其他对话历史
messageList.add(new UserMessage(question)); // 用户的新问题
return chatClient.prompt()
.messages(messageList) // 传入对话历史
.call().content();
}
}
使用样例:

在实际使用中,通常有两种方法来构造List<Message>:
- 前端将所有的历史对话全部一并传过来。
- 将历史对话存入服务端,每次前端只传新的问题。Spring AI提供了Chat Memory 来实习这个功能。
这里我们使用第一种方式进行演示,要求前端传参格式如下:
[
{
"role": "user", // 角色; system, user 或 assistant
"content": "你好,你是谁" // 对话内容
},
...
]
后端接口代码如下:
@PostMapping("/multiTurnChat")
@ResponseBody
public List<Map<String, String>> multiTurnChat(@RequestBody List<Map<String, String>> messages) {
List<Message> messageList = new ArrayList<>();
messages.forEach(itemMap -> {
String role = itemMap.get("role");
String content = itemMap.get("content");
if ("user".equals(role)) {
messageList.add(new UserMessage(content));
} else if ("assistant".equals(role)) {
messageList.add(new AssistantMessage(content));
}
});
ChatClient.CallResponseSpec responseSpec = builder.build()
.prompt()
.messages(messageList)
.call();
// 将大模型生成的内容重新加入到messages中,作为响应
messages.add(Map.of(
"role", "assistant",
"content", responseSpec.content()
));
return messages;
}
使用样例:

4. 配置MCP实现工具调用
Spring AI 实现了MCP Client功能,可以很方便的接入MCP Server,实现工具调用。
4.1 配置MCP Server
首先我们需要在application.yaml中配置好我们的MCP Server。代码如下:
spring:
ai:
mcp:
client:
streamable-http:
connections:
MyMcpServer1: # 随便什么名字
url: http://localhost:8080
endpoint: /api/mcp-endpoint
该MCP Server是我们上一篇中实现的那个。url和endpoint可以在上一篇中的配置中找到。
4.2 多轮对话中使用MCP Tools
在大模型对话中使用MCP Tools非常简单,Spring AI已经帮我们封装的特别好了。只需要2步:
- 通过注入方式获取
SyncMcpToolCallbackProvider provider实例。 - 在调用大模型之间,也就是
.call()之间,加上.toolCallbacks(provider.getToolCallbacks())方法,告诉ChatClient有哪些工具可以用。其中provider.getToolCallbacks()会返回所有可用的MCP Tools.
代码片段如下:
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
@Autowired
private SyncMcpToolCallbackProvider provider;
ChatClient.CallResponseSpec responseSpec = builder.build()
.prompt()
.messages(messageList)
.toolCallbacks(provider.getToolCallbacks()) // 加上这行
.call();
使用效果:

在本次示例中可以看到,第一轮对话系统调用了MCP Server,告知了其可以实现的功能。但是,在第二轮对话中,系统拒绝了用户的请求,这是因为目前为止,我们在调用MCP Server的时候并没有传token,这样MCP Server端是不知道我们当前用户是谁。这要是我们上一篇实现的功能之一。
4.3 MCP Client增加token请求头(Headers)
目前Spring AI的MCP Client实现中并不直接支持动态headers头,但在查阅Issues(#4305)后,官方给出了一种解决方案:构造McpSyncHttpClientRequestCustomizer的bean,在其增加自定义Header。
我的代码如下:
import io.modelcontextprotocol.client.transport.customizer.McpSyncHttpClientRequestCustomizer;
import io.modelcontextprotocol.common.McpTransportContext;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.net.URI;
import java.net.http.HttpRequest;
@Configuration
public class McpConfig {
@Bean
McpSyncHttpClientRequestCustomizer requestCustomizer() {
McpSyncHttpClientRequestCustomizer mcpSyncHttpClientRequestCustomizer = new McpSyncHttpClientRequestCustomizer() {
@Override
public void customize(HttpRequest.Builder builder, String method, URI endpoint, String body, McpTransportContext context) {
if (RequestContextHolder.getRequestAttributes() == null) {
return;
}
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 从HTTP请求的header中获取token
String token = request.getHeader("token");
if (token != null) {
builder.header("token", token);
}
}
};
return mcpSyncHttpClientRequestCustomizer;
}
}
在增加了该配置类后,再次尝试刚刚的样例。这次需要在http的请求头中也需要加入对应的header:

至此,一个简单的MCP Client已经实现完毕。如果有更多想要了解的,可以进一步查阅官方文档。
参考资料
- Spring AI Official Documentation : https://docs.spring.io/spring-ai/reference/index.html
更多推荐


所有评论(0)