聊天记忆
ChatMessage手动维护和管理非常麻烦。因此,LangChain4j 提供了一个ChatMemory抽象以及多个开箱即用的实现。

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

ChatMemory充当ChatMessages 的容器(由 a 支持List),并具有以下附加功能:

驱逐政策
持久性
特殊待遇SystemMessage
工具消息的特殊处理
记忆与
请注意,“记忆”和“历史”是相似但又不同的概念。

历史记录保存了用户和 AI 之间的所有消息。历史记录是用户在 UI 中看到的内容。它代表了实际说过的内容。
记忆会保留一些信息,这些信息会呈现给 LLM,使其表现得好像“记住”了对话一样。记忆与历史截然不同。根据所使用的记忆算法,它可以以各种方式修改历史:删除一些消息、汇总多条消息、汇总单独的消息、从消息中删除不重要的细节、在消息中注入额外信息(例如,用于 RAG)或指令(例如,用于结构化输出)等等。
LangChain4j 目前只提供“记忆”,不提供“历史”,如果需要保留整个历史,请手动操作。

驱逐
驱逐政策是必要的,原因如下:

适合 LLM 的上下文窗口。LLM 一次可以处理的标记数有上限。在某些时候,对话可能会超过此限制。在这种情况下,应逐出某些消息。通常,会逐出最旧的消息,但如果需要,也可以实现更复杂的算法。
控制成本。每个令牌都有成本,使得每次调用 LLM 的成本逐渐增加。驱逐不必要的消息可以降低成本。
控制延迟。发送到 LLM 的 token 越多,处理它们所需的时间就越长。
目前,LangChain4j 提供 2 种开箱即用的实现:

较简单的方法MessageWindowChatMemory充当滑动窗口,保留N最新消息并驱逐不再适合的旧消息。但是,由于每条消息可以包含不同数量的标记,因此 MessageWindowChatMemory主要用于快速原型设计。
一个更复杂的选项是TokenWindowChatMemory,它也可以用作滑动窗口,但重点是保留N最新的标记,并根据需要逐出较旧的消息。消息是不可分割的。如果某条消息不适合,则会被完全逐出。 MessageWindowChatMemory需要 来Tokenizer计算每个 中的标记数ChatMessage。
默认情况下,ChatMemory实现将ChatMessages 存储在内存中。

如果需要持久性,则ChatMemoryStore可以实现自定义以存储ChatMessage在您选择的任何持久存储中:

class PersistentChatMemoryStore implements ChatMemoryStore {

    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
      // TODO: Implement getting all messages from the persistent store by memory ID.
      // ChatMessageDeserializer.messageFromJson(String) and 
      // ChatMessageDeserializer.messagesFromJson(String) helper methods can be used to
      // easily deserialize chat messages from JSON.
    }

    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        // TODO: Implement updating all messages in the persistent store by memory ID.
        // ChatMessageSerializer.messageToJson(ChatMessage) and 
        // ChatMessageSerializer.messagesToJson(List<ChatMessage>) helper methods can be used to
        // easily serialize chat messages into JSON.
    }

    @Override
    public void deleteMessages(Object memoryId) {
      // TODO: Implement deleting all messages in the persistent store by memory ID.
    }
}

ChatMemory chatMemory = MessageWindowChatMemory.builder()
.id(“12345”)
.maxMessages(10)
.chatMemoryStore(new PersistentChatMemoryStore())
.build();

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

笔记
请注意,从 中驱逐的消息ChatMemory也将从 中驱逐ChatMemoryStore。当一条消息被驱逐时,该updateMessages()方法将使用不包括被驱逐消息的消息列表来调用。

getMessages()每当 的用户请求所有消息时,都会调用该方法ChatMemory。这通常在与 LLM 的每次交互中发生一次。参数的值Object memoryId对应于id的创建期间指定的值ChatMemory。它可用于区分多个用户和/或对话。该getMessages()方法应返回与给定内存 ID 相关联的所有消息。

deleteMessages()每当调用时,都会调用该方法ChatMemory.clear()。如果您不使用此功能,则可以将此方法留空。

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

一旦添加,SystemMessage就会始终保留。
SystemMessage每次只能举行一场。
如果SystemMessage添加了新的相同内容,则会被忽略。
如果添加了具有不同内容的新内容SystemMessage,它将取代前一个内容。
工具
如果AiMessage包含ToolExecutionRequests 的 被驱逐,则以下 orphan ToolExecutionResultMessage(s) 也会被自动驱逐,以避免出现某些 LLM 提供商(例如 OpenAI)禁止ToolExecutionResultMessage在请求中发送 orphan(s) 的问题。

作者:Jeebiz  创建时间:2024-08-18 23:51
最后编辑:Jeebiz  更新时间:2024-08-18 23:53