文章标题:SpringAI——运用Java对接大模型的方式
文章内容:
目录
利用Spring AI与Java迅速对接大模型
一、Spring AI概述
二、环境准备
三、项目构建
四、API密钥配置
五、创建配置类(CommonConfiguration.java)
六、编写Controller接口
会话记忆功能
定义会话存储方式
InMemoryChatMemory实现原理剖析
配置会话记忆
何为“配置会话记忆Advisor”?
添加会话ID
设置conversationId的缘由
应用场景 实现多轮对话的“记忆管控”
场景阐释:
Repository:
Repository的实现类:
怎样实现记忆管理?
✳ 实现目标
历史会话与记录查询接口
MessageVO返回前端
利用Spring AI与Java快速对接大模型
伴随AI技术的飞速发展,越来越多开发者渴望将大模型的能力融入自身的Java应用中。Spring AI提供了便捷的工具与框架,使得Java开发人员能够快速且优雅地接入各类主流大模型API,例如OpenAI、DeepSeek、阿里云通义千问等。
一、Spring AI简介
Spring AI是Spring生态里的新成员,专门为AI服务接入而设计,提供统一的API接口以及自动配置功能,极大地简化了开发人员与不同大模型API集成的流程。
二、环境准备
首先,需确保以下环境具备:
-
JDK 17或更高版本
-
Maven或Gradle
-
一个大模型服务API账号

三、项目构建
借助Spring Initializr创建一个Spring Boot项目,添加如下依赖:
-
Spring Web
-
Spring AI

例如,使用Maven配置依赖:

四、API密钥配置
在application.yml
文件中配置大模型API,比如OpenAI:

spring:
ai:
openai:
base-url: https://ark.cn-beijing.volces.com/api
api-key:你的API-key
chat:
options:
model: deepseek-r1-250120
temperature: 0.7
embedding:
options:
model: text-embedding-v3
dimensions: 1024
logging:
level:
org.springframework.ai.chat.client.advisor: debug
com.gege.ai: DEBUG
注意:
Spring AI在请求Chat API时,会默认在指定的base-url
后面自动追加/v1/chat/completions
路径。这意味着base-url
不应包含v3
之后的路径。若包含多余路径,会导致拼接后的路径错误,进而出现404 Not Found
异常。
错误示例:
base-url: https://ark.cn-beijing.volces.com/api/v3
五、创建配置类(CommonConfiguration.java)
@Configuration
public class CommonConfiguration {
// 定义一种记忆存储方式,底层用map保存消息,可考虑持久化存储
// ChatMemory基于conversationId存储,区分不同会话
// 即能根据会话id查询该会话的聊天记录
@Bean
public ChatMemory chatMemory() {
return new InMemoryChatMemory(); // 创建InMemoryChatMemory对象,用于保存聊天记录
}
@Bean
public ChatClient chatClient(OpenAiChatModel openAiChatModel) {
return ChatClient.builder(openAiChatModel)
.defaultSystem("你是一个可爱的助手,你的名字叫做格格,你的任务是帮助用户解决问题")
.defaultAdvisors(
new SimpleLoggerAdvisor(),
// 环绕增强
new MessageChatMemoryAdvisor(chatMemory()) // 将聊天记录保存到内存中
)
.build();
}
}
此处定义了两个关键Bean:
-
ChatMemory
:用于存储聊天记录,当前为内存存储。 -
ChatClient
:配置了系统提示和默认增强器,自动处理对话历史。
六、编写Controller接口
创建REST接口供前端调用:
import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;
@RestController
@RequestMapping("/ai")
@RequiredArgsConstructor
public class ChatController {
private final ChatClient chatClient;
private final ChatHistoryRepository chatHistoryRepository;
@RequestMapping("/chat")
public String chat( String prompt) {
return chatClient.prompt()
.user(prompt)
.call()
.content();
}
// 使用流式传输,需指定页面内容类型,否则会乱码
@RequestMapping(value = "/chatFlux",produces = "text/html;charset=UTF-8")
public Flux<String> chatFlux(String message) {
return chatClient.prompt()
.user(message)
.stream()
.content();
}
}
会话记忆功能
定义会话存储方式

