【langchain4j篇03】:构建多用户对话系统:ChatMemoryProvider与@MemoryId实战

张开发
2026/4/7 20:26:42 15 分钟阅读

分享文章

【langchain4j篇03】:构建多用户对话系统:ChatMemoryProvider与@MemoryId实战
1. 为什么需要多用户对话系统想象一下你正在开发一个智能客服系统每天要同时处理成千上万用户的咨询。如果所有用户的对话记录都混在一起系统回答用户A的问题时突然插入了用户B的历史对话那会是多么混乱的场景。这就是为什么我们需要会话隔离机制——确保每个用户的对话上下文完全独立互不干扰。在实际项目中我遇到过这样一个案例某电商平台的聊天机器人因为缺乏会话隔离导致用户查询订单时看到了其他用户的隐私信息。这不仅影响用户体验更存在严重的数据安全隐患。而使用LangChain4j的ChatMemoryProvider配合MemoryId注解就能完美解决这类问题。2. 基础环境搭建2.1 必备依赖配置首先确保pom.xml包含这些关键依赖。注意我们使用的是1.1.0-beta7版本这个版本在内存管理方面有显著优化dependencies !-- 核心LangChain4j启动器 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-spring-boot-starter/artifactId version1.1.0-beta7/version /dependency !-- 流式响应支持 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-reactor/artifactId version1.1.0-beta7/version /dependency !-- Spring Web支持 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency /dependencies2.2 配置文件要点在application.yml中建议开启调试日志以便观察内存使用情况logging: level: dev.langchain4j: debug your.package.name: debug3. 实现单会话记忆3.1 ChatMemory基础配置我们先从简单的单会话记忆开始。创建配置类定义ChatMemory BeanConfiguration public class MemoryConfig { Bean public ChatMemory chatMemory() { return MessageWindowChatMemory.builder() .maxMessages(20) // 保留最近20条消息 .build(); } }这里使用MessageWindowChatMemory实现类它像滑动窗口一样只保留最近的N条对话。我在实际测试中发现超过20条消息后响应速度会明显下降这个值需要根据业务场景调整。3.2 集成到AiService在服务接口中通过注解绑定ChatMemoryAiService( chatMemory chatMemory ) public interface ChatService { String chat(String message); }测试时你会发现连续发送我叫张三和我叫什么名字系统能正确回答张三。但这种方式所有用户共享同一记忆空间显然不适合生产环境。4. 实现多会话隔离4.1 ChatMemoryProvider核心机制这才是真正的重头戏。我们需要改造配置类Configuration public class MemoryConfig { Bean public ChatMemoryProvider chatMemoryProvider() { return memoryId - MessageWindowChatMemory.builder() .id(memoryId) // 关键绑定会话ID .maxMessages(20) .chatMemoryStore(new InMemoryChatMemoryStore()) .build(); } }这个lambda表达式可能看起来有点魔法其实它等价于new ChatMemoryProvider() { Override public ChatMemory get(Object memoryId) { return MessageWindowChatMemory.builder() .id(memoryId) .maxMessages(20) .build(); } }4.2 接口层改造服务接口需要做两处关键修改AiService( chatMemoryProvider chatMemoryProvider // 替换原来的chatMemory ) public interface ChatService { String chat(MemoryId String sessionId, UserMessage String message); }注意MemoryId注解的参数会成为会话隔离的关键。我在项目中踩过一个坑这个参数类型可以是任意Object但建议使用String或Long等不可变类型避免因对象修改导致内存泄漏。4.3 控制器示例配套的Spring控制器可能是这样的RestController public class ChatController { Autowired private ChatService chatService; PostMapping(/chat) public String handleChat(RequestParam String sessionId, RequestParam String message) { return chatService.chat(sessionId, message); } }5. 高级实践技巧5.1 自定义记忆存储默认的InMemoryChatMemoryStore只适合开发环境。生产环境建议改用RedisBean public ChatMemoryProvider redisMemoryProvider(RedisTemplateString, Object redisTemplate) { return memoryId - MessageWindowChatMemory.builder() .id(memoryId) .maxMessages(50) .chatMemoryStore(new RedisChatMemoryStore(redisTemplate)) .build(); }需要自行实现RedisChatMemoryStore主要逻辑是利用Redis的Hash结构存储对话记录。5.2 记忆过期策略通过Spring的Scheduled实现定期清理Scheduled(fixedRate 3600000) // 每小时清理一次 public void cleanExpiredMemories() { // 实现记忆过期逻辑 }5.3 性能监控建议添加以下监控指标很有帮助当前活跃会话数平均会话内存占用记忆读取/写入延迟6. 常见问题排查问题1会话隔离失效检查MemoryId注解是否应用正确确认ChatMemoryProvider的get方法确实接收到了不同memoryId问题2内存泄漏确保memoryId不会无限增长比如使用自增ID考虑添加记忆过期机制问题3响应变慢检查maxMessages设置是否过大考虑使用TokenWindowChatMemory替代MessageWindowChatMemory我在实际项目中还遇到过线程安全问题。当多个请求使用相同memoryId时需要确保ChatMemory实现是线程安全的。LangChain4j的默认实现已经处理了这个问题但如果自定义存储后端就需要特别注意。

更多文章