第2章:如何基于LangChain4j实现聊天记忆

本章主要介绍基于LangChain4J如何实现聊天记忆的功能,解决大模型无状态下有上下文的聊天

1. 聊天记忆(Chat Memory)的介绍

手动管理和维护ChatMessage(聊天消息)是繁琐的。因此,LangChain4j提供了一个ChatMemory抽象概念,以及多种现成的实现方式。
ChatMemory可以作为独立的低级组件使用,也可以作为高级组件(如AI服务)的一部分。ChatMemory充当ChatMessage的容器(基于List),并具备以下额外功能:

  • 驱逐策略(Eviction policy):决定何时移除旧消息。
  • 持久化(Persistence):将聊天消息存储到持久化存储中。
  • 对SystemMessage的特殊处理:系统消息通常用于定义LLM的行为和风格。
  • 对工具消息的特殊处理:例如,当工具执行请求的消息被移除时,相关的工具执行结果消息也会被自动移除。

2. 记忆与历史的区别

需要注意的是,“记忆”和“历史”是相似但不同的概念:
历史(History):保留用户与AI之间所有消息的完整记录。历史是用户在界面中看到的内容,它代表了实际的对话内容。
记忆(Memory):只保留部分信息,这些信息被呈现给LLM,使其表现得好像“记得”之前的对话。记忆与历史有很大不同。根据使用的记忆算法,它可以以多种方式修改历史:例如移除一些消息、总结多条消息、删除消息中的不重要细节、注入额外信息(如用于RAG)或指令(如用于结构化输出)等。

LangChain4j目前只提供“记忆”功能,而不提供“历史”功能。如果需要保留完整的对话历史,请手动实现。

3. 驱逐策略(Eviction policy)

驱逐策略是必要的,原因包括:

  • 适应LLM的上下文窗口限制:LLM能够处理的token数量是有限的。如果对话内容超出这个限制,就需要移除一些消息。通常会移除最旧的消息,但也可以根据需要实现更复杂的算法。
  • 控制成本:每个token都有成本,因此对话内容越长,调用LLM的费用就越高。移除不必要的消息可以降低成本。
  • 控制延迟:发送给LLM的token越多,处理时间就越长。

LangChain4j目前提供了两种现成的实现:

  • MessageWindowChatMemory:这是一个简单的滑动窗口实现,保留最近的N条消息,并移除不再适合的消息。然而,由于每条消息的token数量可能不同,这种实现主要用于快速原型开发。
  • TokenWindowChatMemory:这是一个更复杂的滑动窗口实现,专注于保留最近的N个token,并根据需要移除旧消息。消息是不可分割的,如果一条消息不适合,它将被完全移除。TokenWindowChatMemory需要一个Tokenizer来计算每条ChatMessage中的token数量。

4. 持久化(Persistence)

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

class PersistentChatMemoryStore implements ChatMemoryStore {
    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        // 实现根据memoryId从持久化存储中获取所有消息。
        // 可以使用ChatMessageDeserializer.messageFromJson(String)和
        // ChatMessageDeserializer.messagesFromJson(String)辅助方法将聊天消息从JSON反序列化。
    }

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

    @Override
    public void deleteMessages(Object memoryId) {
        // 实现根据memoryId删除持久化存储中的所有消息。
    }
}

ChatMemory chatMemory = MessageWindowChatMemory.builder()
        .id("12345")
        .maxMessages(10)
        .chatMemoryStore(new PersistentChatMemoryStore())
        .build();
  • updateMessages():每次向ChatMemory添加新的ChatMessage时都会调用此方法。这通常在每次与LLM交互时发生两次:一次是添加新的UserMessage时,另一次是添加新的AiMessage时。此方法需要更新与给定memory ID关联的所有消息。
  • getMessages():当ChatMemory的使用者请求所有消息时会调用此方法。这通常在每次与LLM交互时发生一次。Object memoryId参数的值对应于创建ChatMemory时指定的id,可用于区分多个用户和/或对话。此方法需要返回与给定memory ID关联的所有消息。
  • deleteMessages():当调用ChatMemory.clear()时会调用此方法。如果你不使用此功能,可以保留此方法为空。

5. 对SystemMessage的特殊处理

SystemMessage是一种特殊类型的消息,因此它与其他消息类型不同:
一旦添加,SystemMessage将始终被保留。
同一时间只能持有一个SystemMessage。
如果添加了一个内容相同的新的SystemMessage,它将被忽略。
如果添加了一个内容不同的新的SystemMessage,它将替换之前的那一个。
如果包含ToolExecutionRequest的AiMessage被移除,那么相关的ToolExecutionResultMessage也会被自动移除,以避免某些LLM提供者(例如OpenAI)禁止在请求中发送孤立的ToolExecutionResultMessage。

6. 示例

文章还提供了一些使用ChatMemory的示例,包括:
使用AiServices时的聊天记忆
每个用户的独立聊天记忆。
持久化的聊天记忆。
每个用户的持久化聊天记忆。
使用LangChain4j的ChatMemory进行生成式AI对话。

使用传统链(With legacy Chains)

1. 带有聊天记忆的对话链(Chat memory with ConversationalChain)

在LangChain4j中,ConversationalChain是一种用于构建对话式应用的链式结构。它允许开发者将多个组件(如语言模型、记忆模块等)组合起来,实现复杂的对话逻辑。当使用ChatMemory与ConversationalChain结合时,可以实现以下功能:

  • 记忆管理:通过ChatMemory,ConversationalChain能够记住之前的对话内容,从而在多轮对话中保持连贯性。
    灵活的对话流程:开发者可以自定义对话流程,例如在对话中插入工具调用、处理用户输入等。
    适应性:ChatMemory可以根据需要选择不同的实现方式(如MessageWindowChatMemory或TokenWindowChatMemory),以适应不同的对话场景和性能需求。
    官方样例:
    带有对话链的聊天记忆

2. 带有聊天记忆的对话检索链(Chat memory with ConversationalRetrievalChain)

ConversationalRetrievalChain是一种更复杂的链式结构,它结合了对话记忆和检索功能,适用于需要从大量数据中检索信息的场景。例如,在问答系统或知识库检索中,ConversationalRetrievalChain可以实现以下功能:
记忆与检索结合:通过ChatMemory,ConversationalRetrievalChain能够记住之前的对话内容,并结合检索功能从知识库中找到与当前对话相关的信息。
动态检索:在对话过程中,根据用户的输入动态检索知识库中的内容,并将检索结果融入对话中。
上下文感知:通过记忆模块,ConversationalRetrievalChain能够理解对话的上下文,并生成更准确、更相关的回答。
官方样例:
带有对话检索链的聊天记忆

总结

这篇文章详细介绍了LangChain4j中的ChatMemory功能,包括如何管理和维护聊天消息、如何实现驱逐策略、如何进行持久化存储,以及对SystemMessage的特殊处理。ChatMemory是构建聊天应用时管理对话状态的重要工具,通过合理使用驱逐策略和持久化功能,可以优化性能、降低成本,并实现更复杂的对话逻辑。

导读说明:
这是LangChain开发智能体的系列文档,欢迎连读

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一起学开源

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值