Skip to content
章节导航

SpringAI Alibaba 集成 Redis 让 AI 记住上下文

pom 依赖

xml
<dependencies>


    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-memory-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>5.2.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>


</dependencies>

添加配置文件

yaml
spring:
  # DashScope 配置
  ai:

    # Redis 配置(用于内存管理)
    memory:
      redis:
        host: localhost
        port: 6379
        password: ""
        timeout: 5000

添加配置类

java
import com.alibaba.cloud.ai.memory.redis.RedisChatMemoryRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Redis 记忆配置类 - 用于 Spring AI Alibaba 的会话记忆功能
 *
 * @author 朔风
 * @date 2026-03-31 17:02
 */
@Configuration
public class RedisMemoryConfig {

    @Value("${spring.ai.memory.redis.host:localhost}")
    private String redisHost;

    @Value("${spring.ai.memory.redis.port:6379}")
    private int redisPort;

    @Value("${spring.ai.memory.redis.password:}")
    private String redisPassword;

    @Value("${spring.ai.memory.redis.timeout:5000}")
    private int redisTimeout;

    /**
     * Redis 聊天记忆仓库配置
     */
    @Bean
    public RedisChatMemoryRepository redisChatMemoryRepository() {
        return RedisChatMemoryRepository.builder()
                .host(redisHost)
                .port(redisPort)
//                .password(redisPassword)
                .timeout(redisTimeout)
                .build();
    }


}

修改 ChatConfig.java 配置类

java
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import com.alibaba.cloud.ai.memory.redis.RedisChatMemoryRepository;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

/**
 * ChatClient 配置类
 *
 * @author 朔风
 * @date 2026-03-31 15:53
 */
@Configuration
public class ChatConfig {

    private static final String DEFAULT_PROMPT = """
            你是一个专业、耐心且富有启发性的 AI 学习助手,名字叫朔风。
            你的目标是帮助用户深入理解知识点,而不仅仅是提供标准答案。
                    请遵循以下原则进行回复:
                    1. 角色设定:你是用户的导师,用鼓励性的语气交流。
                    2. 循序渐进:如果用户问一个复杂问题,先解释基础概念,再逐步深入。
                    3. 举例说明:尽量使用生活中的例子或代码示例来解释抽象概念。
                    4. 启发思考:在给出答案后,提出一个相关的问题,引导用户进一步思考。
                    5. 结构清晰:使用 Markdown 格式(如标题、列表、加粗)使内容易于阅读。
                    6. 准确严谨:确保提供的信息是最新且准确的,如果遇到不确定的领域,请诚实告知。
            """;


    /**
     * 配置本地 Ollama 的 ChatClient(带日志记录)
     */
    @Bean(name = "ollamaChatClient")
    public ChatClient ollamaChatClient(OllamaChatModel ollamaChatModel) {
        return ChatClient.builder(ollamaChatModel)
                .defaultSystem("你是一个博学的本地大模型,名字叫朔风")
                .defaultAdvisors(new SimpleLoggerAdvisor()) // 自动记录请求和响应日志
                .build();
    }

    /**
     * 配置云端 DashScope 的 ChatClient(带日志记录)
     */
    @Bean(name = "dashscopeChatClient")
    @Primary // 设置为默认ChatClient
    public ChatClient dashscopeChatClient(DashScopeChatModel dashscopeChatModel,
                                          RedisChatMemoryRepository redisChatMemoryRepository) {
        return ChatClient.builder(dashscopeChatModel)
                //覆盖配置文件配置
                .defaultSystem(DEFAULT_PROMPT)
                .defaultOptions(DashScopeChatOptions.builder()
                        //指定模型 优先级高于 配置文件
                        .withModel("qwen-plus")
                        //指定温度 优先级高于 配置文件
                        .withTemperature(0.7)
                        .build())

                .defaultAdvisors(
                        // 添加日志顾问
                        new SimpleLoggerAdvisor(),
                        // 添加聊天记忆顾问
                        MessageChatMemoryAdvisor.builder(
                                        // 指定聊天记忆
                                        MessageWindowChatMemory.builder()
                                                //指定聊天记忆仓库
                                                .chatMemoryRepository(redisChatMemoryRepository)
                                                //指定最大消息数
                                                .maxMessages(50)
                                                .build())
                                .build()
                )

                .build();
    }

}

