Spring AI Alibaba 对话记忆丢失问题:Redis 缓存过期后如何恢复 AI 上下文

Spring AI Alibaba 对话记忆丢失问题:Redis 缓存过期后如何恢复 AI 上下文

🤔 问题背景

在开发基于 Spring AI Alibaba 的聊天应用时,遇到了一个令人困惑的问题:

当 Redis 缓存过期后,虽然能从数据库重新加载历史对话记录,但 AI 却"失忆"了------它完全不记得之前的对话内容!

用户访问历史对话时能看到完整的聊天记录,但当继续提问时,AI 的回答就像第一次见面一样,完全没有上下文。这是怎么回事呢?

🔍 问题分析

经过代码审查,我发现了问题的根源:系统中存在两套独立的存储机制,它们之间没有同步!

第一套:消息缓存系统

java 复制代码
// 在 AiChatMessageServiceImpl 中
private static final String CHAT_HISTORY_KEY = "chat:history:";

public List<AiChatMessage> getMessagesFromRedis(Long sessionId) {
    String key = CHAT_HISTORY_KEY + sessionId;
    String json = stringRedisTemplate.opsForValue().get(key);
    // ... 用于前端展示的聊天记录
}

作用:缓存聊天记录,供前端页面展示历史消息。

第二套:AI 记忆系统

java 复制代码
// Spring AI 框架内部使用
RedisChatMemoryRepository chatMemoryRepository;
ChatMemory chatMemory;

作用:为 ChatClient 提供对话上下文,让 AI 能记住之前的对话。

问题本质

这两套系统完全独立

复制代码
┌─────────────────────────┐      ┌──────────────────────────┐
│   消息缓存系统           │      │   AI 记忆系统             │
│                         │      │                          │
│  chat:history:{id}      │  ✗   │  Spring AI Memory Store  │
│  (供前端展示)            │      │  (供 AI 推理使用)         │
└─────────────────────────┘      └──────────────────────────┘
         ↑                              ↑
         │                              │
    从数据库恢复                    未恢复!

当 Redis 过期后,我们只恢复了消息缓存系统 ,但 AI 记忆系统仍然是空的,所以 AI 失去了记忆!

💡 解决方案

核心思路:在从数据库加载历史消息时,同时恢复两套系统的数据

方案架构

复制代码
数据库历史消息
    │
    ├──> 恢复到消息缓存系统 (chat:history:{id})
    │
    └──> 恢复到 AI 记忆系统 (ChatMemory)

🛠️ 完整实现

步骤 1:配置 ChatMemory Bean

首先需要将 ChatMemory 暴露为 Spring Bean,以便其他服务可以注入使用。

java 复制代码
@Configuration
public class AiConfig {
    
    /**
     * 配置 ChatMemory Bean,供其他服务注入
     */
    @Bean
    public ChatMemory chatMemory(RedisChatMemoryRepository chatMemoryRepository) {
        return MessageWindowChatMemory.builder()
                .chatMemoryRepository(chatMemoryRepository)
                .build();
    }

    @Bean
    public ChatClient chatClient(ChatClient.Builder builder, ChatMemory chatMemory) {
        return builder
                .defaultSystem(AiPrompts.GENERAL_ASSISTANT)
                .defaultAdvisors(
                    MessageChatMemoryAdvisor.builder(chatMemory).build()
                )
                .build();
    }
}

步骤 2:修改消息服务,添加记忆恢复逻辑

java 复制代码
@Service
@Slf4j
public class AiChatMessageServiceImpl extends ServiceImpl<AiChatMessageMapper, AiChatMessage> 
        implements IAiChatMessageService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    
    @Resource
    private ObjectMapper objectMapper;
    
    // ✅ 注入 ChatMemory
    @Resource
    private ChatMemory chatMemory;

    /**
     * 获取会话消息(核心方法)
     */
    public List<AiChatMessage> getSessionMessages(Long sessionId) {
        // 1. 先尝试从 Redis 获取
        List<AiChatMessage> messages = getMessagesFromRedis(sessionId);

        // 2. 如果 Redis 没有,从数据库加载
        if (messages == null) {
            messages = getMessagesFromDB(sessionId);
            if (!messages.isEmpty()) {
                // 3. 同时恢复两套系统
                cacheToRedis(sessionId, messages);      // 恢复消息缓存
                restoreAiMemory(sessionId, messages);   // 恢复 AI 记忆
            }
        }

        return messages != null ? messages : new ArrayList<>();
    }

    /**
     * 恢复 AI 记忆系统
     */
    private void restoreAiMemory(Long sessionId, List<AiChatMessage> messages) {
        try {
            String conversationId = String.valueOf(sessionId);
            
            // 将数据库消息转换为 Spring AI 的 Message 格式
            List<Message> aiMessages = messages.stream()
                    .map(msg -> {
                        if (msg.getMessageType() == 1) {
                            return new UserMessage(msg.getContent());
                        } else {
                            return new AssistantMessage(msg.getContent());
                        }
                    })
                    .collect(Collectors.toList());

            // 清空旧记忆
            chatMemory.clear(conversationId);
            
            // 添加历史消息到 AI 记忆
            chatMemory.add(conversationId, aiMessages);
            
            log.info("✅ 恢复会话 {} 的AI记忆,共 {} 条消息", sessionId, aiMessages.size());
        } catch (Exception e) {
            log.error("❌ 恢复AI记忆失败 - 会话ID: {}", sessionId, e);
        }
    }

    /**
     * 删除会话时,同时清理两套系统
     */
    public boolean deleteSessionMessages(Long sessionId, Long userId) {
        boolean removed = remove(/* 数据库删除逻辑 */);

        if (removed) {
            // 删除消息缓存
            stringRedisTemplate.delete(CHAT_HISTORY_KEY + sessionId);
            
            // 清空 AI 记忆
            clearAiMemory(sessionId);
        }

        return removed;
    }

    private void clearAiMemory(Long sessionId) {
        try {
            chatMemory.clear(String.valueOf(sessionId));
            log.info("✅ 清空会话 {} 的AI记忆", sessionId);
        } catch (Exception e) {
            log.error("❌ 清空AI记忆失败", e);
        }
    }
}

