09 - Memory — Agent的记忆系统!从短期记忆到长期记忆,一篇搞定
适合前后端/测试等有编程基础的同学手把手教你让AI拥有“记忆”前言通过前两节课的学习我们已经掌握了LangChain的Model I/O统一调用大模型和Chain构建执行流程。现在你的AI应用已经能“听懂话”、“会干活”了。但它有一个致命的问题——鱼的记忆。回想一下第6节课我们讲过的内容大模型的API是无状态的每次调用都是独立的它完全不记得上一次聊了什么。在第6节课我们手动封装了一个MemoryChat类来管理对话历史。现在进入LangChain的世界我们要用更优雅、更强大的方式来解决这个问题。这就是今天的主角——Memory记忆系统。一句话定义LangChain的Memory组件通过结构化的方式存储、管理对话历史让AI具备“记忆能力”。全文约6000字代码均可直接复制运行。一、为什么AI需要记忆1.1 无状态API的本质大模型的API调用是无状态的——每次请求都是独立的模型不会主动记住任何之前的信息。问题重现# 第一轮对话response1llm.invoke(我叫小明)print(response1.content)# 你好小明很高兴认识你。# 第二轮对话独立请求response2llm.invoke(我叫什么名字)print(response2.content)# 抱歉我不知道你的名字。AI完全“失忆”了。这让多轮对话变得极其尴尬。1.2 Memory的核心作用LangChain的Memory组件通过两个核心动作解决这个问题动作说明存储Save将每一轮的用户输入和AI输出保存到指定存储介质内存、数据库等提取Load新一轮对话时从存储介质中提取历史对话注入到Prompt中供LLM参考Memory能帮我们解决的实际问题避免重复提问你说过“我叫小明”之后AI能直接叫出你的名字支撑复杂任务让AI“先梳理需求再生成方案”它能记住中间结果简化交互不用每次提问都把前因后果说一遍1.3 短期记忆 vs 长期记忆LangChain的记忆系统分为两个层级记忆类型作用范围存储方式典型场景短期记忆Short-term Memory当前会话/线程内内存/检查点多轮对话、跟踪对话状态长期记忆Long-term Memory跨会话、跨用户向量数据库/持久化存储记住用户偏好、历史知识通俗理解短期记忆就像你正在进行的聊天——聊完就忘了长期记忆就像你的通讯录——每次打开都能找到。二、LangChain的四种核心记忆类型LangChain提供了多种Memory实现适用于不同场景。下面我们逐一学习最常用的四种。2.1 ConversationBufferMemory — 全量记忆核心机制保存所有对话历史像一个不断增长的记事本。优点实现简单信息完整无丢失。缺点对话增长会导致Token消耗巨大。适用场景对话轮次少、内容短的场景。fromlangchain.memoryimportConversationBufferMemory# 初始化记忆memoryConversationBufferMemory()# 保存对话上下文memory.save_context({input:你好啊AI小助手。},{output:嗨有什么我可以帮助你的})memory.save_context({input:我叫小明},{output:你好小明很高兴认识你。})# 加载记忆historymemory.load_memory_variables({})print(history)# 输出: {history: Human: 你好啊AI小助手。\nAI: 嗨有什么我可以帮助你的\nHuman: 我叫小明\nAI: 你好小明很高兴认识你。}# 也可以直接获取消息对象列表messagesmemory.chat_memory.messagesprint(messages)# 输出: [HumanMessage(content你好啊AI小助手。), AIMessage(content嗨有什么我可以帮助你的), ...]与Chain结合使用fromlangchain.chainsimportLLMChainfromlangchain.promptsimportPromptTemplatefromlangchain.memoryimportConversationBufferMemory# 定义Prompt模板注意变量名必须是historytemplate 你是一个友好的AI助手。 对话历史 {history} 人类{input} AIpromptPromptTemplate(input_variables[history,input],templatetemplate)# 创建带记忆的ChainmemoryConversationBufferMemory()chainLLMChain(llmllm,promptprompt,memorymemory,verboseTrue# 开启可以看到内部执行过程)# 多轮对话print(chain.predict(input你好我叫小明))print(chain.predict(input我叫什么名字))# AI会记得2.2 ConversationBufferWindowMemory — 窗口记忆核心机制只保留最近K轮对话像一个滑动窗口。优点有效控制上下文长度Token消耗可控。缺点会丢失窗口外的历史信息。适用场景只需要近期对话上下文的场景。fromlangchain.memoryimportConversationBufferWindowMemory# k2 表示只保留最近2轮对话每轮1次Human1次AImemoryConversationBufferWindowMemory(k2)memory.save_context({input:第1轮你好},{output:你好})memory.save_context({input:第2轮我叫小明},{output:你好小明})memory.save_context({input:第3轮我喜欢编程},{output:太好了})# 加载记忆——只会看到最近2轮historymemory.load_memory_variables({})print(history[history])# 输出只包含第2轮和第3轮第1轮已被遗忘2.3 ConversationSummaryMemory — 摘要记忆核心机制调用LLM将历史对话总结为摘要用摘要替代完整历史。优点大幅压缩Token消耗。缺点摘要可能导致细节丢失。适用场景长对话且关注整体脉络的场景。fromlangchain.memoryimportConversationSummaryMemory# 初始化摘要记忆需要传入LLM用于生成摘要memoryConversationSummaryMemory(llmllm)# 多轮对话后记忆会自动生成摘要memory.save_context({input:你好我叫小明是一名软件工程师},{output:你好小明很高兴认识你。})memory.save_context({input:我主要做后端开发用Python},{output:Python后端开发很有前景})memory.save_context({input:最近在学习AI Agent},{output:AI Agent是当前最热门的方向})# 加载记忆——看到的是摘要而不是完整对话historymemory.load_memory_variables({})print(history[history])# 输出类似: 小明是一名软件工程师专注于Python后端开发目前正在学习AI Agent。2.4 VectorStoreRetrieverMemory — 向量检索记忆核心机制将对话存入向量数据库按语义相关性检索最相关的记忆。优点能从超长历史中检索关键信息支持跨会话记忆。缺点依赖向量数据库实现较复杂。适用场景对话历史极长需要根据语义检索关键记忆的场景。fromlangchain.memoryimportVectorStoreRetrieverMemoryfromlangchain.vectorstoresimportChromafromlangchain.embeddingsimportOpenAIEmbeddings# 1. 创建向量数据库embeddingOpenAIEmbeddings()vectorstoreChroma(collection_nameconversation_memory,embedding_functionembedding)# 2. 创建VectorStoreRetrieverMemory# retriever会检索最相关的top-k条记忆memoryVectorStoreRetrieverMemory(retrievervectorstore.as_retriever(search_kwargs{k:3}),memory_keyhistory# 注入Prompt时的变量名)# 3. 保存记忆自动向量化存储memory.save_context({input:我喜欢看科幻电影特别是《星际穿越》},{output:《星际穿越》是一部经典})memory.save_context({input:我住在北京经常去朝阳公园跑步},{output:朝阳公园环境很好适合跑步})memory.save_context({input:我是Python开发者有5年经验},{output:5年Python经验很资深了})# 4. 检索相关记忆基于语义相似度而非关键词匹配# 问推荐一部科幻片 → 自动检索到关于《星际穿越》的记忆relevantmemory.load_memory_variables({input:推荐一部科幻片})print(relevant[history])# 输出包含《星际穿越》相关的记忆三、四种记忆类型对比总结记忆类型核心机制优点缺点适用场景ConversationBufferMemory保存全部对话实现简单信息完整Token消耗巨大短对话ConversationBufferWindowMemory只保留最近K轮控制Token消耗丢失窗口外历史只需近期上下文ConversationSummaryMemoryLLM生成摘要大幅压缩Token细节可能丢失长对话、关注整体脉络VectorStoreRetrieverMemory向量检索相关记忆支持超长历史、语义检索实现复杂需向量库极长对话、跨会话记忆四、实战构建带记忆的“智能客服Agent”把前面学的知识综合起来构建一个真正能用的智能客服Agent。需求用户首次对话时Agent收集用户信息姓名、偏好Agent能记住这些信息在后续对话中调用使用ConversationSummaryMemory管理长对话fromlangchain_openaiimportChatOpenAIfromlangchain.chainsimportLLMChainfromlangchain.promptsimportPromptTemplatefromlangchain.memoryimportConversationSummaryMemory# 1. 初始化模型llmChatOpenAI(modeldeepseek-v4-flash,api_key你的Key,base_urlhttps://api.deepseek.com,temperature0.7)# 2. 定义系统Prompttemplate 你是一位专业的智能客服助手名叫小智。 你的职责是耐心、友好地回答用户的问题并记住用户告诉你的所有信息。 以下是对话摘要历史记忆 {history} 人类{input} 小智promptPromptTemplate(input_variables[history,input],templatetemplate)# 3. 创建带摘要记忆的ChainmemoryConversationSummaryMemory(llmllm,memory_keyhistory)chainLLMChain(llmllm,promptprompt,memorymemory,verboseFalse)# 4. 模拟多轮对话defchat_with_agent(user_input):responsechain.predict(inputuser_input)returnresponse# 测试对话print(客服小智您好我是智能客服小智有什么可以帮您)# 第一轮用户自我介绍user1你好我叫王芳想咨询一下你们的产品print(f用户{user1})print(f小智{chat_with_agent(user1)}\n)# 第二轮用户问具体问题Agent应该记得用户的名字user2我上次问的那个智能手表现在有优惠吗print(f用户{user2})print(f小智{chat_with_agent(user2)}\n)# 第三轮继续追问Agent应该记得之前的对话脉络user3好的那帮我下单一个吧print(f用户{user3})print(f小智{chat_with_agent(user3)}\n)# 查看当前记忆摘要print(--- 当前记忆摘要 ---)print(memory.load_memory_variables({})[history])运行效果客服小智您好我是智能客服小智有什么可以帮您 用户你好我叫王芳想咨询一下你们的产品 小智您好王芳很高兴为您服务。请问您想了解我们哪款产品呢 用户我上次问的那个智能手表现在有优惠吗 小智王芳您好您之前问的智能手表现在确实有优惠活动目前打8折。 用户好的那帮我下单一个吧 小智好的王芳我这就帮您下单请问您需要什么颜色的 --- 当前记忆摘要 --- 王芳是一位咨询产品的客户她询问了智能手表并决定下单购买。Agent成功记住了用户的名字“王芳”以及之前的对话脉络五、生产环境最佳实践5.1 会话隔离使用session_id在实际生产中你需要为每个用户/会话维护独立的记忆而不是所有用户共享一个记忆。# 用字典管理多个会话的记忆session_memories{}defget_memory_for_session(session_id):ifsession_idnotinsession_memories:session_memories[session_id]ConversationBufferMemory()returnsession_memories[session_id]# 每个用户使用自己的session_iduser1_memoryget_memory_for_session(user_001)user2_memoryget_memory_for_session(user_002)5.2 持久化存储默认的Memory只存在内存中程序重启就丢失了。生产环境需要持久化。# 使用Redis存储对话历史示例importredisfromlangchain.memoryimportConversationBufferMemoryfromlangchain.memory.chat_message_historiesimportRedisChatMessageHistory# 使用Redis作为存储后端historyRedisChatMessageHistory(session_iduser_001,urlredis://localhost:6379/0)memoryConversationBufferMemory(chat_memoryhistory,return_messagesTrue)5.3 Token消耗监控使用tiktoken监控Token消耗防止超出上下文窗口。importtiktokendefcount_tokens(text,modelcl100k_base):encodingtiktoken.get_encoding(model)returnlen(encoding.encode(text))# 在每次调用前检查historymemory.load_memory_variables({})[history]token_countcount_tokens(history)print(f当前记忆消耗Token:{token_count})iftoken_count100000:# 超过阈值print(警告记忆过长考虑切换到SummaryMemory)5.4 记忆类型选择决策树对话轮次少10轮 → 是 → ConversationBufferMemory简单够用 → 否 ↓ 需要记住所有细节 → 是 → ConversationBufferWindowMemory控制窗口大小 → 否 ↓ 关注整体脉络而非细节 → 是 → ConversationSummaryMemory摘要压缩 → 否 ↓ 对话极长、需要语义检索 → 是 → VectorStoreRetrieverMemory向量检索六、实战小练习作业练习构建一个“个性化美食推荐Agent”需求Agent能记住用户的基本信息姓名、口味偏好、忌口用户每次问“推荐餐厅”时Agent基于记忆中的偏好进行推荐使用ConversationSummaryMemory管理记忆模拟至少5轮对话验证Agent能记住跨轮次的信息提示代码框架fromlangchain.memoryimportConversationSummaryMemory# 1. 初始化模型和记忆llmChatOpenAI(...)memoryConversationSummaryMemory(llmllm,memory_keyhistory)# 2. 定义Prompt模板包含记忆和历史template 你是一位美食推荐专家。 对话摘要 {history} 用户{input} 专家promptPromptTemplate(...)# 3. 创建ChainchainLLMChain(llmllm,promptprompt,memorymemory)# 4. 多轮对话测试# 第1轮用户自我介绍 口味偏好# 第2轮推荐餐厅基于偏好# 第3轮用户补充信息忌口# 第4轮再次推荐基于更新后的偏好# 第5轮验证Agent记住了所有信息结语今天这节课我们全面学习了LangChain的Memory记忆系统知识点核心内容为什么需要Memory大模型无状态需要记忆组件维持对话连贯性短期 vs 长期记忆短期记忆管理当前会话长期记忆跨会话持久化ConversationBufferMemory全量保存适合短对话ConversationBufferWindowMemory滑动窗口控制Token消耗ConversationSummaryMemoryLLM生成摘要适合长对话VectorStoreRetrieverMemory向量检索支持超长历史和语义检索生产最佳实践会话隔离、持久化存储、Token监控下节课第10节我们将进入LangChain的另一个核心模块——Retrieval检索学习如何让Agent连接外部知识库构建RAG检索增强生成应用如果觉得有帮助欢迎点赞、收藏、评论三连我们下节课见 本文是《AI Agent开发实战》课程第9节的完整内容系列文章持续更新中关注我不迷路