Redis 会话记忆控制器

java
import com.alibaba.cloud.ai.memory.redis.RedisChatMemoryRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;

import java.util.List;

import static org.springframework.ai.chat.memory.ChatMemory.CONVERSATION_ID;

/**
 * Redis 会话记忆控制器
 *
 * @author 朔风
 * @date 2026-03-31 17:06
 */
@RestController
@RequestMapping("/api/redis-session")
@RequiredArgsConstructor
public class RedisSessionController {

    private final ChatClient dashscopeChatClient;
    private final RedisChatMemoryRepository redisChatMemoryRepository;

    private final RedisTemplate redisTemplate;


    /**
     * 带会话记忆的聊天接口
     */
    @GetMapping(value = "/chat", produces = "text/html;charset=utf-8")
    public Flux<String> chatWithMemory(
            @RequestParam("question") String question,
            @RequestParam(value = "sessionId", defaultValue = "student_session") String sessionId,
            @RequestParam(value = "userId", defaultValue = "default_userId") String userId) {

        // 将 userId 和 sessionId 拼接作为 Redis key
        String redisKey = userId + ":" + sessionId;

        MessageWindowChatMemory messageWindowChatMemory = MessageWindowChatMemory.builder()
                .chatMemoryRepository(redisChatMemoryRepository)
                .maxMessages(Integer.MAX_VALUE)
                .build();
        // 如果是新的会话 将 sessionId 存入 Redis list类型 userId为key sessionId为值
        if (messageWindowChatMemory.get(redisKey).size() < 1) {
            redisTemplate.setKeySerializer(RedisSerializer.string());
            redisTemplate.setValueSerializer(RedisSerializer.json());
            BoundListOperations boundListOperations = redisTemplate.boundListOps("history:" + userId);
            boundListOperations.leftPush(sessionId);
        }

        // 使用已配置的 dashscopeChatClient,Redis 记忆已在配置中启用
        return dashscopeChatClient.prompt(question)
                .advisors(
                        a -> a.param(CONVERSATION_ID, redisKey)
                )
                .stream().content();
    }


    /**
     * 获取指定会话的历史消息
     */
    @GetMapping("/history/{userId}/{sessionId}")
    public List<Message> getSessionHistory(
            @PathVariable String userId,
            @PathVariable String sessionId) {

        String redisKey = userId + ":" + sessionId;

        MessageWindowChatMemory messageWindowChatMemory = MessageWindowChatMemory.builder()
                .chatMemoryRepository(redisChatMemoryRepository)
                .maxMessages(Integer.MAX_VALUE)
                .build();

        return messageWindowChatMemory.get(redisKey);
    }


    /**
     * 清除指定会话的记忆
     */
    @DeleteMapping("/clear/{userId}/{sessionId}")
    public String clearSession(@PathVariable String userId, @PathVariable String sessionId) {

        String redisKey = userId + ":" + sessionId;

        MessageWindowChatMemory messageWindowChatMemory = MessageWindowChatMemory.builder()
                .chatMemoryRepository(redisChatMemoryRepository)
                .maxMessages(Integer.MAX_VALUE)
                .build();

        messageWindowChatMemory.clear(redisKey);

        // 从 Redis 会话列表中删除对应的 sessionId
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(RedisSerializer.json());
        BoundListOperations<String, String> boundListOperations = redisTemplate.boundListOps("history:" + userId);
        boundListOperations.remove(0, sessionId);

        return "用户 " + userId + " 的会话 " + sessionId + " 记忆已清除";
    }

    /**
     * 获取指定用户的所有会话ID
     */
    @GetMapping("/sessions/{userId}")
    public List<String> getUserSessions(@PathVariable String userId) {
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(RedisSerializer.json());
        BoundListOperations<String, String> boundListOperations = redisTemplate.boundListOps("history:"+userId);
        return boundListOperations.range(0, -1);
    }

}

访问接口

带会话记忆的聊天接口

shell
http://localhost:8080/api/redis-session/chat?question=你是谁&userId=1&sessionId=1

获取指定会话的历史消息

shell
http://localhost:8080/api/redis-session/history/1/1

清除指定会话的记忆

shell
http://localhost:8080/api/redis-session/clear/1/1

获取指定用户的所有会话ID

shell
http://localhost:8080/api/redis-session/sessions/1