对话记忆管理实战,BufferMemory、SummaryMemory、EntityMemory对比 对话记忆管理实战BufferMemory、SummaryMemory、EntityMemory对比上一篇讲了记忆系统的基本概念。这一篇我们深入对话记忆的管理。对话记忆是Agent最常用的记忆类型。用户和Agent一来一回地聊上下文怎么维护历史对话怎么管理Token怎么控制。这些问题的答案都在对话记忆策略里。LangChain提供了好几种对话记忆组件各有各的特点。我们一个一个看对比它们的优缺点和适用场景。ConversationBufferMemory最简单粗暴的记忆方式。把所有对话原封不动地存起来。fromlangchain_openaiimportChatOpenAIfromlangchain.memoryimportConversationBufferMemoryfromlangchain.chainsimportConversationChain llmChatOpenAI(modelgpt-3.5-turbo,temperature0)memoryConversationBufferMemory()conversationConversationChain(llmllm,memorymemory)conversation.invoke(我想学Python)conversation.invoke(Python好学吗)conversation.invoke(有什么好的学习资源)# 查看记忆里存了什么print(memory.buffer)优点是简单。不需要任何处理原样存取。信息完整不会遗漏任何细节。缺点是Token爆炸。对话长了以后所有内容都塞进PromptToken消耗线性增长。聊到50轮可能一轮调用就要消耗好几千Token。又慢又贵还会超出模型的上下文长度限制。适合场景。短对话几轮到十几轮。调试和开发阶段需要看到完整的对话历史。对Token消耗不敏感的场景。ConversationBufferWindowMemory滑动窗口策略。只保留最近N轮对话更早的自动丢弃。fromlangchain.memoryimportConversationBufferWindowMemory# 只保留最近5轮对话memoryConversationBufferWindowMemory(k5)conversationConversationChain(llmllm,memorymemory)foriinrange(10):conversation.invoke(f这是第{i1}条消息)# memory里只有最近5轮print(memory.buffer)优点是Token可控。不管聊多少轮记忆的大小是固定的。不会出现Token爆炸的问题。缺点是丢早期信息。用户在第1轮说的信息到第7轮就忘了。如果早期信息很重要这个策略就不合适。适合场景。长对话但每轮对话相对独立不需要回溯太远。比如闲聊、简单问答。k值设多大看你的场景和模型上下文长度。GPT-3.5上下文4097 Tokenk设5到8比较合适。GPT-4上下文更长k可以设大一点。ConversationSummaryMemory摘要策略。定期把旧对话总结成一段摘要用摘要替代原始对话。fromlangchain.memoryimportConversationSummaryMemory memoryConversationSummaryMemory(llmllm)conversationConversationChain(llmllm,memorymemory)conversation.invoke(我叫张三在一家互联网公司做后端开发)conversation.invoke(我们公司主要做电商技术栈是Java和Go)conversation.invoke(我最近想转AI方向在学Python和机器学习)# 查看摘要print(memory.buffer)# 输出类似# 用户叫张三在互联网公司做后端开发公司主要做电商# 技术栈Java和Go。最近想转AI方向在学Python和机器学习。三轮对话的内容被压缩成了一段摘要。Token大幅减少关键信息保留了。优点是Token省。长对话也能压缩成简短的摘要不会随对话增长而无限膨胀。缺点是有信息损失。摘要不可能保留所有细节。具体的措辞、语气、细节信息可能会丢。摘要质量依赖大模型的能力。每次生成摘要也要消耗Token。适合场景。超长对话需要保留整体脉络但不需要每句话的细节。客服对话、咨询场景。ConversationSummaryBufferMemory混合策略。近期对话保留原文旧对话变成摘要。两个策略的优点都占了。fromlangchain.memoryimportConversationSummaryBufferMemory# 设一个Token阈值超过阈值的旧对话会被摘要memoryConversationSummaryBufferMemory(llmllm,max_token_limit200,# 超过200 Token的旧对话会被压缩)conversationConversationChain(llmllm,memorymemory)foriinrange(20):conversation.invoke(f消息{i1}这是一段对话内容包含一些信息。)# 近期对话是原文早期对话是摘要print(memory.buffer)优点是平衡。近期对话完整保留细节不遗漏。旧对话压缩成摘要控制Token。既保留了近期的上下文又不让Token无限增长。缺点是实现复杂。需要管理原文和摘要的切换内部逻辑比较复杂。摘要生成也需要消耗额外的Token。适合场景。大部分需要多轮对话的场景。既能保持近期上下文的准确性又能控制总Token量。这是我比较推荐的策略。ConversationEntityMemory实体记忆。从对话中提取实体信息单独存储和维护。fromlangchain.memoryimportConversationEntityMemory memoryConversationEntityMemory(llmllm)conversationConversationChain(llmllm,memorymemory)conversation.invoke(我叫张三在阿里巴巴做算法工程师)conversation.invoke(我老婆叫李四她在字节跳动做产品经理)conversation.invoke(我们住在杭州)# 查看实体记忆print(memory.entity_store.store)# 输出类似# {# 张三: 用户的名字在阿里巴巴做算法工程师,# 阿里巴巴: 用户工作的公司,# 李四: 用户的妻子在字节跳动做产品经理,# 字节跳动: 李四工作的公司,# 杭州: 用户居住的城市# }conversation.invoke(我老婆在哪家公司)# Agent能回答字节跳动因为它记住了李四这个实体优点是结构化。实体信息被提取出来单独存储不会因为对话变长而丢失。查询特定实体的信息很方便。缺点是只关注实体。实体之间的复杂关系、时间序列信息、非实体的上下文它不太好处理。实体提取的质量依赖大模型。适合场景。需要跟踪人物、地点、组织等实体信息的场景。比如个人助手、客户关系管理。怎么选对比一下这几种策略。策略Token控制信息保留实现难度适合场景Buffer差完整最简单短对话、调试Window好丢早期简单长对话、闲聊Summary好有损失中等超长对话SummaryBuffer较好近期完整较复杂大部分场景Entity好实体完整中等实体追踪我的建议是先用BufferWindowMemoryk设5到10。简单有效大部分场景够用。如果对话特别长换成SummaryBufferMemory。近期原文加远期摘要兼顾效果和Token。如果需要追踪用户信息、实体关系加一层EntityMemory。跟其他策略可以组合使用。记住这些策略可以组合。比如同时用WindowMemory管理近期对话用EntityMemory管理实体信息用向量数据库做长期记忆。组合使用效果更好。下一篇讲知识库记忆。怎么把对话中的重要信息提取出来存入知识库形成长期记忆。