聊天记忆官网
记忆和历史
- 历史记录会完整保存用户与人工智能之间的所有消息。历史记录就是用户在用户界面中看到的内容,它代表了实际发生过的所有对话。
- 内存会保留一些信息,这些信息会呈现给 LLM(生命周期管理模型),使其表现得好像“记住”了对话。内存与历史记录截然不同。根据所使用的内存算法,它可以以各种方式修改历史记录:例如,删除一些消息、汇总多条消息、汇总单个消息、移除消息中不重要的细节、向消息中注入额外信息(例如,用于 RAG 算法)或指令(例如,用于结构化输出)等等。
LangChain4j 目前仅提供“内存”管理,不提供“历史记录”管理。如果您需要保留完整的历史记录,请手动操作。
淘汰策略
- 为了适应 LLM 的上下文窗口,LLM 一次可以处理的令牌数量是有上限的。在某些情况下,对话可能会超过这个限制。在这种情况下,应该移除一些消息。通常情况下,会移除最旧的消息,但如有必要,也可以实现更复杂的算法。
- 为了控制成本。每个令牌都有成本,因此每次调用 LLM 的成本都会逐渐增加。清除不必要的消息可以降低成本。
- 为了控制延迟。发送到 LLM 的令牌越多,处理它们所需的时间就越长。
目前,LangChain4j 提供了 2 种开箱即用的实现方式:
- 较简单的版本MessageWindowChatMemory以滑动窗口的形式运行,保留N最新消息并移除不再适合显示的旧消息。然而,由于每条消息可能包含数量不等的标记,因此 MessageWindowChatMemory它主要用于快速原型开发。
- 更复杂的方法是TokenWindowChatMemory使用滑动窗口,它专注于保留N最新的标记,并根据需要移除较旧的消息。消息是不可分割的。如果消息不适合滑动窗口,则会被完全移除。 TokenWindowChatMemory需要使用
TokenCountEstimatorcount 来统计每个窗口中的标记数量ChatMessage。
代码实现
//接口类
public interface ChatMemoryAssistant {
String chatMemory(@MemoryId Long userId, @UserMessage String prompt);
}
//配置类,注入实现ChatMemoryAssistant的方式
@Configuration
public class LLMConfig {
@Bean
public ChatModel chatModel(){
return OpenAiChatModel.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.modelName("qwen-long")
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
}
@Bean("chatMemoryWindow")
public ChatMemoryAssistant chatMemoryByWindow(ChatModel chatModel){
return AiServices.builder(ChatMemoryAssistant.class)
.chatModel(chatModel)
.chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(100))
.build();
}
@Bean("chatMemoryToken")
public ChatMemoryAssistant chatMemoryByToken(ChatModel chatModel){
TokenCountEstimator tokenCountEstimator = new OpenAiTokenCountEstimator("gpt-4");
return AiServices.builder(ChatMemoryAssistant.class)
.chatModel(chatModel)
.chatMemoryProvider(memoryId -> TokenWindowChatMemory.withMaxTokens(100000,tokenCountEstimator))
.build();
}
}
接口调用
@RestController
public class ChatMemoryController {
@Resource(name = "chatMemoryWindow")
private ChatMemoryAssistant windowChatMemoryAssistant;
@Resource(name = "chatMemoryToken")
private ChatMemoryAssistant tokenChatMemoryAssistant;
@GetMapping("/chat/memory1")
public String chatMemory1(){
windowChatMemoryAssistant.chatMemory(1L,"你的名字叫爱吃鱼的大灰狼");
String result = windowChatMemoryAssistant.chatMemory(1L, "你叫什么名字");
windowChatMemoryAssistant.chatMemory(2L,"你的名字叫安静的狮子");
String result2 = windowChatMemoryAssistant.chatMemory(2L, "你叫什么名字");
return result + "\r\n" + result2;
}
@GetMapping("/chat/memory2")
public String chatMemory2(){
tokenChatMemoryAssistant.chatMemory(3L,"你的名字叫爱吃鱼的大灰狼");
String result = tokenChatMemoryAssistant.chatMemory(3L, "你叫什么名字");
tokenChatMemoryAssistant.chatMemory(4L,"你的名字叫安静的狮子");
String result2 = tokenChatMemoryAssistant.chatMemory(4L, "你叫什么名字");
return result + "\r\n" + result2;
}
}