🎯 关键要点

1. 消息格式转换

数据库的消息需要正确转换为 Spring AI 的格式:

java 复制代码
// 用户消息
new UserMessage(content)

// AI 助手消息
new AssistantMessage(content)

2. 会话 ID 一致性

确保两套系统使用相同的会话标识:

java 复制代码
String conversationId = String.valueOf(sessionId);

3. 异常处理

AI 记忆恢复失败不应影响主流程:

java 复制代码
try {
    restoreAiMemory(sessionId, messages);
} catch (Exception e) {
    log.error("恢复失败,但不影响消息加载", e);
}

🧪 测试验证

测试场景 1:正常对话流程

bash 复制代码
# 发起对话
curl "http://localhost:8080/ai/chatStream?message=我叫张三&sessionId=123"
# AI: 你好张三,很高兴认识你!

# 继续对话
curl "http://localhost:8080/ai/chatStream?message=我叫什么名字&sessionId=123"
# AI: 你叫张三

测试场景 2:Redis 过期后恢复

bash 复制代码
# 1. 模拟 Redis 过期
redis-cli DEL "chat:history:123"

# 2. 获取历史消息(触发恢复)
curl "http://localhost:8080/ai/messages/123"

# 3. 继续对话,AI 应该记得之前的内容
curl "http://localhost:8080/ai/chatStream?message=我叫什么名字&sessionId=123"
# AI: 你叫张三(记忆恢复成功!)

📊 效果对比

修复前

复制代码
用户: 我叫张三
AI: 你好张三!

[Redis 过期]

用户: 我叫什么名字?
AI: 抱歉,我不知道你的名字 ❌

修复后

复制代码
用户: 我叫张三
AI: 你好张三!

[Redis 过期 + 自动恢复]

用户: 我叫什么名字?
AI: 你叫张三 ✅

🎓 经验总结

1. 理解框架的存储机制

Spring AI 有自己的记忆管理系统,不能简单地认为"消息在数据库就够了"。

2. 统一数据同步点

在数据恢复的关键节点(如从数据库加载),同时处理所有相关系统的状态恢复。

3. 保持状态一致性

java 复制代码
// 保存时
saveToDatabase();
syncToRedis();
syncToAiMemory();

// 删除时
deleteFromDatabase();
deleteFromRedis();
deleteFromAiMemory();

4. 优雅降级

即使 AI 记忆恢复失败,也不影响用户查看历史消息的基本功能。


如果觉得有帮助,欢迎点赞收藏!有问题欢迎评论区交流~

相关推荐
北京耐用通信19 小时前
耐达讯自动化CANopen转Profibus 网关:实现光伏逆变器无缝接入工业以太网的技术解析
网络·人工智能·物联网·网络协议·自动化·信息与通信
GeeLark19 小时前
GeeLark 12月功能更新合集
人工智能·智能手机·自动化
设计是门艺术19 小时前
AI 生成 PPT 工具大全,智能排版 + 互动效果拉满
人工智能
移远通信19 小时前
移远5G-A王炸模组上线!AI+Wi-Fi 8+卫星通信,三重Buff叠满
人工智能·5g·移远通信
Aaron_94519 小时前
Memos:开源自托管笔记服务的技术深度解析
人工智能
橘颂TA19 小时前
线程池与线程安全:后端开发的 “性能 + 安全” 双维实践
java·开发语言·安全
人工智能知识库19 小时前
华为HCIA-AI Solution H13-313题库(带详细解析)
人工智能·华为·hcia-ai·h13-313
集芯微电科技有限公司20 小时前
替代HT6310/KP3310离线式AC-DC无感线性稳压器
数据结构·人工智能·单片机·嵌入式硬件·fpga开发
悟道心20 小时前
6. 自然语言处理NLP - 迁移学习
人工智能·transformer
juxieyiyi87820 小时前
CDN与PCDN在边缘计算中的分工
人工智能·边缘计算·cdn·pcdn·平台搭建·互联网项目·pcdn平台搭建双收益