【LangChain4j学习笔记】LangChain4j 对话记忆与多用户隔离
·
LangChain4j 对话记忆与多用户隔离
学习时间:2026/4/1
技术栈:Spring Boot 4.0.5 + LangChain4j 1.12.2 + 通义千问模型
一、为什么需要对话记忆?
默认情况下,每次对话都是独立的:
用户:你好 → AI:你好
用户:我订了机票 → AI:(不知道上文)请提供更多信息
开启对话记忆后,AI 可以记住上下文:
用户:你好 → AI:你好
用户:我订了机票 → AI:我知道,请问您的预订号是?
二、单用户对话记忆
在 AiServices 中配置 ChatMemory 即可,对话记录存储在JVM内存中:
@Configuration
public class AiConfig {
@Bean
public Assistant assistant(ChatModel qwenChatModel) {
// 创建对话记忆,保留最近 10 条消息
ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
return AiServices.builder(Assistant.class)
.chatModel(qwenChatModel)
.chatMemory(chatMemory)
.build();
}
}
2.1 工作原理
第一轮对话:
用户:你好 → 记忆:[用户:你好]
AI:你好,很高兴为您服务 → 记忆:[用户:你好, AI:你好]
第二轮对话:
用户:我要退票 → 记忆:[用户:你好, AI:你好, 用户:我要退票]
AI:请问您的订单号是? → 记忆:[用户:你好, AI:你好, 用户:我要退票, AI:请问...]
三、多用户对话隔离
3.1 问题场景
多个用户同时使用 bot,需要各自的独立对话:
- 用户 A:询问航班信息
- 用户 B:取消订单
如果共享记忆,用户 A 和用户 B 的对话会混在一起!
3.2 解决方案:MemoryId
public interface ChatAssistant {
// 通过 @MemoryId 区分不同用户的对话
String chat(@MemoryId int memoryId, @UserMessage String message);
TokenStream streamChat(@MemoryId int memoryId, @UserMessage String message);
}
3.3 ChatMemoryProvider
@Configuration
public class AiConfig {
@Bean
public ChatAssistant chatAssistant(ChatModel qwenChatModel) {
// 为每个 memoryId 创建独立的 ChatMemory
ChatMemoryProvider chatMemoryProvider = memoryId ->
MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(10)
.build();
return AiServices.builder(ChatAssistant.class)
.chatModel(qwenChatModel)
.chatMemoryProvider(chatMemoryProvider)
.build();
}
}
3.4 Controller 实现
@GetMapping("/memory-chat-isolation")
public String chatIsolation(@RequestParam int memoryId,
@RequestParam String message) {
return chatAssistant.chat(memoryId, message);
}
@GetMapping(value = "/memory-streaming-chat-isolation",
produces = "text/stream;charset=UTF-8")
public Flux<String> chatIsolationStreaming(@RequestParam int memoryId,
@RequestParam String message) {
TokenStream tokenStream = chatAssistant.streamChat(memoryId, message);
return Flux.create(sink ->
tokenStream.onPartialResponse(sink::next)
.onCompleteResponse(response -> sink.complete())
.onError(sink::error)
.start());
}
四、记忆持久化
上文实现了记忆存储在内存中,服务重启后会丢失。
4.1 自定义存储接口
实现ChatMemoryStore接口
public class PersistentChatMemoryStore implements ChatMemoryStore {
@Override
public List<ChatMessage> getMessages(Object memoryId) {
// TODO: 从 Redis/数据库加载
return List.of();
}
@Override
public void updateMessages(Object memoryId, List<ChatMessage> messages) {
// TODO: 保存到 Redis/数据库
}
@Override
public void deleteMessages(Object memoryId) {
// TODO: 删除存储
}
}
4.2 使用持久化存储
@Bean
public ChatAssistant chatAssistant(ChatModel qwenChatModel) {
PersistentChatMemoryStore memoryStore = new PersistentChatMemoryStore();
ChatMemoryProvider chatMemoryProvider = memoryId ->
MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(10)
.chatMemoryStore(memoryStore) // 使用持久化存储
.build();
return AiServices.builder(ChatAssistant.class)
.chatModel(qwenChatModel)
.chatMemoryProvider(chatMemoryProvider)
.build();
}
4.3 扩展方向
| 存储方式 | 适用场景 | 特点 |
|---|---|---|
| Redis | 高频访问 | 速度快,支持分布式 |
| MySQL | 长期保存 | 成熟稳定 |
| MongoDB | 灵活结构 | 适合存储消息历史 |
| 混合存储 | 生产环境 | Redis 缓存 + 数据库持久化 |
五、记忆类型对比
| 类型 | 配置方式 | 适用场景 |
|---|---|---|
| 无记忆 | 不配置 | 单次问答 |
| 单用户记忆 | ChatMemory |
个人助手 |
| 多用户隔离 | ChatMemoryProvider |
多用户系统 |
六、最佳实践
6.1 memoryId 设计
// 用户 ID(适合用户体系)
String chat(@MemoryId String userId, @UserMessage String message);
// 会话 ID(适合无用户体系)
String chat(@MemoryId String sessionId, @UserMessage String message);
// 复合 ID
String chat(@MemoryId String key, ...) {
// key = "user:123:session:456"
}
6.2 定期清理记忆
// 删除单个对话
memoryStore.deleteMessages(memoryId);
// 清理过期对话
public void cleanupExpiredMemories() {
// 删除 30 天未活跃的对话
}
七、总结
对话记忆让 AI 能够理解上下文:
- 单用户记忆:通过
ChatMemory实现 - 多用户隔离:通过
@MemoryId+ChatMemoryProvider实现 - 持久化存储:通过
ChatMemoryStore接口实现 - 灵活设计:支持 Redis、数据库等多种存储方式
相关笔记:
更多推荐

所有评论(0)