Chat Memory
手动维护和管理 ChatMessage 很麻烦。因此,LangChain4j 提供了一个 ChatMemory 抽象以及多个开箱即用的实现。
ChatMemory 可以用作独立的低级组件,也可以用作高级组件(如 AI 服务 )的一部分。
ChatMemory 充当 ChatMessage 的容器(由 List 支持),具有其他功能,例如:
- Eviction policy 驱逐策略
- Persistence 坚持
- Special treatment of SystemMessage 系统消息的特殊处理
- Special treatment of tool messages 工具消息的特殊处理
Memory vs History 内存与历史
请注意,“记忆”和“历史”是相似但不同的概念。
- 历史记录使用户和 AI 之间的所有消息保持不变 。历史记录是用户在 UI 中看到的内容。它代表了实际所说的内容。
- Memory 保留了一些信息 ,这些信息被呈现给 ,LLM 使其表现得好像它 “记住” 了对话一样。记忆与历史完全不同。根据所使用的内存算法,它可以以各种方式修改历史记录:驱逐一些消息、汇总多条消息、汇总单独的消息、从消息中删除不重要的细节、将额外信息(例如,用于 RAG)或指令(例如,用于结构化输出)注入消息,等等。
LangChain4j 目前只提供 “memory”,不提供 “history”。如果您需要保留整个历史记录,请手动执行此作。
Eviction policy 驱逐策略
驱逐策略是必要的,原因如下:
- 以适应 LLM 的上下文窗口。一次可以处理的代币LLM数量有上限。在某些时候,对话可能会超过此限制。在这种情况下,应逐出某些消息。通常,最旧的消息会被逐出,但如果需要,可以实现更复杂的算法。
- 为了控制成本。每个令牌都有成本,每次调用都会LLM逐渐增加成本。驱逐不必要的消息可以降低成本。
- 控制延迟。发送到 的LLM令牌越多,处理它们所需的时间就越多。
目前,LangChain4j 提供了 2 种开箱即用的实现:
- 更简单的 MessageWindowChatMemory 充当滑动窗口,保留 N 条最近的消息并驱逐不再适合的旧消息。 但是,由于每条消息可以包含不同数量的令牌, MessageWindowChatMemory 对于快速原型设计非常有用。
- 更复杂的选项是 TokenWindowChatMemory,它也作为滑动窗口运行,但专注于保留 N 个最新的令牌 。 根据需要驱逐较旧的消息。 信息是不可分割的。如果消息不合适,则会将其完全逐出。 TokenWindowChatMemory 需要 Tokenizer 对每个 ChatMessage 中的令牌进行计数。
Persistence 坚持
默认情况下,ChatMemory 实现将 ChatMessage 存储在内存中。
如果需要持久性,则可以实现自定义 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:实现通过内存 ID 删除持久存储中的所有消息。
}
}
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.id("12345")
.maxMessages(10)
.chatMemoryStore(new PersistentChatMemoryStore())
.build();
每次将新的 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() 方法。如果不使用此功能,可以将此方法留空。
Special treatment of SystemMessage 系统消息的特殊处理
SystemMessage 是一种特殊类型的消息,因此它的处理方式与其他消息类型不同:
- 添加后,将始终保留 SystemMessage。
- 一次只能保留一个 SystemMessage。
- 如果添加了具有相同内容的新 SystemMessage,则会忽略该 SystemMessage。
- 如果添加了具有不同内容的新 SystemMessage,它将替换前一个 SystemMessage。
Special treatment of tool messages 工具消息的特殊处理
如果包含 ToolExecutionRequest 的 AiMessage 被逐出,则以下孤立的 ToolExecutionResultMessage 也会自动被逐出,以避免某些LLM提供程序(如 OpenAI)出现问题,这些提供程序禁止在请求中发送孤立的 ToolExecutionResultMessage。
Examples 例子
- 使用 AiServices:
- 聊天记忆:https://github.com/langchain4j/langchain4j-examples/blob/main/other-examples/src/main/java/ServiceWithMemoryExample.java
- 为每个用户单独聊天:https://github.com/langchain4j/langchain4j-examples/blob/main/other-examples/src/main/java/ServiceWithMemoryForEachUserExample.java
- 持久聊天记忆:https://github.com/langchain4j/langchain4j-examples/blob/main/other-examples/src/main/java/ServiceWithPersistentMemoryExample.java
- 每个用户的持久聊天内存:https://github.com/langchain4j/langchain4j-examples/blob/main/other-examples/src/main/java/ServiceWithPersistentMemoryForEachUserExample.java
- 使用旧版 Chains (PASS)
&spm=1001.2101.3001.5002&articleId=147201303&d=1&t=3&u=325adaeaf95f41289abba391bd140f4c)
1374

被折叠的 条评论
为什么被折叠?



