Agent无记忆之痛:为什么用户说了3次“交互屏组装A线”它还在问?

发布时间:2026/7/27 7:46:29
Agent无记忆之痛:为什么用户说了3次“交互屏组装A线”它还在问? 导航收藏不迷路—制造业数据与AI践行者老蒋的技术博客全系列文章汇总持续更新文章摘要与Agent对话时用户分三次提供了意图、日期、产线名称但Agent每次都重新询问缺失信息仿佛患上“失忆症”。本文剖析Agent无状态Stateless的设计本质揭示AgentExecutor默认不携带Memory的机制缺陷并提供ConversationBufferMemory的接入方案。适用于所有基于LangChain构建对话Agent的开发者。问题现象兄弟们前面五篇排坑笔记你们一路跟下来了。导入链污染、版本碎片化、中文括号、嵌套JSON、花括号战争——我们一个接一个地解决了。我以为Agent终于可以好好干活了。然后我做了一个测试——模拟真实用户的对话习惯分三次输入信息‍ 你: oee Agent: 您好请告知我要查询哪条产线以及具体是哪个日期的数据 ‍ 你: 2026-07-18 Agent: 您想查询哪条生产线在2026-07-18的OEE呢请提供具体的产线名称。 ‍ 你: 交互屏组装A线 Agent: 请问您想查询哪一天的OEE数据我当时的第一反应又是这不科学啊......我跟它说了三次话第一次说我要查OEE第二次给了日期第三次给了产线名称。正常人或者说正常程序应该能把这三条信息拼在一起然后给我答案吧结果它倒好我说OEE它问产线我给产线它问日期我给日期它又问产线——就像一条金鱼记忆力只有3秒钟。根因分析说句实在话这其实不是Agent的Bug而是它的出厂设置就是这样。但问题是LangChain的文档里没有把这个“特性”用大字标出来导致我一开始以为是自己代码写错了。第一层Agent是无状态的Stateless by DesignLangChain的AgentExecutor在默认情况下每次调用invoke()都是一次独立的对话。它不会记住上一次用户说了什么也不会记住上一次它自己说了什么。每次调用时Agent接收到的信息只有当前这一次的用户输入input当前这一次的工具调用历史intermediate_steps它看不到之前的对话记录。这就意味着当你分三次输入信息时Agent的视角是这样的第1次调用用户说oee → 我不知道产线和日期 → 我问他要 第2次调用用户说2026-07-18 → 我不知道这是日期还是别的什么 → 我问他产线 第3次调用用户说交互屏组装A线 → 我不知道日期 → 我问他日期每一次都是全新的一轮对话之前的上下文全部丢失。第二层为什么会这样设计LangChain的设计者可能认为Agent应该是无状态的、可独立调用的函数——每次输入都独立处理不依赖外部状态。这样的设计在微服务架构、批量处理等场景下是合理的。但问题是我们做的是对话Agent。对话的本质是多轮交互用户不会一次性把所有信息都告诉你而是像跟人聊天一样想到哪儿说到哪儿。一个没有记忆的对话Agent就像一个每次见面都问你“你叫什么名字”的朋友——你会觉得他要么是记性太差要么是根本不在乎你。第三层你可能会问——那我用ConversationBufferMemory不就行了吗是的ConversationBufferMemory就是解决方案。但LangChain 0.3.x版本中AgentExecutor对memory的支持方式发生了变化不是简单传进去就能用的。如果只是把memory塞给AgentExecutor你会发现它根本不搭理你。这也是我花了点时间才搞明白的。解决方案说实话ConversationBufferMemory这个方案本身不复杂但LangChain的文档写得跟天书一样东一榔头西一棒槌。我踩了一遍坑之后整理出一套能直接跑的办法。第一步在common/base_agent_builder.py中注入Memory修改BaseAgentBuilder的build方法把memory挂到AgentExecutor上from langchain.memory import ConversationBufferMemory class BaseAgentBuilder: def __init__(self, temperature: float 0.1, max_iterations: int 5, with_memory: bool True): self.llm get_llm(temperaturetemperature) self.max_iterations max_iterations self.with_memory with_memory self._tools None def build(self) - AgentExecutor: tools self._register_tools() prompt PromptTemplate.from_template(self._get_prompt_template()) agent create_react_agent(self.llm, tools, prompt) # ---- 关键注入Memory ---- memory None if self.with_memory: memory ConversationBufferMemory( memory_keychat_history, return_messagesTrue ) executor AgentExecutor( agentagent, toolstools, memorymemory, # 这里传入memory verboseTrue, max_iterationsself.max_iterations, handle_parsing_errorsTrue ) return executor第二步修改Prompt模板让Agent知道“历史记录”的存在这一步是很多人会漏掉的。你光给Agent塞了Memory但如果不告诉它“你有历史记录可以用”它还是会无视。在_get_prompt_template中增加chat_history的引用def _get_prompt_template(self) - str: return 你是一个专业的制造业AI助手。 以下是对话历史记录 {chat_history} 当前用户的问题是{input} 你有权使用以下工具{tools} ... 这样Agent在每次推理时都会把之前所有的对话记录都加载到Prompt里实现“记忆”的效果。第三步验证——Agent终于“长记性”了修改之后重新运行测试‍ 你: oee Agent: 请问您要查询哪条产线、哪一天的OEE呢 ‍ 你: 交互屏组装A线 Agent: 好的产线是交互屏组装A线请问具体是哪一天呢 ‍ 你: 2026-07-12 Agent: 正在计算交互屏组装A线在2026-07-12的OEE... Agent: 交互屏组装A线在2026-07-12的OEE为86.39%。这才是对话该有的样子经验总结怕你忘了我再啰嗦一遍Agent默认是金鱼记忆想让它有长期记忆你得主动给它装一个“记忆芯片”。落到具体操作上就是四条AgentExecutor默认不携带memory。这是它的设计机制不是Bug。想让Agent记住对话必须主动注入ConversationBufferMemory或其它Memory类型。注入Memory的同时必须在Prompt模板里添加{chat_history}占位符。如果你只注入不引用Agent依然会无视历史记录——就像你给一个健忘症患者配了笔记本但没告诉他要翻笔记本一样。ConversationBufferMemory会随着对话变长而膨胀。如果对话轮次很多超过20轮建议换成ConversationSummaryMemory或ConversationBufferWindowMemory避免Token超限。Case02将完整实现带记忆的Agent。本文只介绍了核心原理和最小实现方案。在即将发布的Case02中我们会做一个完整的、可直接复用的带记忆Agent支持持久化存储和跨会话记忆。系列导航本文属于《数据与AI工程排坑笔记》系列上一篇离谱PromptTemplate 遇上 JSON花括号直接引发解析战争下一篇本智联工坊案例1系列已完结6篇全部交付 排坑笔记系列汇总至此《数据与AI工程排坑笔记》系列6篇文章已全部交付编号标题状态坑01__init__.py导入链污染✅ 已发布坑02LangChain版本碎片化✅ 待发布坑03Pydantic中文括号SyntaxError✅ 待发布坑04Agent嵌套JSON套娃✅ 待发布坑05PromptTemplate花括号冲突✅ 待发布坑06Agent无记忆之痛✅ 待发布本文问题源自智联工坊实战制造知识库工具调用Agent从零搭建OEE手册排班-CSDN博客 完整源码及深度教程见该文详细内容。建议收藏下次遇到Agent“金鱼记忆”问题可以快速对照本文排查。互动与交流你在使用LangChain Agent时有没有被它“金鱼般的记忆”折磨过欢迎在评论区吐槽或者分享你配置Memory时遇到的奇葩问题——咱们互相支支招。关于作者制造业数据与AI践行者老蒋23年IT老兵。聚焦制造业数据架构与AI融合落地。全流程实战全源码开源。标签#排坑笔记#LangChain#Agent#记忆系统