掌握如何在LangChain4J中管理会话状态和对话历史,实现真正的多轮对话
时间:30分钟 | 难度:⭐⭐⭐ | Week 2
官方Example信息
- GitHub链接:MemoryExample.java
- 相关Example:ConversationMemoryExample、ChatHistoryExample
- 所在路径:src/main/java/dev/langchain4j/examples/
- 代码行数:约50-80行
- 难度:中级 ⭐⭐⭐
学习目标
- 理解Memory的核心概念
- 掌握不同的Memory类型
- 学会实现会话管理
- 掌握Memory的持久化
- 实现Memory的清理和优化
- 在生产系统中应用Memory
🚀 快速入门:什么是Memory?
Memory的本质
Memory是LLM应用中保存和检索对话历史的机制。
没有Memory的对话:
用户: 我叫张三
LLM: 你好!很高兴认识你。
用户: 我是谁?
LLM: 我不知道。(没记住刚才的信息)
有Memory的对话:
用户: 我叫张三
LLM: 你好张三!很高兴认识你。
用户: 我是谁?
LLM: 你是张三。(记住了)
为什么需要Memory?
没有Memory的问题:
1. 无法记住用户身份和偏好
2. 无法进行有意义的多轮对话
3. 用户体验差(每次都要重新说一遍)
4. 不适合聊天机器人场景
深度讲解
1️⃣ Memory的四种类型
┌─────────────┐
│ Memory │
└──────┬──────┘
│
┌──┴────┬───────┬──────────┐
│ │ │ │
┌───▼──┐ ┌──▼──┐ ┌─▼──┐ ┌────▼──┐
│Buffer│ │ ...│ │ ...│ │ ... │
└──────┘ └─────┘ └────┘ └───────┘
1. BufferMemory - 保存所有消息(最简单)
2. SummaryMemory - 定期总结历史
3. VectorMemory - 向量相似度检索
4. EntityMemory - 提取关键实体
2️⃣ BufferMemory实现
@Service
public class BufferMemoryService {
private final ChatModel model;
private final List<ChatMessage> conversationHistory = new ArrayList<>();
public BufferMemoryService(ChatModel model) {
this.model = model;
}
/**
* 添加消息到内存
*/
public void addMessage(ChatMessage message) {
conversationHistory.add(message);
}
/**
* 获取对话历史
*/
public List<ChatMessage> getHistory() {
return new ArrayList<>(conversationHistory);
}
/**
* 聊天(带记忆)
*/
public String chat(String userMessage) {
// 1. 添加用户消息到历史
addMessage(UserMessage.from(userMessage));
// 2. 调用LLM,发送完整历史
Response<AiMessage> response = model.chat(new ArrayList<>(conversationHistory));
// 3. 添加AI回复到历史
AiMessage aiMessage = response.content();
addMessage(aiMessage);
return aiMessage.text();
}
/**
* 清空历史
*/
public void clear() {
conversationHistory.clear();
}
/**
* 获取历史token数
*/
public int countTokens() {
return conversationHistory.stream()
.mapToInt(msg -> msg.text().length() / 4) // 粗略估算
.sum();
}
}
3️⃣ SummaryMemory实现
@Service
public class SummaryMemoryService {
@Autowired
private ChatModel model;
@Autowired
private ChatModel summaryModel;
private final List<ChatMessage> messages = new ArrayList<>();
private String summary = "";
private static final int SUMMARY_THRESHOLD = 10; // 10条消息后总结
public String chat(String userMessage) {
// Step 1: 添加用户消息
messages.add(UserMessage.from(userMessage));
// Step 2: 检查是否需要总结
if (messages.size() % SUMMARY_THRESHOLD == 0) {
summary = createSummary();
}
// Step 3: 构建提示(摘要 + 最近消息)
String prompt = buildPromptWithSummary();
// Step 4: 调用LLM
String response = model.chat(prompt);
// Step 5: 添加LLM回复
messages.add(AiMessage.from(response));
return response;
}
private String createSummary() {
StringBuilder history = new StringBuilder();
for (ChatMessage msg : messages) {
history.append(msg.type()).append(": ").append(msg.text()).append("\n");
}
String summaryPrompt = """
总结以下对话的关键点(不超过200字):
%s
""".formatted(history);
return summaryModel.chat(summaryPrompt);
}
private String buildPromptWithSummary() {
StringBuilder prompt = new StringBuilder();
// 添加摘要
if (!summary.isEmpty()) {
prompt.append("对话背景:\n").append(summary).append("\n\n");
}
// 添加最近的5条消息
int startIdx = Math.max(0, messages.size() - 5);
for (int i = startIdx; i < messages.size(); i++) {
ChatMessage msg = messages.get(i);
prompt.append(msg.type()).append(": ").append(msg.text()).append("\n");
}
return prompt.toString();
}
}
4️⃣ Memory的持久化
@Service
public class PersistentMemoryService {
@Autowired
private ChatModel model;
@Autowired
private ConversationRepository repository;
private final String sessionId;
public PersistentMemoryService(String sessionId) {
this.sessionId = sessionId;
}
/**
* 加载历史对话
*/
public void loadHistory() {
List<ConversationMessage> messages = repository.findBySessionId(sessionId);
// 加载到内存
}
/**
* 保存消息
*/
public void saveMessage(ChatMessage message) {
ConversationMessage dbMessage = ConversationMessage.builder()
.sessionId(sessionId)
.role(message.type().toString())
.content(message.text())
.createdAt(LocalDateTime.now())
.build();
repository.save(dbMessage);
}
/**
* 聊天并持久化
*/
public String chat(String userMessage) {
// 1. 保存用户消息
saveMessage(UserMessage.from(userMessage));
// 2. 加载历史
List<ChatMessage> history = loadHistory();
// 3. 调用LLM
String response = model.chat(userMessage);
// 4. 保存AI回复
saveMessage(AiMessage.from(response));
return response;
}
private List<ChatMessage> loadHistory() {
return repository.findBySessionId(sessionId).stream()
.map(msg -> "USER".equals(msg.getRole()) ?
UserMessage.from(msg.getContent()) :
AiMessage.from(msg.getContent()))
.collect(Collectors.toList());
}
}
@Repository
public interface ConversationRepository extends JpaRepository<ConversationMessage, Long> {
List<ConversationMessage> findBySessionId(String sessionId);
}
@Entity
@Data
class ConversationMessage {
@Id
@GeneratedValue
private Long id;
private String sessionId;
private String role; // USER或AI
private String content;
private LocalDateTime createdAt;
}
🔧 Memory管理最佳实践
✅ 好的做法
// 定期清理过期会话
@Scheduled(fixedRate = 3600000) // 每小时
public void cleanupExpiredSessions() {
LocalDateTime oneMonthAgo = LocalDateTime.now().minusDays(30);
repository.deleteByCreatedAtBefore(oneMonthAgo);
}
// 限制Memory大小
public String chat(String message) {
if (messages.size() > MAX_HISTORY_SIZE) {
messages.remove(0); // 删除最旧的消息
}
// ...
}
❌ 坏的做法
// ❌ 永远保存所有消息,内存溢出
public String chat(String message) {
messages.add(UserMessage.from(message)); // 无限增长
// ...
}
学习成果检查:
- 能解释什么是Memory ✅ 2026-03-07
- 能实现BufferMemory ✅ 2026-03-07
- 能实现SummaryMemory ✅ 2026-03-07
- 能持久化Memory ✅ 2026-03-07
- 能优化Memory大小 ✅ 2026-03-07
下一步:学习Agent设计,实现自主决策和多工具调用。