SpringBoot中集成LangChain4j+阿里百炼平台实现AI对话记忆功能、对话隔离、对话持久化到Redis功能

场景

SpringBoot中集成LangChain4j实现集成阿里百炼平台进行AI对话记忆功能和对话隔离功能:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/160221252

在上面基础上实现的会话没有持久化存储功能。

AI对话持久化功能流程

用户请求 → Controller → AI Service(带有 @MemoryId)→ ChatMemory(从 Redis 加载历史)→ DashScope 模型 → 返回答案 → ChatMemory 更新并保存到 Redis。

关键知识点

@MemoryId

标注在方法参数上,用于区分不同用户的会话。框架会根据该 ID 自动从 ChatMemoryStore 中获取对应的对话历史。

ChatMemory

管理单个会话的消息列表,支持滑动窗口(如保留最近 N 条消息)。

ChatMemoryStore

持久化接口,负责将 ChatMemory 的消息列表存储到外部介质(如 Redis、数据库)。我们需要实现它。

MessageWindowChatMemory

基于消息数量的滑动窗口实现,超出 maxMessages 后自动丢弃最早的消息。

ChatMessageSerializer

LangChain4j 官方提供的序列化工具,能将 List<ChatMessage> 转为 JSON 字符串,

并能正确处理多态类型(SystemMessage、UserMessage、AiMessage)。

AiServices

框架的核心,通过动态代理自动为接口生成实现,并织入记忆、工具调用等能力。

环境准备

JDK 17+

Redis(Windows 可用 tporadowski/redis 5.0+,不需要 RedisJSON 模块)

Maven 3.6+

注册 阿里云百炼

获取 API Key(在控制台 → API Key 管理)

开通模型服务(如 qwen-max)

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi

实现

项目依赖配置(pom.xml)

复制代码
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.5</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>spring-langchain4j-bailian</artifactId>
    <version>1.0</version>

    <properties>
        <java.version>17</java.version>
        <langchain4j.version>1.0.0-beta3</langchain4j.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>dev.langchain4j</groupId>
                <artifactId>langchain4j-community-bom</artifactId>
                <version>${langchain4j.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- LangChain4j 核心 Spring Boot Starter -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-spring-boot-starter</artifactId>
             <version>${langchain4j.version}</version>
        </dependency>

        <!-- 阿里百炼 DashScope 集成 Starter -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
        </dependency>

        <!-- 显式添加 DashScope 核心依赖(Starter 有时不会传递) -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-community-dashscope</artifactId>
        </dependency>

        <!-- Spring Data Redis 支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

    </dependencies>

不需要额外添加 jackson 或 langchain4j-jackson,框架内部已包含必要的序列化支持。

配置文件(application.yml)

复制代码
langchain4j:
  community:
    dashscope:
      chat-model:
        api-key: ${DASHSCOPE_API_KEY}  # 使用环境变量存放API Key,更安全
        model-name: qwen-max           # 在阿里百炼模型广场查看并选择合适的模型
        log-requests: true             # 开启请求日志,便于调试
        log-responses: true            # 开启响应日志

spring:
  data:
    redis:
      host: localhost   # Redis 服务器地址
      port: 6379        # Redis 端口
      password: 123456  # 如有密码请配置
      database: 0       # 使用的数据库索引

代码实现

Redis 存储实现(核心)

创建 RedisChatMemoryStore.java,实现 ChatMemoryStore 接口,使用官方序列化工具。

复制代码
package com.badao.ai.config;

import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.ChatMessageDeserializer;
import dev.langchain4j.data.message.ChatMessageSerializer;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class RedisChatMemoryStore implements ChatMemoryStore {

    private static final String KEY_PREFIX = "chat:memory:";
    private static final long TTL_SECONDS = 3600; // 1小时过期

    private final StringRedisTemplate redisTemplate;

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

    private String getKey(Object memoryId) {
        return KEY_PREFIX + memoryId;
    }

    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        String key = getKey(memoryId);
        String json = redisTemplate.opsForValue().get(key);
        if (json == null || json.isBlank()) {
            return new ArrayList<>();
        }
        try {
            // ✅ 使用官方推荐的反序列化方法
            return ChatMessageDeserializer.messagesFromJson(json);
        } catch (Exception e) {
            log.error("反序列化失败,memoryId: {}, json: {}", memoryId, json, e);
            redisTemplate.delete(key);
            return new ArrayList<>();
        }
    }

    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        String key = getKey(memoryId);
        try {
            // ✅ 使用官方推荐的序列化方法
            String json = ChatMessageSerializer.messagesToJson(messages);
            redisTemplate.opsForValue().set(key, json, TTL_SECONDS, TimeUnit.SECONDS);
            log.debug("存储会话成功,memoryId: {}, 消息数: {}", memoryId, messages.size());
        } catch (Exception e) {
            log.error("序列化失败,memoryId: {}", memoryId, e);
        }
    }

    @Override
    public void deleteMessages(Object memoryId) {
        String key = getKey(memoryId);
        redisTemplate.delete(key);
        log.info("删除会话记忆,memoryId: {}", memoryId);
    }
}