图中给出以下接口定义:
public interface ChatMemory {
void add(String conversationId, List<Message> messages);
List<Message> get(String conversationId, int lastN);
void clear(String conversationId);
}
接口含义:
-
add
:
将指定会话ID的聊天消息列表存储到内存或持久存储中。 -
get
:
根据会话ID和数量lastN
,获取最近的N条聊天消息。 -
clear
:
清空指定会话ID对应的消息列表。
InMemoryChatMemory
实现原理剖析
上图中使用InMemoryChatMemory
类,这是一种基于内存的简易聊天记忆方案。其实现原理大致如下:
public class InMemoryChatMemory implements ChatMemory {
// 内部存储结构:用ConcurrentHashMap管理会话与消息列表映射
private final Map<String, List<Message>> memory = new ConcurrentHashMap<>();
@Override
public void add(String conversationId, List<Message> messages) {
memory.computeIfAbsent(conversationId, key -> new ArrayList<>())
.addAll(messages);
}
@Override
public List<Message> get(String conversationId, int lastN) {
List<Message> messages = memory.getOrDefault(conversationId, Collections.emptyList());
int size = messages.size();
return messages.subList(Math.max(0, size - lastN), size);
}
@Override
public void clear(String conversationId) {
memory.remove(conversationId);
}
}
- 采用
ConcurrentHashMap
实现线程安全的内存存储,适用于并发场景。
ConcurrentHashMap
是支持高并发访问的哈希映射表,允许多个线程同时读写,无线程安全问题。
-
每个会话
conversationId
对应一个List<Message>
,消息按时间顺序存储。 -
获取消息时,返回最新的N条消息,便于后续调用大模型进行上下文处理。
它是ChatMemory
接口的实现类,用于在内存中保存每个会话的聊天消息。
可将其理解为“简易版聊天历史数据库”,但存在于内存中,不持久化到磁盘或数据库
配置会话记忆

何为“配置会话记忆Advisor”?
在Spring AI中,Advisor是一种增强机制(类似AOP),可理解为:
对ChatClient的一次调用进行“前处理”和“后处理”,借此实现对话上下文(历史聊天)的自动管理。
@Bean
public ChatClient chatClient(OllamaChatModel model) {
return ChatClient.builder(model)
.defaultSystem("你是可爱的助手,名字叫小团团") // 系统提示词
.defaultAdvisors(
new SimpleLoggerAdvisor(), // 日志输出
new MessageChatMemoryAdvisor(chatMemory()) // ⭐ 记忆增强器
)
.build();
}
此段中:
-
MessageChatMemoryAdvisor(chatMemory)
会注册一个增强器。 -
它会拦截每次调用
.call()
或.stream()
的前后行为:-
前:取出
chatId
对应的历史消息→添加到prompt中 -
后:把当前响应消息写入对应会话历史中
-
添加会话ID

