场景
Spring AI Advisor 完全指南:拦截器机制与实战全解:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/161018652
上述学习advisor时已基本实现会话记忆功能。
下面进行扩展学习。
大语言模型(LLM)本质上是无状态的------每次调用都是独立的。
如果在第一轮告诉模型"我叫小明",第二轮再问"我叫什么?",模型不会记得之前的对话。
要实现连贯的多轮对话,核心思路是在每次请求时把完整的对话历史一起发送给模型。
Spring AI 通过 ChatMemory 和 Advisor 机制将这一流程深度封装,开发者只需少量配置即可实现记忆、会话隔离甚至持久化。
本文聚焦于 ChatMemory 的核心知识点,基于 Spring AI 1.1.2 版本提供完整的示例代码,
并汇总实践中高频遇到的问题和解决方案。
核心概念
Spring AI 将对话记忆的管理拆分为三个层次,各司其职:
组件 角色 职责
ChatMemory 记忆策略层 决定保留哪些消息、何时裁剪(如只保留最近 N 条)
ChatMemoryRepository 存储层 纯粹负责消息的增删查改(内存、JDBC、Redis 等)
MessageChatMemoryAdvisor 拦截器层 自动在每次请求中注入对话历史,并在响应后保存新消息
MessageWindowChatMemory:滑动窗口记忆
MessageWindowChatMemory 是 Spring AI 目前推荐的核心记忆实现。
它维护一个固定大小的消息窗口(默认 20 条),超过上限时自动移除旧消息,但会保留 系统消息 (SystemMessage)。
这种设计使你能精确控制每次发送给模型的上下文长度,避免 Token 爆炸。
两种 Advisor 注册方式
方式 代码 适用场景
构建时注册(全局) ChatClient.builder().defaultAdvisors(...) 对所有请求生效
请求时指定(按需) prompt().advisors(a -> a.param(...)) 传递运行时参数(如 conversationId)
因为记忆需要根据会话 ID 动态加载不同用户的历史,所以 MessageChatMemoryAdvisor 必须采用请求时传参的方式。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
实现
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version> <!-- 降级为稳定版,解决冲突 -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-ai-ollama-demo</artifactId>
<version>1.0</version>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.1.2</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI Ollama 核心 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
<version>${spring-ai.version}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-milestones</id>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
application.yml
server:
port: 886
spring:
ai:
ollama:
base-url: http://localhost:11434
chat:
model: qwen2.5:7b-instruct
options:
temperature: 0.7
num-ctx: 4096 # Ollama 上下文窗口大小
logging:
level:
org.springframework.ai.chat.client.advisor: DEBUG # 观察记忆注入日志
MemoryConfig ------ 创建 ChatMemory
package com.badao.ai.config;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MemoryConfig {
@Bean
public ChatMemory chatMemory() {
// 1. 创建底层存储仓库(内存实现)
InMemoryChatMemoryRepository repository = new InMemoryChatMemoryRepository();
// 2. 用滑动窗口策略包装,限制每次最多注入 10 条最近消息
return MessageWindowChatMemory.builder()
.chatMemoryRepository(repository)
.maxMessages(10)
.build();
}
}
ChatConfig ------ 注册记忆 Advisor
package com.badao.ai.config;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ChatConfig {
@Bean
public ChatClient chatClient(ChatModel chatModel, ChatMemory chatMemory) {
return ChatClient.builder(chatModel)
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build()
)
.build();
}
}
控制器 ------ 多轮对话接口
package com.badao.ai.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory; // 导入 ChatMemory 接口
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class MemoryChatController {
private final ChatClient chatClient;
public MemoryChatController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@PostMapping("/chat/memory")
public ChatResponse chatWithMemory(@RequestBody MemoryChatRequest request) {
String result = chatClient.prompt()
.user(request.message())
.advisors(advisor -> advisor.param(
ChatMemory.CONVERSATION_ID,
request.conversationId()
))
.call()
.content();
return new ChatResponse(200, "success", result);
}
public record MemoryChatRequest(String message, String conversationId) {}
public record ChatResponse(int code, String msg, String data) {}
}
测试验证
测试会话记忆,传递001,告诉名字,再询问名字

传递002询问名字
