Yang's blog Yang's blog
首页
Java
密码学
机器学习
命令手册
关于
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

xiaoyang

编程爱好者
首页
Java
密码学
机器学习
命令手册
关于
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • SpringCloud

    • 微服务架构介绍
    • SpringCloud介绍
    • Spring Cloud:生产者与消费者
    • Spring Cloud Eureka:构建可靠的服务注册与发现
    • Spring Cloud Ribbon:负载均衡
    • Spring Cloud Fegin:服务调用
    • Spring Cloud Hystrix:熔断器
    • Spring Cloud Zuul:统一网关路由
    • Spring Cloud Config:配置中心
  • Java后端框架

    • LangChain4j

      • 介绍
      • 快速开始
      • Chat and Language Models
      • Chat Memory
        • Memory 和 History 的区别
        • 驱逐策略
        • 持久化
        • 对 SystemMessage 的特殊处理
        • 对工具消息的特殊处理
        • 示例
        • 相关教程
      • Model Parameters
      • Response Streaming
      • AI Services
      • Agent
      • Tools (Function Calling)
      • RAG
      • Structured Outputs
      • Classification
      • Embedding (Vector) Stores
      • Image Models
      • Quarkus Integration
      • Spring Boot Integration
      • Kotlin Support
      • Logging
      • Observability
      • Testing and Evaluation
      • Model Context Protocol
  • 八股文

    • 操作系统
    • JVM介绍
    • Java多线程
    • Java集合框架
    • Java反射
    • JavaIO
    • Mybatis介绍
    • Spring介绍
    • SpringBoot介绍
    • Mysql
    • Redis
    • 数据结构
    • 云计算
    • 设计模式
    • 计算机网络
    • 锁核心类AQS
    • Nginx
  • 前端技术

    • 初识Vue3
    • Vue3数据双向绑定
    • Vue3生命周期
    • Vue-Router 组件
    • Pinia 集中式状态存储
  • 中间件

    • RocketMQ
  • 开发知识

    • 请求参数注解
    • 时间复杂度和空间复杂度
    • JSON序列化与反序列化
    • Timestamp vs Datetime
    • Java开发中必备能力单元测试
    • 正向代理和反向代理
    • 什么是VPN
    • 正则表达式
  • Java
  • Java后端框架
  • LangChain4j
xiaoyang
2025-01-14
目录

Chat Memory

# 聊天记忆

手动维护和管理 ChatMessage 非常繁琐。因此,LangChain4j 提供了 ChatMemory 抽象以及多种开箱即用的实现。

ChatMemory 可以作为独立的低级组件使用,也可以作为诸如 AI 服务 等高级组件的一部分。

ChatMemory 作为 ChatMessage 的容器(由 List 支持),并附加了以下功能:

  • 驱逐策略
  • 持久化
  • 对 SystemMessage 的特殊处理
  • 对 工具 消息的特殊处理

# Memory 和 History 的区别

请注意,“Memory”(记忆)和 “History”(历史)是相似但不同的概念。

  • History 保留用户与 AI 之间的所有消息,完整。历史是用户在界面中看到的内容,代表实际的对话内容。
  • Memory 保留部分信息,用于呈现给 LLM,使其表现得像是“记住”了对话内容。
    Memory 与 History 相差甚远,根据使用的记忆算法,它可以以各种方式修改历史:
    驱逐某些消息、总结多条消息、独立总结消息、去除消息中的不重要细节、
    向消息中注入额外信息(如用于 RAG)或指令(如用于结构化输出)等。

LangChain4j 当前仅提供“Memory”,不提供“History”。如果需要保留整个历史记录,请手动完成。

# 驱逐策略

出于多种原因,驱逐策略是必要的:

  • 为了适应 LLM 的上下文窗口。LLM 在一次处理的 token 数量上有上限。当对话超过这个限制时,某些消息需要被驱逐。通常情况下,最旧的消息会被驱逐,但如果需要,也可以实现更复杂的算法。
  • 控制成本。每个 token 都有成本,因此对 LLM 的每次调用会越来越昂贵。驱逐不必要的消息可以降低成本。
  • 控制延迟。发送给 LLM 的 token 越多,处理时间越长。

目前,LangChain4j 提供了两种开箱即用的实现:

  • 简单的实现 MessageWindowChatMemory,它充当滑动窗口,保留最近的 N 条消息并驱逐那些不再适合的旧消息。但由于每条消息可以包含不同数量的 token,MessageWindowChatMemory 主要用于快速原型设计。
  • 更复杂的实现是 TokenWindowChatMemory,它同样作为滑动窗口运行,但着重于保留最近的 N 个 token,
    根据需要驱逐旧消息。消息是不可分割的。如果一条消息无法适配,它会被完全驱逐。
    TokenWindowChatMemory 需要一个 Tokenizer 来计算每个 ChatMessage 的 token 数量。

