
别只给 Agent 加个记忆字段长期记忆架构的核心是“遗忘机制”和“检索质量”这两年做 AI Agent 落地大家几乎都会遇到同一个瓶颈单次对话里表现很好一跨会话就变成了“金鱼”。问它昨天整理过的项目资料它理直气壮地编一个让它基于上个月的供应链数据做判断它完全没有上下文。很多团队的第一反应是那就给 Agent 加一个 memory 字段把历史信息存进去。这个方向没有错但如果只是简单地把聊天记录塞进一个变量、一个 JSON 文件或者一张数据库表很快会发现更大的问题记忆越来越多Agent 反而越来越“笨”。真正需要拆清楚的不是“有没有记忆”而是记忆怎么分层、怎么筛选、怎么检索、怎么过期、怎么遗忘。我按一套完整架构实现思路把短期记忆、长期记忆、分层存储、检索、遗忘机制串起来讲一遍。这套思路不只是给 LangGraph、AutoGen 这类框架用的也可以直接迁移到你自建的 Agent 系统里。1. 先搞清楚 Agent 需要的不是“存储”而是“记忆生命周期”很多教程会把短期记忆等同于上下文窗口长期记忆等同于外部存储。这个理解能解释概念但距离工程实现差得很远。真实情况是短期记忆和长期记忆不是两个“地方”而是两条不同的生命周期管理链路。它们在数据格式、写入时机、读取方式、过期策略上完全不一样。1.1 短期记忆不是“窗口大一点”就能解决的短期记忆首先当然跟上下文窗口有关。当前对话里用户提到的需求、Agent 已经采取的步骤、中间结果的依赖关系这些必须在一次任务执行期间被持续引用通常直接放在模型上下文或线程状态里。但短期记忆的真正难点不是容量而是“组织方式”。一个任务执行到第 8 步时前面 7 步的中间产物、临时假设、已经确认过的约束条件如果全部堆在上下文里模型会被噪声干扰。更合理的方式是给短期记忆做结构化管理比如划分成任务目标、已完成步骤、当前状态、待确认信息而不是一整块流水账。LangGraph 里的 Thread State 就是一个典型例子。所有节点共享的状态对象看起来像长期存储其实它的生命周期只覆盖一次任务线程任务结束或会话切换后如果不显式持久化它就消失了。把它当成短期记忆的容器还是当成长期记忆的入口取决于你有没有做“沉淀”这一步。1.2 长期记忆的本质是“业务事实”和“可复用模式”长期记忆真正要解决的不是“记得上次聊了什么”而是“Agent 在跨会话、跨任务时能稳定复用哪些事实、偏好、规范和模式”。同样是记录一段用户信息“用户偏好Markdown格式”是长期记忆“用户昨天发过一段JSON”不是后者只是临时事实过两天就失去价值。同样在供应链监控场景里“供应商A的交付周期通常是5天”是长期记忆“昨天供应商A的交货单号是XXX”是临时状态。区分这两种信息是设计长期记忆的第一原则。如果什么信息都往里写长期记忆很快就会变成一个大垃圾场检索时召回大量无效内容模型反而被干扰。1.3 为什么要用“生命周期”而不是“存储”来理解记忆用生命周期理解记忆最大的好处是逼你回答四个问题什么信息需要被记录记录在哪里、用什么结构什么时机下如何被取回什么条件下该被遗忘或降级Storage 只是这一步里的载体。更多时候记忆架构的瓶颈在写策略、读策略和淘汰策略而不在数据库本身。2. 记忆分层的工程思路工作记忆、情景记忆、语义记忆不是三个表认知科学里经常把记忆分成工作记忆、情景记忆、语义记忆。Agent 的长期记忆架构可以借鉴这个分层但工程实现时不能简单套概念而是要把它落到具体的数据结构和流程上。2.1 工作记忆对应当前任务上下文工作记忆对应任务执行期间最活跃的那部分状态。它应该保持在“可快速读取、可覆盖、低延迟”的载体里通常是内存状态、上下文窗口、或者一个轻量级 Redis 缓存。工作记忆的数据特点是高频率读写单次任务生命周期数据量小、结构灵活不强调持久化在 LangGraph 里这意味着你需要谨慎设计 State 的字段不是所有中间数据都值得保留到任务结束。比如一个数据分析 Agent 在处理 100 个文件时当前处理到第几个文件这种进度信息放到工作记忆没问题每个文件全部内容的 embedding 都塞进 State就是典型的反模式。2.2 情景记忆记录“发生过什么”按时间线管理情景记忆记录的是 Agent 经历过的重要事件和对话序列。它解决的核心问题是当用户说“还记得昨天分析的那个销售数据吗”Agent 能准确找到“昨天那个分析”对应的具体事件。工程上不建议直接把原始对话全文存进去。更好的做法是做成“事件摘要”加“关键实体索引”的结构时间事件发生的时间戳用户参与者或用户标识任务类型数据分析、代码生成、文档归纳核心内容摘要300-500字的事件摘要关联实体文件名、项目名、指标名、偏好关键词这种结构让情景记忆既能被时间线检索也能被实体检索同时不会因为原始日志过长而导致信息密度下降。对于 LangGraph 这类框架To-Many 关系存储是一个常见设计一个长期记忆主题关联多个会话每个会话又记录多个关键事件。2.3 语义记忆沉淀“规律、偏好和知识”不依赖具体时间语义记忆是长期记忆里价值最高的部分因为它提取的是跨任务稳定的信息。比如用户偏好详细代码注释当前项目使用 Python 3.11客户要求所有报告先出摘要再出明细系统处理订单时需要先验证库存再调用支付语义记忆的构建方法通常是定期从情景记忆中做“知识蒸馏”而不是每写一条对话就生成一条语义记忆。比如每隔 5 次会话、或积累了一定量的情景记忆后用一个提炼任务LLM总结出用户的稳定偏好和项目约束再写入语义记忆库。这里有一个很容易做错的点不要用 LLM 对每条消息都做语义抽取成本高噪声也大。更合适的方式是批量归纳比如任务完成后把本次任务的关键决策提炼成语义记忆每 N 次会话做一次偏好总结用户明确说“以后都这样”时立刻写入2.4 三个层级之间不是并列关系而是流水线关系三层记忆的正确关系是工作记忆在任务执行中产生原始信息经过提炼变成情景记忆情景记忆再经过总结和去重变成语义记忆语义记忆反过来影响下一次任务的工作记忆初始化。这套流水线是长期记忆架构的主轴。如果你只做了“存储”没做“提炼”长期记忆的质量一定上不去。3. 检索机制记忆不是存得越多越好是“取得到”才算数长期记忆架构里真正决定体验的往往是检索。同一个记忆库检索设计得好模型像多了一个贴身助理检索设计得差模型像是被塞了一堆无关背景资料。3.1 检索链路先窄后宽别一上来就全局向量搜索我把记忆检索拆成三步候选生成、粗排、精排。候选生成负责快速缩小范围可以用如下方式按时间范围过滤最近7天、最近30天按实体过滤只召回涉及特定项目名、用户名的记忆按类型过滤只召回语义记忆或只召回情景记忆向量相似度召回把当前输入 embedding 后取 Top-K粗排的目标是把候选记忆限制在模型上下文能接受的范围通常 6 到 15 条。精排则按照与当前任务的相关性排序相关性的计算可以结合向量相似度、时间衰减因子、实体匹配度。这里要特别提醒如果一开始就只靠向量相似度去做全局检索大概率会出现低质量召回因为向量检索对“语义相近但实体不同”的信息区分度并不够。3.2 混合检索向量 关键词 元数据过滤比较稳健的方案是混合检索。在记忆库中每条记忆都应该带有元数据时间、来源会话、涉及实体、记忆类型、重要性分数、最后访问时间。检索时先按元数据做结构化过滤然后并行跑向量检索和关键词检索最后合并结果做去重和排序。以召回一条记忆的技术为例async def recall_memories(query: str, user_id: str, top_k: int 10): # 1. 结构化过滤 filters { user_id: user_id, memory_type: semantic } # 2. 并行执行向量检索和关键词检索 vector_results await vector_store.search(query, filtersfilters, top_ktop_k) keyword_results await keyword_store.search(query, filtersfilters, top_ktop_k) # 3. 合并去重 merged merge_results(vector_results, keyword_results) # 4. 精排 ranked rescore(merged, query) return ranked[:top_k]这个示例结构适用于主流向量数据库 ES/Bm25 的组合实际场景里可以按需求替换组件。3.3 检索之后的“时机判断”比检索本身更关键检索到记忆后是否要把这些记忆注入当前上下文不是无脑操作。一般来说有三种使用方式直接注入上下文适用于用户直接问“我之前说过什么偏好”这类场景把检索到的高相关记忆作为对话背景。交给一个“记忆路由”判断是否需要如果当前任务和检索到的记忆相关性一般就不要注入避免干扰。作为工具调用结果返回Agent 可以在需要时主动读取记忆而不是系统每次自动注入。我对生产系统的建议是低风险、高确定的记忆自动注入高风险、需要验证的记忆先路由给 Agent 判断。比如用户偏好用 Python 脚本处理数据这种可以直接注入但“用户曾经说过某个供应商价格偏高”这种判断性记忆最好交给 Agent 自己决定何时使用。4. 遗忘机制长期记忆架构里最被低估、也最决定上限的一环很多人做长期记忆时会忽视遗忘机制。但工程经验会告诉你遗忘策略不做好记忆库三个月后就会变成“噪声库”检索质量急剧下降Agent 的回答越来越离谱。4.1 遗忘不只是删除还包括衰落、降级、归档长期记忆的遗忘处理至少有四个级别访问触发式增强每次被检索到增加重要性分数时间衰减超过一定时间未被访问重要性指数下降降级归档重要性低于阈值时从语义记忆降级为情景记忆或从在线存储转移到冷存储删除明确失效的记忆比如用户改变偏好或者长期无访问且无价值的记忆直接删除用工程语言说每条记忆都应该有一组生命周期字段memory_record { memory_id: xxx, content: 用户偏好Markdown格式, memory_type: semantic, importance_score: 0.85, access_count: 12, last_access_at: 2025-06-01T08:00:00Z, created_at: 2025-05-01T08:00:00Z, decay_factor: 0.95 }4.2 遗忘机制为什么不能“简单按时间删”最常见的一种错误是设置一个固定的过期时间到点就删。但不同记忆的保鲜期差异很大供应商联系人方式需要长期保留上次任务的临时文件路径一周后就不该再被回忆起来。更合理的做法是“权重随时间衰减而不是到点删除”配合访问频率做动态调整def calculate_importance(record, current_time): base_score record[importance_score] days_since (current_time - record[last_access_at]).days time_decay record[decay_factor] ** days_since access_boost min(record[access_count] * 0.01, 0.2) return base_score * time_decay access_boost这样设计会带来一个很自然的效果经常被访问的记忆越来越稳定长期不被访问的记忆不断滑向冷端最后自动进入归档或清理队列。整个系统像生物体的记忆一样在“记住什么”上持续做动态取舍。4.3 对抗性遗忘与记忆冲突解决更高级一点的核心问题是当新记忆和旧记忆矛盾时系统怎么处理典型场景用户之前一直偏好简洁回复今天明确说“以后请给我详细报告”。如果系统只做增量写入老记忆还在检索时新旧冲突就被同时注入上下文模型行为就在“简洁”和“详细”之间震荡。解决思路是给记忆增加一个 version 或 supersedes 字段new_memory { content: 用户偏好详细报告需要覆盖背景、数据、结论, supersedes: old_memory_id, memory_type: semantic, importance_score: 1.0 }写入时如果检测到同一实体有旧偏好自动把旧记忆标记为失效、降权或链路到新的记忆。这一步做好了Agent 的长期行为才会“跟着用户一起成长”而不是每隔几天就精神分裂一次。5. 基于 LangGraph 的长期记忆落地要点如果你正在用 LangGraph 开发 Agent长期记忆的落地已经有一些现成思路可以借鉴。我这里给出一个框架层面的映射而不是绑定某个具体版本。5.1 利用 LangGraph 的持久化机制做 Thread State 冷存储LangGraph 的 Thread State 天然适合作为工作记忆的载体配合其持久化扩展例如基于 Checkpointer 的机制可以实现跨断点恢复。但要注意跨 Thread 的长期记忆仍然需要自己构建独立的记忆库而不是把所有 Thread State 都当成长期记忆堆起来。更务实的做法是用 Checkpointer 恢复中断任务这是工作记忆的持久化用一个独立的长期记忆模块向量数据库 Redis 关系表存储情景记忆和语义记忆任务结束时从 Thread State 中提取关键信息写入长期记忆模块5.2 在 Agent 编排流程中插入记忆写入和读取节点在 LangGraph 里记忆操作可以建模成独立节点而不是散落在业务节点里。比如memory_save_node在当前任务结束时执行读取 Thread State 摘要调用提炼逻辑写入长期记忆。memory_recall_node在每个新任务开始时执行读取用户输入完成检索与粗排把记忆注入后续节点上下文。这样做的好处是记忆逻辑和业务逻辑解耦后续调整遗忘策略、检索策略时不用改主流程。5.3 长期记忆模块的独立扩展设计不管用不用 LangGraph长期记忆模块建议设计成独立的服务单独维护。模块内部可以划分成四个层次接入层提供写入、检索、更新、删除的 API策略层记忆提炼、重要性评分、遗忘调度存储层向量库、关系库、Redis 缓存质量控制层记忆冲突检测、重复清洗、过期归档分层的意义在于不同团队可以并行迭代不同层次。比如存储层可以用标准的向量库方案策略层则是团队的核心竞争力。6. 从一次摸爬滚打中总结的长期记忆落地清单我基于常见实践整理了一套四步走方案方案不依赖特定框架可以按自己业务情况微调。6.1 第一步给记忆定义“类型”和“优先级”先别急着建库先把业务里需要记忆的信息梳理成类型清单。建议至少定义这几个维度用户偏好类写作风格、输出格式、沟通方式项目事实类项目目标、约束条件、技术栈、决策记录业务流程类审批链路、依赖关系、关键节点临时状态类当前任务进度、待确认信息、临时文件路径优先级建议按照“对后续任务影响程度”来评估。影响越大、复用频率越高的信息优先级越高越值得写进语义记忆。6.2 第二步按读写频率选择存储不同记忆类型的读写特征完全不同用一张表规划清楚记忆类型读写频率推荐存储生命周期工作记忆极高Thread State / Redis单次任务情景记忆中等事件表 向量索引1-3个月语义记忆低写高读向量库 关系表长期冲突记录低关系表长期存储选型时别被新技术带偏先看满足业务需求的成本和稳定性。6.3 第三步设计“任务结束即提炼”的写入流程写入长期记忆的时机比写入什么更关键。建议在任务结束或会话结束时批量提炼而不是在任务执行过程中频繁触达 LLM。一个比较实用的技巧是流程首先要忠实调用 LLM 做提炼记录需要额外注意的信息如耗时、token消耗、结果质量每次只提炼该任务新出现的高价值信息对每条提炼结果做“价值自评”打分低于阈值就不入库6.4 第四步建立遗忘与质量巡检机制最后一步是给长期记忆库配一个“质检员”。定期做下面几件事检查重复记忆是否两次会话写了同一偏好但措辞不同检查过时记忆是否有明确被替代的旧偏好检查信息密度是否有大量低价值、低访问的记忆占主导检查检索质量抽样测试若干 query看召回结果是否准确从这里以后真正决定了长期记忆价值的已经不是向量库的“档位”而是你这套生命周期管理流程是不是真的闭环了。回到最初的问题Agent 长期记忆架构到底在解决什么从表象看它解决的是“Agent 跨会话不会忘事”的问题。但从更本质的层面看它解决的是——让 Agent 从“每次重新认识世界”升级为“持续积累对世界的理解”。这个积累过程必须包含四个能力分层存储让不同类型的记忆各就各位提炼让记忆从原始信息中沉淀出知识检索让记忆在合适的时间被找回遗忘让记忆库不被噪声淹没。我把长期记忆项目放在一起比较过那些做得好的系统没有一个是从一开始就规划所有细节的。都是先用一个最小闭环把“任务结束提炼写入、新任务开始时检索注入”这条链路跑通再逐步加上时间衰减、记忆冲突、批量提炼、质量巡检。这个顺序建议你也试一下。