一、ChatMemory
由于手动维护和管理ChatMessages很麻烦,LangChain4j提供了ChatMemory抽象以及多个开箱即用的实现。
ChatMemory可以作为独立的低级组件来使用,也可以作为高级组件(AiService)的一部分使用。
ChatMemory作为ChatMessages的容器,它有如下附加功能:
- 逐出策略
- 持久化
- SystemMessage的特殊处理
- 工具信息的特殊处理
二、记忆与历史
记忆与历史是两个不同的概念,我们要做好区分:
历史记录:它保留的是用户与AI之间的所有信息。用户可以在UI中看到的内容,代表了之前用户与AI之间所发生过的对话。
记忆:保存了一些信息,这些信息会呈现给LLM,使其看上去就像记住了之前对话一样,它与历史不同,会根据所使用的内存算法,可以以各种方式修改历史记录、汇总单独的消息、从消息中删除不重要的细节、向消息中注入额外的信息或指令。
LangChain4j当前只提供记忆而不会提供历史,如果我们要保存历史则需要我们自行实现对过对话信息的保存。
三、逐出策略
逐出策略非常重要也是必须的。原因如下:
- LLM的上下文窗口一次可以处理的令牌长度是有限的。在有些情况下,会话超过这个限制则逐出一些消息,而逐出策略就是用来控制哪些消息要逐出。我们最为常用的策略是:最老的消息被逐出
- 控制成本。每个令牌都是有一定的成本的,每次对LLM的调用逐渐会变得更加昂贵,为了降低这个成本可以考虑清除掉不必要的消息
- 加快响应。发送给到LLM的令牌越多,处理它们所需要的时间则会越多
LangChain4j当前提供了两个开箱即用的实现
- MessageWindowChatMemory:作为滑动窗口,保留最新的N条消息,并逐出不再合适的旧消息。
- TokenWindowChatMemory:作为滑动窗口,保留N个最新的令牌,根据需要逐出旧消息。使用这个的话需要有一个Tokenizer来计算每个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 delete