ChatMessageSerializer.messagesToJson() 内部使用 Jackson,能正确处理 ChatMessage 的多态类型。

设置 TTL 可以防止 Redis 被历史会话撑爆。

配置 ChatMemoryProvider

创建 AiConfig.java,将 RedisChatMemoryStore 注入到 ChatMemoryProvider 中。

package com.badao.ai.config;

复制代码
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AiConfig {

    @Bean
    public ChatMemoryProvider chatMemoryProvider(RedisChatMemoryStore redisChatMemoryStore) {
        // 每个 memoryId 创建独立的 ChatMemory,并关联 Redis 存储
        return memoryId -> MessageWindowChatMemory.builder()
                .id(memoryId)
                .chatMemoryStore(redisChatMemoryStore)
                .maxMessages(10) // 保留最近 10 条消息
                .build();
    }
}

MessageWindowChatMemory 是内存中的滑动窗口,但它的消息列表会通过 chatMemoryStore 持久化。

每次对话更新时,框架会自动调用 updateMessages 保存。

AI 服务接口

创建 Assistant.java,使用 @AiService 注解,并通过 @MemoryId 标记会话 ID。

复制代码
package com.badao.ai.service;

import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.spring.AiService;

@AiService
public interface Assistant {

    @SystemMessage("你是一位知识渊博的AI助手,请用中文友好地回答用户的问题。")
    String chat(@MemoryId Long memoryId, @UserMessage String userMessage);

}

@AiService 让框架动态生成实现类,并自动注入 ChatMemory。

@MemoryId 参数值决定了使用哪个会话的存储。

@UserMessage 可以省略(框架默认会将最后一个 String 参数作为用户消息),但显式标注更清晰。

Controller

创建 AssistantController.java,从请求头获取用户 ID 作为 memoryId。

复制代码
package com.badao.ai.controller;

import com.badao.ai.service.Assistant;
import org.springframework.web.bind.annotation.*;

@RestController
public class AssistantController {

    private final Assistant assistant;

    public AssistantController(Assistant assistant) {
        this.assistant = assistant;
    }

    @GetMapping("/ai/assistant")
    public String chat(@RequestHeader("X-User-Id") Long userId,
                       @RequestParam(value = "message") String message) {
        // 将用户ID作为 memoryId 传递给AI服务
        return assistant.chat(userId, message);
    }
}

测试验证

启动 Redis

启动 Spring Boot 应用

确保环境变量 DASHSCOPE_API_KEY 已设置,然后运行主类。

发送请求

使用 curl(或 Postman)测试

curl -H "X-User-Id: 2" "http://localhost:885/ai/assistant?message= 我叫霸道的程序猿"

停掉服务,此时查看redis中数据已经存储。

重启项目后,询问我叫什么名字,仍能回答。

相关推荐
givemeacar2 小时前
spring session、spring security和redis整合的简单使用
redis·spring·bootstrap
@不误正业2 小时前
大模型注意力机制源码解析-从MQA到MLA全链路演进与PyTorch实现
人工智能·pytorch·python
come112342 小时前
最新的 gpt 5.4 和 claude 4.7 模型为什么更好用
人工智能·gpt
WYiQIU2 小时前
宇树科技Web前端岗(AI方向),这不算泄题吧......
前端·vue.js·人工智能·笔记·科技·面试·职场和发展
kiku18182 小时前
NoSQL之Redis配置与优化
数据库·redis·非关系型数据库
Li emily2 小时前
外汇api接口实践:实时汇率与历史数据获取
人工智能·python·api·fastapi
甄心爱学习2 小时前
【项目实训】法律文书智能摘要系统3
前端·人工智能
TheRouter2 小时前
AI 不会消灭软件工程,它只会消灭低维的软件工程
人工智能·软件工程
冲浪中台2 小时前
从追逐技术到回归业务本质,吃互联网红利罢了
服务器·前端·人工智能·低代码