会话ID(conversationId)如何添加?——由Advisor机制 + 手动设置参数协同完成
1. 给当前对话上下文设置参数
传入名为CHAT_MEMORY_CONVERSATION_ID_KEY
的参数,这是Spring AI内置识别的“会话ID键名”。
相当于告知Advisor:本轮对话属于chatId这个会话。
该key实际为:
public static final String CHAT_MEMORY_CONVERSATION_ID_KEY = "conversationId";
设置conversationId的缘由
conversationId
是“会话编号”,作用是标记和区分不同用户或同一用户的不同对话场景,让每次请求能关联自身上下文(聊天历史),实现“多轮对话记忆”。
应用场景 实现多轮对话的“记忆管控”
场景阐释:
在对话系统中(如客服、智能助理、问答机器人),通常需:
需求 | 示例 |
---|---|
记录用户与AI的每次会话ID | 用户今日问两次问题,分别记录 |
支持“继续对话”功能 | 用户打开旧对话,接着提问 |
查看“历史会话列表” | 展示聊天记录列表 |
多业务类型区分 | 比如“AI助理” vs “售后客服” |
Repository:
若存在不同业务(如chat、助理等)在同一网站,需根据不同业务保存该业务下的会话ID,此时若要完成记忆存储,需实现将不同业务下的会话ID保存在内存中的方法及获取该业务下所有会话ID的方法
public interface ChatHistoryRepository {
/**
* 保存会话记录
* @param type 业务类型
* @param chatId 会话ID
*/
void save(String type, String chatId);
/**
* 获取会话ID列表
* @param type 业务类型
* @return
*/
List<String> getChatIds(String type);
}
Repository的实现类:
// TODO 先使用内存保存会话记录,后续可改造为数据库保存
@Component
public class InMemoryChatHistoryRepository implements ChatHistoryRepository{
private final Map<String,List<String>> chatHistory = new HashMap<>();
@Override
public void save(String type, String chatId) {
// 先判断是否存在,存在则更新,不存在则新增
/*if(!chatHistory.containsKey(type)){
chatHistory.put(type,new ArrayList<>());
}
List<String> chatIds = chatHistory.get(type); // 取到对应的chatIds
*/
List<String> chatIds = chatHistory.computeIfAbsent(type, k -> new ArrayList<>());
if(chatIds.contains(chatId)){ // 若已存在,不处理
return;
}
chatIds.add(chatId);
}
@Override
public List<String> getChatIds(String type) {
// return chatHistory.get(type) == null ? List.of() : chatHistory.get(type);
return chatHistory.getOrDefault(type,List.of());
}
}
① computeIfAbsent
List<String> chatIds = chatHistory.computeIfAbsent(type, k -> new ArrayList<>());
含义:
若
chatHistory
中不存在keytype
,则为其创建新的ArrayList
并返回;
否则直接返回已有List<String>
。
-
想往某个
type
下添加一个chatId
-
若该类型无记录,自动创建新列表
② getOrDefault
return chatHistory.getOrDefault(type, List.of());
含义:
若
chatHistory
中存在keytype
,返回对应值;
否则返回默认空List(不可变)。
怎样实现记忆管理?
基于Spring AI,可借助
ChatMemory + Advisor + ChatHistoryRepository
实现完整记忆管理能力。
✳ 实现目标
-
每轮对话可带会话ID(
conversationId
) -
自动记录该
chatId
-
自动查询旧对话列表(展示历史记录)
-
绑定业务类型(如:客服、AI问答)
@RequestMapping("/chat")
public Flux<String> chat( String prompt,String chatId) {
// 保存会话
chatHistoryRepository.save("chat",chatId);// chat是一种类型
return chatClient.prompt()
.user(prompt)
.advisors(a->a.param(CHAT_MEMORY_CONVERSATION_ID_KEY,chatId))
.stream()
.content();
}
.advisors(a -> a.param("conversationId", chatId))
即告知Spring AI:当前对话属于哪个会话(chatId)
相当于告知Spring AI:“此次对话属于chatId=xxx的上下文,请读取之前消息,并记录此次对话。”
只要设置.advisors(a -> a.param("conversationId", chatId))
,Spring AI会:
-
自动读取该chatId之前的UserMessage + AssistantMessage
-
自动将当前用户输入和模型生成的AssistantMessage保存到该chatId的上下文中
-
下次提问时,能“记住之前内容”
用户发送请求 => /chat?prompt=你好&chatId=user123
1️⃣ 保存会话chatId到ChatHistoryRepository
chatHistoryRepository.save("chat", "user123")
2️⃣ 构造prompt请求 + 设置advisors
ChatClient.prompt().user("你好").advisors(...)
3️⃣ MessageChatMemoryAdvisor做两件事:
- doBefore:从chatMemory取出user123的历史消息,注入prompt
- doAfter:将生成的新回复追加到user123的上下文记录中
4️⃣ ChatClient.stream().content()启动流式大模型响应
5️⃣ 返回Flux<String>流结果给前端,逐字展示
历史会话与记录查询接口
import static java.lang.Integer.MAX_VALUE;
@RequestMapping("/ai/history")
@RequiredArgsConstructor
@RestController
public class ChatHistoryController {
private final ChatHistoryRepository chatHistoryRepository;
private final ChatMemory chatMemory;
// 查询某业务类型下所有会话ID
@GetMapping("/{type}")
public List<String> getChatIds(@PathVariable String type) {
return chatHistoryRepository.getChatIds(type);
}
// 根据会话ID查询对应信息
@GetMapping("/{type}/{chatId}")
public List<MessageVO> getChatHistory(@PathVariable String type, @PathVariable String chatId) {
List<Message> messages = chatMemory.get(chatId, MAX_VALUE);
if (messages == null){
return List.of();
}
return messages.stream().map(MessageVO::new).toList();
}
}
方法 | 功能 | 底层依赖 |
---|---|---|
getChatIds(String type) |
获取某类型下所有历史会话ID | ChatHistoryRepository |
getChatHistory(String type, String chatId) |
获取某次对话完整聊天记录 | |
ChatMemory (记忆上下文) |
MessageVO返回前端
为何返回role?
因前端需根据role区分是谁回答,进行不同渲染
@NoArgsConstructor
@Data
public class MessageVO {
private String role;
private String content;
public MessageVO(Message message) {
switch (message.getMessageType()) {
case USER:
this.role = "user";
break;
case ASSISTANT:
this.role = "assistant";
break;
default:
role=",";
break;
}
this.content = message.getText();
}
}

MessageType有四种,当前场景只需获取其中两种。
实现多轮对话记忆管理的核心是:**使用
conversationId
作为对话唯一标识 + 自动记忆上下文(ChatMemory