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节的完整内容系列文章持续更新中关注我不迷路

相关新闻

从MrBeast挑战看AIGC视频生成:Viggle AI如何让静态肖像动起来

从MrBeast挑战看AIGC视频生成:Viggle AI如何让静态肖像动起来

你刷到过这样的视频吗?一个穿着红色T恤、笑容极具感染力的光头小哥,在镜头前做出各种夸张、搞怪的动作,背景音乐动感十足,内容要么是挑战极限,要么是撒钱做慈善。没错,他就是YouTube上的顶流——MrBeast。他…

2026/10/9 20:13:31 阅读更多 →
FPGA硬件仿真GameBoy Color:从周期精确到复古游戏机复刻的工程实践

FPGA硬件仿真GameBoy Color:从周期精确到复古游戏机复刻的工程实践

1. 先搞清楚这个“副厂零件”FPGA GameBoy Color到底是什么 看到“全新副厂零件打造的FPGA版GameBoy Color”这个标题,很多人的第一反应可能是:这不就是个用现代芯片复刻的老游戏机吗?有什么特别的? 如果你这么想,那就…

2026/10/12 9:43:51 阅读更多 →
AutoGen进阶实战:构建高效可控的多智能体协作系统

AutoGen进阶实战:构建高效可控的多智能体协作系统

1. 从“能用”到“好用”:AutoGen进阶的核心价值如果你已经跟着教程跑通了第一个AutoGen的“Hello World”,让两个智能体聊了几句天,那么恭喜你,你已经打开了多智能体协作开发的大门。但紧接着,你可能会遇到一些现实问…

2026/9/27 0:30:24 阅读更多 →

最新新闻

RouterOS WebFig汉化补丁:v7.9–v7.13内核级本地化方案

RouterOS WebFig汉化补丁:v7.9–v7.13内核级本地化方案

简介:本资源是专为中文网络管理员与RouterOS初学者设计的Web界面汉化补丁包,解决英文WebFig界面带来的理解与操作障碍,显著提升中文用户在企业级路由配置中的效率与体验。压缩包共4个文件(257KB),含1个核心…

2026/10/13 3:43:54 阅读更多 →
用NodeXL做动态网络分析:从时间线到演化指标全流程

用NodeXL做动态网络分析:从时间线到演化指标全流程

很多人做社会网络分析,第一步就是把边列表导入软件,出一张五颜六色的网络图,然后开始数节点、算密度、找社群。说实话,静态分析确实入门友好,但我自己在用 NodeXL 的过程中越来越觉得,如果数据里带着时间戳…

2026/10/13 3:43:54 阅读更多 →
SpringAI+MySQL:基于MCP协议的安全数据访问服务实战

SpringAI+MySQL:基于MCP协议的安全数据访问服务实战

前阵子有个需求,要在内部系统里挂一个能回答业务问题的大模型助手,结果发现大部分问题都牵扯到真实订单数据。最开始我有两条路:给模型一个直连数据库的账号,或者给它写一堆REST查询接口。前者太吓人,后者太磨人。后来…

2026/10/13 3:43:54 阅读更多 →
RabbitMQ路由模式实战:用direct交换机实现日志分级精准投递

RabbitMQ路由模式实战:用direct交换机实现日志分级精准投递

我最早用RabbitMQ做基础的消息推送时,只会一把梭用fanout交换机,把所有消息广播出去,也没觉得有什么不对劲。直到做日志分级推送,才被现实狠狠教育了一顿:告警系统只想收error级别,审计系统只想收某个特定业…

2026/10/13 3:43:54 阅读更多 →
Windows端口占用排查与清理实战:从netstat到PowerShell

Windows端口占用排查与清理实战:从netstat到PowerShell

1. 端口占用这件事,到底是怎么回事先从一个最典型的场景说起:本地起了个后端服务,端口习惯性用 8080,结果启动日志直接报 "Port 8080 was already in use"。又或者装了个数据库,怎么都连不上,排查…

2026/10/13 3:43:54 阅读更多 →
Python编程中类与类的关系详解

Python编程中类与类的关系详解

前言 写代码写到一定程度,问题就不再是"怎么写一个类",而是"这几个类之间到底该是什么关系"。常见的关系有三类:继承(inheritance)、组合(composition)、依赖(d…

2026/10/13 3:42:54 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →