场景
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中数据已经存储。

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