LangChain4j Java Agent智能体会话记忆实战指南
LangChain4j 的会话记忆机制,专门用来解决大语言模型天生“无状态”的缺陷——模型自身无法记住历史对话,每次交互都如同初次见面。要在多轮对话中维持上下文连贯,必须依赖 ChatMemory 组件进行有效管理。
直接看效果:用之前的代码测试,第一轮输入“我是小锋”,模型记住了;接着问“我是谁?”——模型完全遗忘。这验证了默认情况下模型确实不具备记忆能力。
会话记忆的核心机制
- Memory vs. History:
ChatMemory是 LangChain4j 提供的核心抽象,负责管理送入模型的上文信息。与记录完整对话历史的History不同,ChatMemory 通过内置算法(如驱逐旧消息、摘要生成、信息注入等)实现选择性“记忆”,既能适配模型的上下文窗口限制,又能控制调用成本与响应延迟。
内置的 ChatMemory 实现
LangChain4j 提供两种开箱即用的 ChatMemory 实现,也支持通过实现 ChatMemory 接口自行扩展。
- MessageWindowChatMemory:基于滑动窗口,只保留最近的 N 条消息。逻辑清晰,适合快速原型开发或对话轮次较少的场景。
- TokenWindowChatMemory:同样是滑动窗口,但以 Token 数为限制条件。更精准地控制送入模型的上下文长度(尤其在 Token 计费敏感的场景),超出限制的消息会被彻底丢弃。
会话隔离:@MemoryId 注解
默认情况下 ChatMemory 是所有用户或会话共享的,这容易导致“记忆串扰”——用户 A 的对话内容可能污染用户 B 的上下文。@MemoryId 注解提供了优雅的隔离方案。在 AI Service 接口方法的参数上标注该注解后,LangChain4j 会自动为每个 memoryId 创建并维护独立的 ChatMemory 实例。
先看一个简单会话实例
首先在 AssistantConfig 中定义 ChatMemoryProvider:
@Bean
public ChatMemoryProvider memoryChatMemoryProvider() {
return memoryId -> MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(20)
.build();
}
接着创建 Assistant4Service 接口,并在 AI Service 注解中配置 chatMemoryProvider:
package com.ja va1234.service;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;
import reactor.core.publisher.Flux;
@AiService(wiringMode = AiServiceWiringMode.EXPLICIT,
streamingChatModel = "openAiStreamingChatModel",
chatMemoryProvider = "memoryChatMemoryProvider"
)
public interface Assistant4Service {
Flux chat(@MemoryId String memoryId, @UserMessage String question);
}
最后在 MyChatController 中注入 Assistant4Service 并实现 chat6 端点:
@Autowired
private Assistant4Service assistant4Service;
@RequestMapping(value = "/chat6", produces = "text/html;charset=utf-8")
public Flux chat6(String memoryId, String question) {
return assistant4Service.chat(memoryId, question);
}
测试:先提问“我是小锋”,记得在浏览器地址栏带上 memoryId 参数。
再次提问“我是谁?”——模型已成功记住“我是小锋”。
本质上,第二次请求发送时,系统会将历史提问与回答一并作为提示词传入模型,从而实现上下文连贯。
再学习一个会话持久化实例
实现会话持久化,可以选用数据库或 Redis。企业级开发更倾向于 Redis。
在 Windows 上运行 Redis,推荐使用 Docker 环境。安装 Docker Desktop 后即可操作。
直接执行镜像启动命令:
docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 -v d:/redis-data:/data redis/redis-stack:latest
端口映射说明:6379 映射 redis-stack 服务,8001 映射可视化界面。
第一步:在 pom.xml 中添加 Redis 依赖:
dev.langchain4j
langchain4j-community-redis-spring-boot-starter
1.15.0-beta25
第二步:在 application.yml 中配置 Redis 连接信息:
langchain4j:
community:
redis:
enabled: false # 关闭 Redis 向量库自动配置,仅保留手动管理的会话记忆
redis:
host: localhost
port: 6379
password: # 无密码时留空
ttl-seconds: 3600 # 每个会话 key 过期时间,演示用 1 小时
第三步:新建 Redis 会话配置类 RedisChatMemoryConfig:
package com.ja va1234.config;
import dev.langchain4j.community.store.memory.chat.redis.RedisChatMemoryStore;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "langchain4j.redis")
@EnableConfigurationProperties(RedisChatMemoryConfig.class)
public class RedisChatMemoryConfig {
private String host;
private int port;
private String password;
private long ttlSeconds;
@Bean
public RedisChatMemoryStore redisChatMemoryStore() {
var builder = RedisChatMemoryStore.builder()
.host(host)
.port(port)
.ttl(ttlSeconds);
if (password != null && !password.isBlank()) {
builder.password(password);
}
return builder.build();
}
// getter 和 setter 方法
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
public String getPassword { public void setPassword(String password) { , public getTtlSeconds() { get?t; } ;
}
最后:修改 AssistantConfig,注入 RedisChatMemoryStore:
@Bean
public ChatMemoryProvider memoryChatMemoryProvider(RedisChatMemoryStore redisChatMemoryStore) {
return memoryId -> MessageWindowChatMemory.builder()
.id(memoryId)
.chatMemoryStore(redisChatMemoryStore)
.maxMessages(20)
.build();
}
继续测试:先告诉模型“我是小锋”。
在 Redis 客户端中可以看到会话信息已持久化。
再次向模型提问“我是谁?”——模型正确回答。
刷新 Redis 客户端,会话信息也已同步更新。