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、数据库等多种存储方式

相关笔记:

  1. LangChain4j 入门指南
  2. LangChain4j 声明式 AI 服务开发
Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