# 持久化

默认情况下,ChatMemory 实现会将 ChatMessage 存储在内存中。如果需要持久化,可以实现一个自定义的 ChatMemoryStore, 将 ChatMessage 存储到任何您选择的持久化存储中:

class PersistentChatMemoryStore implements ChatMemoryStore {

        @Override
        public List<ChatMessage> getMessages(Object memoryId) {
          // TODO: 实现通过内存 ID 从持久化存储中获取所有消息。
          // 可使用 ChatMessageDeserializer.messageFromJson(String) 和
          // ChatMessageDeserializer.messagesFromJson(String) 辅助方法轻松反序列化 JSON 中的聊天消息。
        }

        @Override
        public void updateMessages(Object memoryId, List<ChatMessage> messages) {
            // TODO: 实现通过内存 ID 更新持久化存储中的所有消息。
            // 可使用 ChatMessageSerializer.messageToJson(ChatMessage) 和
            // ChatMessageSerializer.messagesToJson(List<ChatMessage>) 辅助方法轻松将聊天消息序列化为 JSON。
        }

        @Override
        public void deleteMessages(Object memoryId) {
          // TODO: 实现通过内存 ID 删除持久化存储中的所有消息。
        }
    }

ChatMemory chatMemory = MessageWindowChatMemory.builder()
        .id("12345")
        .maxMessages(10)
        .chatMemoryStore(new PersistentChatMemoryStore())
        .build();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

每当新的 ChatMessage 被添加到 ChatMemory 中时,都会调用 updateMessages() 方法。在与 LLM 的每次交互期间,这通常发生两次:第一次是添加新的 UserMessage 时,第二次是添加新的 AiMessage 时。
updateMessages() 方法需要更新与给定内存 ID 相关联的所有消息。ChatMessage 可以单独存储(如每条消息对应一个记录/行/对象),也可以一起存储(如整个 ChatMemory 对应一个记录/行/对象)。

请注意,从 ChatMemory 中驱逐的消息也会从 ChatMemoryStore 中驱逐。当一条消息被驱逐时,会调用 updateMessages() 方法,其参数列表中不包括被驱逐的消息。

每当 ChatMemory 的用户请求所有消息时,都会调用 getMessages() 方法。这通常发生在与 LLM 的每次交互期间。Object memoryId 参数的值对应于创建 ChatMemory 时指定的 id, 可用于区分多个用户和/或对话。getMessages() 方法需要返回与给定内存 ID 相关联的所有消息。每当调用 ChatMemory.clear() 时,都会调用 deleteMessages() 方法。如果不使用此功能,可以将该方法留空。

# 对 SystemMessage 的特殊处理

SystemMessage 是一种特殊类型的消息,因此其处理方式不同于其他消息类型:

  • 添加后,SystemMessage 始终保留。
  • 一次只能保存一条 SystemMessage。
  • 如果添加了内容相同的新 SystemMessage,将被忽略。
  • 如果添加了内容不同的新 SystemMessage,将替换之前的。

# 对工具消息的特殊处理

如果包含 ToolExecutionRequest 的 AiMessage 被驱逐,后续孤立的 ToolExecutionResultMessage 也会被自动驱逐,以避免某些 LLM 提供商(如 OpenAI)禁止在请求中发送孤立的 ToolExecutionResultMessage 的问题。

# 示例

  • With AiServices

    • 聊天记忆 (opens new window)
    • 为每个用户分配独立的聊天记忆 (opens new window)
    • 持久化聊天记忆 (opens new window)
    • 为每个用户持久化聊天记忆 (opens new window)
  • With legacy Chains

    • 具有会话链的聊天记忆 (opens new window)
    • 具有会话检索链的聊天记忆 (opens new window)

# 相关教程

  • 使用 LangChain4j ChatMemory 实现生成式 AI 对话 (opens new window)作者:Siva (opens new window)
编辑 (opens new window)
上次更新: 2025/04/01, 01:48:12

← Chat and Language Models Model Parameters→

最近更新
01
操作系统
03-18
02
Nginx
03-17
03
后端服务端主动推送消息的常见方式
03-11
更多文章>
Theme by Vdoing | Copyright © 2023-2025 xiaoyang | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式