LangChain4j 对话记忆管理

概述
对话记忆(Chat Memory)是实现上下文对话的关键。LangChain4j 提供了多种记忆管理策略,让 AI 能够记住之前的对话内容,实现连贯的多轮对话。
为什么需要记忆?
无记忆的对话
java
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName(GPT_4_O_MINI)
.build();
String response1 = model.generate("我叫张三");
System.out.println(response1); // 你好,张三!
String response2 = model.generate("我叫什么名字?");
System.out.println(response2); // 对不起,我不知道您的名字。
// ❌ 模型无法记住之前的对话
有记忆的对话
java
interface Assistant {
String chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10)) // 添加记忆
.build();
String response1 = assistant.chat("我叫张三");
System.out.println(response1); // 你好,张三!
String response2 = assistant.chat("我叫什么名字?");
System.out.println(response2); // 你叫张三。
// ✅ 模型记住了之前的对话
记忆类型
1. MessageWindowChatMemory(消息窗口记忆)
按消息数量限制记忆,保留最近的 N 条消息。
代码示例
java
package dev.langchain4j.example;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import static dev.langchain4j.model.openai.OpenAiChatModelName.GPT_4_O_MINI;
public class MessageWindowExample {
interface Assistant {
String chat(String message);
}
public static void main(String[] args) {
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName(GPT_4_O_MINI)
.build();
// 保留最近 10 条消息(5 轮对话)
ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.chatMemory(chatMemory)
.build();
// 对话测试
System.out.println(assistant.chat("我叫张三"));
System.out.println(assistant.chat("我今年 30 岁"));
System.out.println(assistant.chat("我住在北京"));
System.out.println(assistant.chat("总结一下我的信息"));
// 输出: 你叫张三,今年30岁,住在北京。
}
}
特点:
- ✅ 简单直接
- ✅ 易于控制对话长度
- ❌ 可能因消息过长导致超出 Token 限制
2. TokenWindowChatMemory(Token 窗口记忆)
按 Token 数量限制记忆,更精确地控制上下文长度。
代码示例 (参考:tutorials/src/main/java/_05_Memory.java)
java
package dev.langchain4j.example;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.TokenWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiTokenizer;
import static dev.langchain4j.model.openai.OpenAiChatModelName.GPT_4_O_MINI;
public class TokenWindowExample {
public static void main(String[] args) {
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName(GPT_4_O_MINI)
.build();
// 创建 Token 计数器
OpenAiTokenizer tokenizer = new OpenAiTokenizer(GPT_4_O_MINI);
// 限制为 1000 个 tokens
ChatMemory chatMemory = TokenWindowChatMemory.withMaxTokens(1000, tokenizer);
// 添加系统消息
SystemMessage systemMessage = SystemMessage.from(
"你是一位资深的 Java 开发工程师,擅长解决技术问题。"
);
chatMemory.add(systemMessage);
// 添加用户消息
UserMessage userMessage1 = UserMessage.from(
"如何优化 Spring Boot 应用的启动速度?"
);
chatMemory.add(userMessage1);
// 发送到模型
String response1 = model.generate(chatMemory.messages()).content().text();
System.out.println("AI: " + response1);
// 将 AI 响应也添加到记忆
chatMemory.add(AiMessage.from(response1));
// 继续对话
UserMessage userMessage2 = UserMessage.from("具体说说懒加载怎么实现?");
chatMemory.add(userMessage2);
String response2 = model.generate(chatMemory.messages()).content().text();
System.out.println("\nAI: " + response2);
chatMemory.add(AiMessage.from(response2));
// 查看当前记忆中的消息数量
System.out.println("\n当前记忆中的消息数: " + chatMemory.messages().size());
}
}
特点:
- ✅ 精确控制 Token 使用
- ✅ 避免超出模型上下文限制
- ✅ 适合长对话场景
3. 自定义记忆策略
你可以实现自己的记忆策略:
java
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.memory.ChatMemory;
import java.util.ArrayList;
import java.util.List;
public class SummaryMemory implements ChatMemory {
private final List<ChatMessage> messages = new ArrayList<>();
private final ChatLanguageModel summaryModel;
private final int maxMessages;
public SummaryMemory(ChatLanguageModel summaryModel, int maxMessages) {
this.summaryModel = summaryModel;
this.maxMessages = maxMessages;
}
@Override
public void add(ChatMessage message) {
messages.add(message);
// 当消息过多时,总结前面的对话
if (messages.size() > maxMessages) {
String summary = summarizeOldMessages();
messages.clear();
messages.add(SystemMessage.from("之前的对话摘要:" + summary));
messages.add(message);
}
}
@Override
public List<ChatMessage> messages() {
return new ArrayList<>(messages);
}
@Override
public void clear() {
messages.clear();
}
private String summarizeOldMessages() {
// 使用 LLM 总结历史对话
String conversation = messages.stream()
.map(ChatMessage::text)
.collect(Collectors.joining("\n"));
return summaryModel.generate("总结以下对话的要点:\n" + conversation);
}
}
多用户记忆管理
使用 ChatMemoryStore 持久化记忆
java
package dev.langchain4j.example;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;
import static dev.langchain4j.model.openai.OpenAiChatModelName.GPT_4_O_MINI;
public class MultiUserMemoryExample {
interface Assistant {
String chat(@MemoryId int userId, @UserMessage String message);
}
public static void main(String[] args) {
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName(GPT_4_O_MINI)
.build();
// 创建共享的内存存储(可以替换为数据库实现)
ChatMemoryStore store = new InMemoryChatMemoryStore();
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(20)
.chatMemoryStore(store)
.build())
.build();
// 模拟多个用户的对话
System.out.println("=== 用户 1 ===");
System.out.println(assistant.chat(1, "我想买一台笔记本电脑"));
System.out.println(assistant.chat(1, "预算 5000 元左右"));
System.out.println("\n=== 用户 2 ===");
System.out.println(assistant.chat(2, "我想买一部手机"));
System.out.println(assistant.chat(2, "主要用来拍照"));
System.out.println("\n=== 用户 1(继续) ===");
System.out.println(assistant.chat(1, "我之前说预算是多少?"));
// 输出: 你之前说预算是5000元左右。
System.out.println("\n=== 用户 2(继续) ===");
System.out.println(assistant.chat(2, "我之前说主要用来做什么?"));
// 输出: 你之前说主要用来拍照。
}
}
持久化到数据库
LangChain4j 支持多种持久化方案:
java
// 使用 PostgreSQL
import dev.langchain4j.store.memory.chat.postgresql.PostgresChatMemoryStore;
ChatMemoryStore store = PostgresChatMemoryStore.builder()
.dataSource(dataSource)
.tableName("chat_memory")
.build();
// 使用 MongoDB
import dev.langchain4j.store.memory.chat.mongodb.MongoDbChatMemoryStore;
ChatMemoryStore store = MongoDbChatMemoryStore.builder()
.mongoClient(mongoClient)
.databaseName("langchain4j")
.collectionName("chat_memory")
.build();
// 使用 Redis
import dev.langchain4j.store.memory.chat.redis.RedisChatMemoryStore;
ChatMemoryStore store = RedisChatMemoryStore.builder()
.host("localhost")
.port(6379)
.build();
与 AI Service 集成
方式 1: 使用默认记忆
java
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
方式 2: 使用 ChatMemoryProvider
适用于多用户场景:
java
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.chatMemoryProvider(memoryId -> {
// 为每个 memoryId 创建独立的记忆
return MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(10)
.chatMemoryStore(store)
.build();
})
.build();
方式 3: 使用 @MemoryId 注解
java
interface Assistant {
String chat(@MemoryId String sessionId, @UserMessage String message);
}
// 使用不同的 sessionId 隔离对话
assistant.chat("session-001", "你好");
assistant.chat("session-002", "你好");
记忆管理实战示例
客服机器人示例
代码示例 (参考:tutorials/src/main/java/_09_ServiceWithPersistentMemoryForEachUserExample.java)
java
package dev.langchain4j.example;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;
import static dev.langchain4j.model.openai.OpenAiChatModelName.GPT_4_O_MINI;
public class CustomerServiceBot {
interface CustomerSupport {
@SystemMessage({
"你是一位专业的客服代表。",
"你友好、耐心、乐于助人。",
"你会记住用户之前提到的所有信息。"
})
String chat(@MemoryId String customerId, @UserMessage String message);
}
public static void main(String[] args) {
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName(GPT_4_O_MINI)
.build();
CustomerSupport support = AiServices.builder(CustomerSupport.class)
.chatModel(model)
.chatMemoryProvider(customerId -> MessageWindowChatMemory.builder()
.id(customerId)
.maxMessages(100)
.chatMemoryStore(new InMemoryChatMemoryStore())
.build())
.build();
// 客户 A 的对话
String customerA = "CUSTOMER_A";
System.out.println("=== 客户 A ===");
System.out.println(support.chat(customerA, "你好,我订单号是 12345"));
System.out.println(support.chat(customerA, "我想查询物流信息"));
System.out.println(support.chat(customerA, "我的订单号是多少?"));
// 输出: 您的订单号是12345。
// 客户 B 的对话(完全独立)
String customerB = "CUSTOMER_B";
System.out.println("\n=== 客户 B ===");
System.out.println(support.chat(customerB, "我想退货"));
System.out.println(support.chat(customerB, "订单号是 67890"));
System.out.println(support.chat(customerB, "我的订单号是多少?"));
// 输出: 您的订单号是67890。
}
}
技术支持对话示例
java
interface TechnicalSupport {
@SystemMessage({
"你是一位资深的技术支持工程师。",
"你会记录用户的问题和已经尝试的解决方案。",
"在提供新建议前,先回顾之前的对话。"
})
String assist(@MemoryId String ticketId, @UserMessage String message);
}
// 使用
TechnicalSupport support = AiServices.builder(TechnicalSupport.class)
.chatModel(model)
.chatMemoryProvider(ticketId -> TokenWindowChatMemory.builder()
.id(ticketId)
.maxTokens(2000)
.tokenCountEstimator(new OpenAiTokenizer(GPT_4_O_MINI))
.chatMemoryStore(new InMemoryChatMemoryStore())
.build())
.build();
String ticket = "TICKET-001";
support.assist(ticket, "我的应用启动失败");
support.assist(ticket, "错误信息是:OutOfMemoryError");
support.assist(ticket, "我已经设置了 -Xmx2g,还是失败");
support.assist(ticket, "总结一下我遇到的问题和已尝试的方案");
记忆清理与管理
清空记忆
java
ChatMemory memory = MessageWindowChatMemory.withMaxMessages(10);
// 添加消息
memory.add(UserMessage.from("你好"));
memory.add(AiMessage.from("你好!有什么可以帮你的?"));
// 清空记忆
memory.clear();
// 记忆已被清空
System.out.println(memory.messages().size()); // 0
手动管理消息
java
import dev.langchain4j.data.message.ChatMessage;
import java.util.List;
ChatMemory memory = MessageWindowChatMemory.withMaxMessages(10);
// 添加消息
memory.add(UserMessage.from("消息1"));
memory.add(AiMessage.from("回复1"));
// 获取所有消息
List<ChatMessage> messages = memory.messages();
// 手动过滤或处理消息
List<ChatMessage> filteredMessages = messages.stream()
.filter(msg -> !msg.text().contains("敏感词"))
.collect(Collectors.toList());
记忆策略对比
| 策略 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| MessageWindowChatMemory | 简单直接 | 可能超出 Token 限制 | 短对话、简单场景 |
| TokenWindowChatMemory | 精确控制 Token | 需要 Tokenizer | 长对话、成本敏感 |
| 摘要记忆 | 保留完整上下文 | 需要额外 LLM 调用 | 超长对话 |
| 持久化记忆 | 跨会话保留 | 需要外部存储 | 多会话、生产环境 |
最佳实践
1. 根据模型选择记忆大小
java
// GPT-4o-mini: 128k tokens
TokenWindowChatMemory.withMaxTokens(10000, tokenizer); // 保留约 10k tokens
// GPT-3.5-turbo: 16k tokens
TokenWindowChatMemory.withMaxTokens(4000, tokenizer); // 保留约 4k tokens
2. 为系统消息预留空间
java
// 系统消息通常较长,计入 Token 限制
int systemMessageTokens = 500;
int maxContextTokens = 4000;
int availableTokens = maxContextTokens - systemMessageTokens;
ChatMemory memory = TokenWindowChatMemory.withMaxTokens(availableTokens, tokenizer);
3. 定期摘要长对话
java
public class SmartMemoryManager {
private final ChatLanguageModel summaryModel;
private final List<ChatMessage> allMessages = new ArrayList<>();
private String summary = "";
public void addMessage(ChatMessage message) {
allMessages.add(message);
// 每 20 条消息总结一次
if (allMessages.size() % 20 == 0) {
summary = summarizeHistory();
allMessages.clear();
}
}
public List<ChatMessage> getContextMessages() {
List<ChatMessage> context = new ArrayList<>();
if (!summary.isEmpty()) {
context.add(SystemMessage.from("之前的对话摘要:" + summary));
}
context.addAll(allMessages);
return context;
}
private String summarizeHistory() {
String conversation = allMessages.stream()
.map(ChatMessage::text)
.collect(Collectors.joining("\n"));
return summaryModel.generate("用3-5句话总结以下对话:\n" + conversation);
}
}
4. 为敏感信息设置过期策略
java
public class SecureMemory implements ChatMemory {
private final List<MessageWithTimestamp> messages = new ArrayList<>();
private final Duration expirationTime;
public void add(ChatMessage message) {
messages.add(new MessageWithTimestamp(message, Instant.now()));
removeExpiredMessages();
}
public List<ChatMessage> messages() {
removeExpiredMessages();
return messages.stream()
.map(MessageWithTimestamp::getMessage)
.collect(Collectors.toList());
}
private void removeExpiredMessages() {
Instant cutoff = Instant.now().minus(expirationTime);
messages.removeIf(m -> m.getTimestamp().isBefore(cutoff));
}
static class MessageWithTimestamp {
private final ChatMessage message;
private final Instant timestamp;
// constructor, getters...
}
}
常见问题
Q1: 如何知道当前使用了多少 Token?
java
OpenAiTokenizer tokenizer = new OpenAiTokenizer(GPT_4_O_MINI);
List<ChatMessage> messages = memory.messages();
int totalTokens = messages.stream()
.mapToInt(msg -> tokenizer.estimateTokenCountInMessage(msg))
.sum();
System.out.println("当前使用 Token: " + totalTokens);
Q2: 如何在不同会话间共享部分记忆?
java
// 使用共享的系统消息
SystemMessage sharedContext = SystemMessage.from("用户偏好:喜欢简洁的回答");
ChatMemory session1 = MessageWindowChatMemory.withMaxMessages(10);
session1.add(sharedContext);
ChatMemory session2 = MessageWindowChatMemory.withMaxMessages(10);
session2.add(sharedContext);
Q3: 记忆满了会发生什么?
- MessageWindowChatMemory: 删除最旧的消息
- TokenWindowChatMemory: 删除最旧的消息直到满足 Token 限制
下一步学习
- 06-工具与函数调用 - 让 AI 调用外部工具
- 07-RAG检索增强生成 - 接入文档知识库
- 09-智能体工作流 - 构建复杂的 AI 应用
参考资料
- 示例代码:
tutorials/src/main/java/_05_Memory.java - 多用户示例:
tutorials/src/main/java/_09_ServiceWithPersistentMemoryForEachUserExample.java - 官方文档: https://docs.langchain4j.dev/tutorials/chat-memory