RedisChatMemoryStore.java 2.36 KB
package com.xly.config;

import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.ChatMessageDeserializer;
import dev.langchain4j.data.message.ChatMessageSerializer;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

/**
 * Redis 持久化的 ChatMemoryStore —— 会话记忆按 conversationId 存到 Redis,重启/换实例不丢,
 * 也让「重登录后接回上一次会话」成立(会话状态按稳定身份+conversationId 存,而非按 token)。
 *
 * <p>键:{@code chat:mem:{conversationId}};值:LangChain4j 序列化后的消息 JSON。30 天 TTL。
 */
@Component
public class RedisChatMemoryStore implements ChatMemoryStore {

    private static final String PREFIX = "chat:mem:";
    private static final Duration TTL = Duration.ofDays(30);

    private final StringRedisTemplate redis;

    public RedisChatMemoryStore(StringRedisTemplate redis) {
        this.redis = redis;
    }

    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        String json = redis.opsForValue().get(PREFIX + memoryId);
        if (json == null || json.isBlank()) {
            return new ArrayList<>();
        }
        return ChatMessageDeserializer.messagesFromJson(json);
    }

    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        redis.opsForValue().set(PREFIX + memoryId, ChatMessageSerializer.messagesToJson(messages), TTL);
    }

    @Override
    public void deleteMessages(Object memoryId) {
        redis.delete(PREFIX + memoryId);
    }

    /** 确定性路径(表单/澄清/提议/确认)不经过 LLM 记忆——用它把该轮补进存储,修补记忆空洞。 */
    public void appendTurn(Object memoryId, String userText, String aiText) {
        List<ChatMessage> full = new ArrayList<>(getMessages(memoryId));
        if (userText != null && !userText.isBlank()) {
            full.add(UserMessage.from(userText));
        }
        if (aiText != null && !aiText.isBlank()) {
            full.add(AiMessage.from(aiText));
        }
        updateMessages(memoryId, full);
    }
}