一、介绍
接着上文继续实现会话记忆功能
前置后端内容: https://blog.csdn.net/2401_84926677/article/details/151962893
前置前端内容:https://blog.csdn.net/2401_84926677/article/details/152051570
Langchain4j 的会话记忆功能核心作用是为 AI 对话提供 "上下文感知能力",解决大语言模型(LLM)默认 "无状态" 的问题 ------ 即让 AI 能记住多轮交互中的历史消息、用户偏好和对话逻辑,从而实现更连贯、个性化且符合场景的对话体验。
二、步骤
1. 定义会话记忆对象
创建config包下的CommonConfig类
java
@Configuration
public class CommonConfig {
@Bean
public ChatMemory chatMemory() {
MessageWindowChatMemory memory = MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
return memory;
}
}
@Bean表示加入到 IOC 容器里
2. 配置会话记忆对象
在@AiService注解里设置chatMemory,配置会话记忆对象
java
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT,
chatModel = "openAiChatModel",
streamingChatModel = "openAiStreamingChatModel",
chatMemory = "chatMemory" //这里的chatMemory是配置类@Bean注解下的方法名
)
public interface ConsultantService {
// @SystemMessage(fromResource = "system.txt")
@UserMessage("你是一位厨师,{{love}}")
public Flux<String> chat(@V("love")String message);
}
3. 效果测试

后端请求的日志输出,已经携带了之前的会话历史记录:

但是!如果另一个用户进行访问,这个会话记忆是什么样的?
我们打开另一个浏览器访问,并继续提问:


---------------发现它仍然和上一个用户的关联着,没有实现隔离!----------------
三、会话记忆隔离实现
1. 定义会话记忆对象提供者
在config包下的,CommonConfig类中加入ChatMemoryProvider的定义
java
//构建ChatMemoryProvider对象
@Bean
public ChatMemoryProvider chatMemoryProvider() {
ChatMemoryProvider chatMemoryProvider=new ChatMemoryProvider(){
@Override
public ChatMemory get(Object memoryId) {
return MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(20)
.build();
}
};
return chatMemoryProvider;
}
2. 配置会话记忆对象提供者
修改@AiService注解里的内容:加入chatMemoryProvider,去掉chatMemory
java
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT,
chatModel = "openAiChatModel",
streamingChatModel = "openAiStreamingChatModel",
// chatMemory = "chatMemory",
chatMemoryProvider = "chatMemoryProvider"
)
public interface ConsultantService {
@SystemMessage(fromResource = "system.txt")
//@UserMessage("你是一位厨师,{{love}}")
public Flux<String> chat(String message);
}
3. ConsultantService接口方法中添加参数MemoryId
在接口方法的参数加入memoryId,并加上相关注解标明这两个参数
java
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT,
chatModel = "openAiChatModel",
streamingChatModel = "openAiStreamingChatModel",
// chatMemory = "chatMemory",
chatMemoryProvider = "chatMemoryProvider"
)
public interface ConsultantService {
@SystemMessage(fromResource = "system.txt")
//@UserMessage("你是一位厨师,{{love}}")
public Flux<String> chat(@MemoryId String memoryId,@UserMessage String message);
}
4. Controller中chat接口接收memoryId
在控制器方法和返回值中加入memoryId参数
java
@RestController
@RequestMapping("/api")
@CrossOrigin
public class ChatController {
@Autowired
private ConsultantService consultantService;
@GetMapping(value = "/chat",produces = "text/html;charset=utf-8")
public Flux<String> chat(@RequestParam String memoryId, @RequestParam String message) {
return consultantService.chat(memoryId, message);
}
}
5. 前端页面请求时传递memoryId
新增会话按钮,设置点击事件:
html
<button @click="newSession" class="new-session-btn">+</button>
javascript
/**
* 创建新会话
*/
const newSession = () => {
// 生成新的memoryId
memoryId.value = Date.now().toString()
// 清空消息
messages.value = []
}
发送请求时加入memoryId参数:
javascript
// 调用后端API,发送用户消息,包含memoryId参数
const response = await fetch(`http://localhost:8080/api/chat?memoryId=${encodeURIComponent(memoryId.value)}&message=${encodeURIComponent(messageToSend)}`)
6. 效果测试
第一个用户页面:


第二个用户页面:


可见并没有携带第一个用户的会话,效果达成!
但是!如果系统重启,岂不是就都没有了?

重启后接着问:

可见,系统或者服务器重启后,会话记忆历史也就丢失了,那么就需要再次改进。
四、会话记忆持久化
想要实现持久化,那么必须通过外部的存储工具,例如redis,mysql或者其他存储工具
这里使用redis实现会话记忆持久化:
1. redis环境准备
这里使用VMware虚拟机linux的CentOS系统,通过Docker容器化安装部署Redis
bash
sudo docker run -d \
--name redis \
-p 6379:6379 \
-v /usr/local/redis/data:/data \
redis:latest \
redis-server --appendonly yes

然后安装Redis的可视化工具,这里使用Redis Insight创建连接:

2. pom.xml文件引入redis起步依赖
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3. 配置redis连接信息
配置application.yaml文件
javascript
spring:
data:
redis:
host: 192.168.1.128
port: 6379
4. 提供ChatMemoryStore实现类
创建repository包,创建RedisChatMemoryStore类实现ChatMemoryStore接口
java
@Repository
public class RedisChatMemoryStore implements ChatMemoryStore {
//注入redisTemplate
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public List<ChatMessage> getMessages(Object memoryId) {
// 获取会话消息
String json = redisTemplate.opsForValue().get(memoryId);
// 把json数据转成List<ChatMessage>
List<ChatMessage> list = ChatMessageDeserializer.messagesFromJson(json);
return list;
}
@Override
public void updateMessages(Object memoryId, List<ChatMessage> list) {
// 更新会话消息
//1.把list转成json数据
String json = ChatMessageSerializer.messagesToJson(list);
//2.保存到redis中
redisTemplate.opsForValue().set( memoryId.toString(), json, Duration.ofDays(1));
}
@Override
public void deleteMessages(Object memoryId) {
redisTemplate.delete(memoryId.toString());
}
}
5. 配置ChatMemoryStore
配置类配置ChatMemoryStore
java
@Configuration
public class CommonConfig {
@Autowired
private RedisChatMemoryStore redisChatMemoryStore; // 注入RedisChatMemoryStore
@Bean
public ChatMemory chatMemory() {
MessageWindowChatMemory memory = MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
return memory;
}
//构建ChatMemoryProvider对象
@Bean
public ChatMemoryProvider chatMemoryProvider() {
ChatMemoryProvider chatMemoryProvider=new ChatMemoryProvider(){
@Override
public ChatMemory get(Object memoryId) {
return MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(20)
.chatMemoryStore(redisChatMemoryStore) // 设置存储对象
.build();
}
};
return chatMemoryProvider;
}
}
6. 效果测试
重启项目,进行测试


------------会话记忆正常-------------
那么我们重启一下后端再试试:


------------依旧可以!我们来看看后端-------------

下面我们打开redis看一下

会话历史记录已成功存入Redis缓存,实现了会话历史的限时持久化存储
