10-Memory会话管理深度解析

3 阅读3分钟

掌握如何在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设计,实现自主决策和多工具调用。