尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Agent记忆组件设计指南:从短期到永久,手写轻量实现与排坑实录

发布时间:2026/9/28 18:14:50

资讯中心
01
ARTICLE

Agent记忆组件设计指南:从短期到永久,手写轻量实现与排坑实录

Agent记忆组件设计指南:从短期到永久,手写轻量实现与排坑实录
做了快两年的Agent应用开发如果要我选一个最容易被低估、却又最影响体验的模块我一定会选记忆组件。很多项目在Demo阶段跑得挺惊艳一旦进入真实的多轮对话、跨天任务、需要识别用户偏好的场景就立刻原形毕露你问它上一步做了什么它一脸茫然你告诉它客户的购买偏好它下一轮就忘。说白了没有记忆组件的Agent就像一个只有几秒钟工作记忆的临时工永远在从零开始。这篇文章想系统聊聊Agent记忆组件的设计思路和落地技巧包括短期、长期、永久记忆怎么分层现成框架和自研怎么选以及怎么自己写一个轻量实现。内容更适合已经在做AI Agent开发、想给助手加上“记性”的工程师也适合刚入门、想弄明白“记忆到底是怎么做出来的”的朋友。我会尽量用实际操作中的场景来解释少讲空概念多给能直接抄作业的方案。1. 为什么Agent需要记忆从“金鱼脑”到“长期搭档”1.1 没有记忆的Agent会卡在哪里先说一个我自己的真实感受。早期做客服机器人我们直接用大模型API加Prompt每轮把用户的问题丢进去生成回答。单轮效果还行可一旦用户说“我刚才问的那个订单你还没回答完”模型就完全接不上话。后来换成长上下文模型稍微好一点但token成本爆炸而且超过几万字后模型对早期信息的关注度会明显下降。这个“接不上话”的根源就是Agent没有持久化记忆。除了连续对话任务延续更明显。比如让Agent帮你查资料、整理周报第一次调用生成了一份大纲第二次告诉它“把大纲里的第三部分展开”如果它不记得大纲内容只能重新生成一份不相关的回答。用户感知到的就是“这工具不靠谱”。更细一点用户偏好也很难积累同一个用户上周明确说过“报告不要摘要只要数据”这周Agent又给他写了一长段摘要这种体验非常劝退。所以记忆组件解决的不是“能不能聊”而是“能不能越用越懂你”。它让Agent在单次会话之外拥有跨会话、跨任务、甚至跨团队的信息复用能力。没有这一层Agent充其量是一个聪明的接口封装而不是一个合格的数字助手。1.2 记忆组件的三个核心层次设计记忆组件之前要先分清记忆的类型。我通常把Agent记忆分成三层短期记忆、长期记忆和永久记忆。听起来和人的记忆很像但实现方式完全不同。短期记忆工作记忆当前会话里正在处理的信息比如用户本轮输入、上一步的中间结果、模型刚生成的回复。它通常存放在上下文窗口里或者一个短暂的会话缓存里任务结束或会话超时后就失效。长期记忆情景记忆跨会话但仍然带有时效性的信息。比如用户昨天聊过的项目进展、上个星期提过的偏好。它需要存储到外部数据库并能在下次对话时被检索回来。永久记忆语义记忆稳定的、不随时间变化的事实。比如用户的公司名称、身份角色、权限等级、长期偏好。这类记忆优先级最高通常不允许轻易被覆盖。这个分层可以用一个例子来理解你早上和Agent讨论了一份合同这份合同的细节是短期记忆你告诉Agent“我们公司采购流程需要三个人审批”这是长期记忆而你是公司员工、你负责采购部这是永久记忆。如果不分层管理把什么都混在一起后面检索和更新会非常痛苦。1.3 记忆组件在Agent架构中的位置从架构上看记忆组件夹在Agent编排层和大模型之间承担的是“外部上下文”的角色。大模型本身有固定窗口限制记忆组件负责把最相关信息提取出来拼装成上下文再把新的重要信息写回存储。这个过程类似人类大脑的海马体不是把所有信息都原封不动存下来而是筛选、编码、索引最终在需要时提取。我在画系统设计时习惯把记忆组件拆成四个模块记忆写入、记忆存储、记忆检索、记忆更新。写入模块负责筛选“什么值得记”存储模块负责持久化检索模块负责在对话开始或任务进行中召回相关内容更新模块负责修改、合并、过期旧记忆。四个模块缺一不可。如果只做存储和检索那和普通数据库没有区别只有把写入和更新也纳入设计才算真正的记忆组件。提示很多Agent框架把记忆做成一个可选插件但实际项目里我建议把它当成一等公民从一开始就参与上下文构建。否则后面想加记忆往往要推倒重来。2. 记忆组件的整体设计与选型2.1 短期记忆别把上下文窗口当成记忆短期记忆最常见也最容易偷懒的做法是直接把所有对话历史都塞进上下文窗口。早期我也这么干结果对话超过二十轮就开始超长超出模型上下文长度就直接报错。后来加了滑动窗口只保留最近十轮。这样能解决报错但会丢失更早的关键信息比如用户在第一轮提到的约束条件。更好的做法是“滑动窗口滚动摘要”。窗口内保留最近几轮完整消息窗口外但仍有价值的早期内容由模型定期生成摘要作为压缩后的短期记忆继续参与对话。我实践下来的参数是最近8轮完整保留超过8轮就触发一次摘要摘要控制在300字以内再和最近8轮一起拼装。这样做的好处是既能保持即时语境又不至于让上下文无限膨胀。这里有个容易忽略的细节短期记忆不能只靠模型自己总结因为摘要的生成时机很关键。我会在每一轮结束之后检查当前上下文长度超过阈值就调用一次摘要而不是等到下一轮开始才做。这样能避免用户等待时间明显变长。2.2 长期记忆存储、索引、召回三件套长期记忆是Agent记忆组件的主战场。它的核心是一个三件套存储介质、索引方式、召回策略。存储介质常见有向量数据库如Milvus、Qdrant、Chroma、关系数据库PostgreSQL、MySQL和键值存储Redis、SQLite。向量数据库适合存储需要语义检索的文本记忆关系数据库适合存结构化的用户偏好和状态Redis适合短期热数据。我在实际项目中通常组合使用用户基本资料放PostgreSQL行为偏好放向量库会话缓存放Redis。索引方式上文本记忆要通过Embedding模型转成向量Metadata时间、来源、重要性、用户ID也要一起索引。召回策略不是简单的“算相似度取top_k”还要结合时间衰减、来源权重、重要性过滤。比如同样的两个记忆一个是今天说的一个是半年前说的即使语义相似度相同也该优先召回今天的。长期记忆的写入同样需要设计。我见过很多项目用Agent输出里的所有内容做Embedding结果存了一堆垃圾。我自己的原则是先让Agent判断这段信息是否包含“用户偏好、项目状态、待办事项、持续事实”中的一种再决定要不要写入。可以用一个小的分类Prompt也可以直接写规则包含“我喜欢”“我不喜欢”“我们的项目是”“我需要在周五前”这类触发词就写入。2.3 框架选型LangChain、MemGPT、自研怎么选做一个Agent记忆组件之前最纠结的是要不要直接用现成框架。我的答案很直接小Demo可以用框架生产环境至少要把记忆模块独立出来。LangChain的Memory模块提供ConversationBufferMemory和ConversationSummaryMemory等做短期记忆很方便但长期记忆的存取逻辑跟业务强相关框架层面的抽象往往不够灵活。MemGPT现在叫Letta把分层记忆做成了系统级方案有长期记忆存储和自我编辑机制概念很先进不过它引入了自己的运行时要完整嵌入现有Agent系统有一定改造成本。我自己更倾向于自研轻量层因为记忆组件的核心逻辑不复杂结构化存储、Embedding检索、自定义更新规则。这些用几百行代码就能搭起来还方便按业务调整。框架可以作为参考比如参考LangChain的Buffer结构和MemGPT的记忆管理思路但不要让框架限制你的数据结构。选型的时候重点想清楚三个问题一是你的记忆实体是什么是对话片段、用户画像、任务状态还是知识条目二是并发规模多大需不需要分布式存储三是记忆更新频率高不高如果用户偏好经常变化就需要支持实时更新和失效机制。2.4 记忆的数据结构与序列化这里给一个我常用的记忆条目结构它是一个JSON对象也是存到向量库和关系库的统一格式{ memory_id: mem_8f3a2c91, user_id: user_12345, type: preference, content: 用户偏好数据分析报告不需要摘要只要图表。, embedding: [...], importance: 0.8, timestamp: 1699000000, expire_at: null, source: session_42_round_7, metadata: {channel: web, project: data-report} }字段说明type用来区分偏好、任务状态、用户画像等importance控制召回权重expire_at实现临时记忆的自动过期source记录这条记忆来源于哪次会话哪一轮方便回溯。有了这个结构短期、长期、永久记忆只是type和expire_at不同存储和检索逻辑是共用的。这比我一开始按记忆类型建多张表要灵活得多。3. 从零手写一个轻量Agent记忆组件3.1 定义记忆的数据模型先写一个最简单的Python数据模型。我建议用Pydantic或dataclass做数据校验避免脏数据写进数据库。from dataclasses import dataclass from typing import Optional, Dict, Any import time dataclass class Memory: memory_id: str user_id: str type: str # preference / task / fact / event content: str importance: float 0.5 created_at: int time.time() updated_at: int time.time() expire_at: Optional[int] None source: Optional[str] None metadata: Dict[str, Any] None dataclass class MemoryRecord: memory: Memory vector: Optional[list] None score: float 0.0这里的Memory是逻辑实体MemoryRecord用于检索结果返回。实际存储时我把Memory序列化成JSON存入PostgreSQL的JSONB字段同时把content和向量存入向量库两者通过memory_id关联。这个组合方式让我既能做复杂查询又能做语义检索。3.2 写入记忆的时机与预处理写入逻辑是整个组件里最容易被忽略的。很多人觉得“对话内容直接存进去就行”其实不行。我的写入流程是每轮Agent执行完后把用户输入Agent回复中间步骤打包。用一个轻量分类器判断这段内容是否值得写入。这个分类器可以用Prompt实现也可以用规则。规则示例出现“记住/以后别忘了/我喜欢/我不喜欢/我们团队/项目截止”等关键词就触发。如果是值得写入的内容再判断它是用户画像、任务状态还是临时事件。用户画像和永久事实写入永久区任务状态写入长期区临时事件设置过期时间。生成embedding和结构化数据一起写入。我第一次实现时跳过了预处理把所有对话都存进去结果召回时经常出现两条互相矛盾的记忆比如用户前一轮说“预算控制在10万”后一轮又说“可以到15万”系统同时召回Agent逻辑直接混乱。加了分类和规则之后重要记忆才能稳定写入。3.3 混合检索向量、关键词和元数据过滤检索不能只靠向量相似度。我建议做混合检索先用关键词命中做精确匹配再用向量召回做语义扩展最后用Metadata过滤掉过期或来源不可信的记忆。下面是一个简化示例class MemoryStore: def __init__(self, embedder, vector_db, sql_db): self.embedder embedder self.vector_db vector_db self.sql_db sql_db def search(self, user_id: str, query: str, top_k: int 5): # 1. 关键词过滤 SQL精确查询 exact_results self.sql_db.query( SELECT * FROM memories WHERE user_id :uid AND content LIKE :kw, {uid: user_id, kw: f%{query}%} ) # 2. 向量召回 vec self.embedder.embed(query) vector_results self.vector_db.search( vectorvec, metadata{user_id: user_id}, top_ktop_k ) # 3. 合并排序去重 merged self._merge_and_rank(exact_results, vector_results) # 4. 时间衰减和重要性加权 return self._apply_decay(merged)_apply_decay是核心逻辑我用一个简单公式final_score raw_score * importance * decay_factor。decay_factor按时间衰减比如七天以内的记忆衰减系数为1.0超过七天每周减少0.2。这个逻辑看着简单但能明显提升召回质量。有一个经验是top_k不要设太大。我一般调成5最多8。Agent上下文窗口有限塞太多历史记忆反而干扰当前决策。如果每条记忆都很重要我会先做一个rerank用LLM对候选记忆打分只保留最相关的三条。3.4 更新与遗忘策略记忆不能只增不改。用户上周说喜欢A方案这周明确说A方案不行了如果旧记忆还在Agent会反复给出矛盾建议。我的策略分三种直接覆盖识别到同类型同主题的新记忆例如“用户偏好从A变成B”直接把旧记忆标记为过期。带权更新对于重要性较高的记忆不直接删除而是更新updated_at和content保留原始创建时间。自动遗忘使用TTL过期任务。比如临时事件设置expire_at为7天后过期后不参与检索。实现上我建立了一个“主题指纹”字段它基于内容做Hash同主题的新记忆写入时先查询是否有同一个user_id和主题指纹的旧记忆。如果有就执行更新而不是新增。这样可以避免存储膨胀也让召回结果更干净。注意永久记忆不要设置过期时间但要设置“不可篡改”标记。这类记忆的更新必须显式触发不能由对话内容自动覆盖。我就碰到过用户闲聊时说“我现在不想管这个项目了”系统把用户的“项目负责人”永久记忆给覆盖了结果下回任务分配直接出错。4. 实操中的常见问题和排坑实录4.1 记忆串台多用户信息互相污染新手最容易踩的坑就是没有按用户隔离记忆。开发时自己测试只有一个用户一切正常。上线后用户A问的记忆用户B也看得到直接酿成数据事故。解决方式很简单所有记忆表和检索请求都强制带user_id查询条件里永远加上。但光有字段不够我在编写检索代码时专门封装了一个MemoryScope对象每次调用都传入user_id和scope_type比如private、team、global在入口处做权限校验。这样即使后来加多Agent协作也不会因为漏传user_id导致串数据。另外会话级记忆也要和用户级记忆分开。用户切换会话时不应该把上个会话里所有内容都带入新会话。我的做法是每个会话有一个session_id高时效记忆和临时上下文存在session级只有显式标记为长期偏好的内容才会提升到user级。4.2 检索质量差召回一堆没用的旧账另一个常见问题是召回结果看起来相似但完全不是用户当前关心的内容。比如用户说“帮我找一下之前那份市场分析”系统召回一堆所有包含“市场”的聊天记录但真正的那份文档没有召回。原因出在三方面Embedding模型选型不当。通用Embedding对专业领域法律、医疗、代码效果差我后来换成了针对领域微调的Embedding模型召回明显改善。缺少Metadata过滤。没有按文档类型、项目名、时间范围过滤导致向量检索只能靠语义相似度去碰运气。我建议在写入时就把project_id、doc_type、tag这些元数据标准化检索时先在Metadata层面做粗筛。缺少Rerank。向量召回top50直接取前5质量很不稳定。我加了一个轻量级Rerank步骤用交叉编码器对50条候选重新打分只取前5精确率和用户满意度都上来了。4.3 executor terminated due to error记忆读写导致Agent中断调试Agent时经常看到一句话agent execution terminated due to error。这个错误提示笼统但如果Agent框架里集成了记忆组件我第一个排查方向就是记忆读写。最常见的坑是session_id或memory_id传了非字符串类型比如传了None或整型向量库客户端直接抛异常。第二个坑是向量维度不一致模型升级后Embedding维度变了新写入的向量和旧向量无法比较检索时报维度错误。我的排查步骤是查Agent日志里记忆组件的调用链看是写入、检索还是更新环节报错。检查传入的user_id、session_id是否为空。检查向量库collection的维度配置和当前Embedding模型输出维度是否一致。如果是网络问题比如连接向量库超时给记忆组件的调用加重试和熔断避免单次记忆失败拖垮整个Agent执行。4.4 Token膨胀与上下文爆炸记忆回填太猛同样会导致Agent报错。场景是这样的我一开始把召回的所有记忆拼装进System Prompt结果一次任务召回了20多条记忆每条100~200字再加上工具调用和对话历史直接把上下文顶到上限。现在我的上下文构建会分区块系统指令、用户画像只放永久记忆、当前任务上下文短期记忆、相关历史记忆最多3条、工具结果。每个区块有token预算超过预算就截断或摘要。我还要在每次构建前估算token工具调用和回复都留出余量。如果预算不够优先压缩历史记忆而不是压缩系统指令。实操心得给记忆组件设置超时上限比如每次检索不超过200ms否则一次慢查询会让用户感知到整个Agent变卡。记忆属于外部依赖外部依赖的延迟和失败必须被包裹起来不能直接暴露到用户交互路径上。5. 进阶多Agent协作、记忆安全与评估5.1 多Agent共享记忆的粒度与一致性多Agent场景下记忆不再是单用户私有还涉及协作Agent之间的共享。比如一个Agent负责调研另一个Agent负责写报告调研Agent的结果要能被写报告Agent读取。这里需要处理三个问题可见性、写入权限、一致性。可见性用scope_type区分私有记忆只有所属Agent或用户可见团队记忆可被同项目Agent读取全局记忆由管理员维护。写入权限也很关键默认情况下Agent只能写自己的记忆团队记忆需要显式授权避免A Agent覆盖B Agent的数据。一致性问题更隐蔽。多个Agent同时更新同一条用户偏好会出现“后写覆盖先写”的丢更新风险。我用的方案是在记忆数据模型中增加updated_at和lock_version字段更新时先比较版本号版本不一致就重新读取再合并。这很像数据库乐观锁不是新东西但在记忆组件里极其实用。5.2 记忆安全不该被记的别记记忆组件天然会积累用户隐私所以安全设计不能事后补。我强烈建议在写入管线上加三道闸敏感信息检测邮箱、手机号、身份证号、银行卡号一旦检测到就用占位符替换或者直接不写入。这个可以用正则加上NER模型实现。脱敏存储如果业务确实需要记录部分敏感信息比如用户ID要加密存储检索展示时再做脱敏。记忆访问审计谁在什么时间访问了哪条记忆都要有日志。多Agent环境下尤其重要否则出了数据问题你都找不到源头。我见过团队上线后才发现记忆组件把用户的完整错误日志都存了里面包含API密钥。这类事故非常危险。现在我的做法是写入前先进行密钥检测用正则匹配sk-开头的字符串命中的内容一律不进记忆库。5.3 怎么评估一套记忆组件好不好最后聊一下评估。我一般从三个维度看记忆组件的效果召回准确率、记忆新鲜度、遗忘准确率。召回准确率可以离线评估构造一批历史问答给定问题看系统能否召回正确答案相关记忆计算Hit5。记忆新鲜度看用户偏好变化后旧记忆能否在下次召回中排除。遗忘准确率看过期记忆是否真的不再出现。这些指标不能只看整体数值还要按用户维度切分因为Agent记忆最终是为单个人服务的。我还建议做A/B测试同一批用户一组有记忆组件一组没有对比任务完成率、用户主动纠错次数、回访满意度。很多项目觉得记忆组件是锦上添花但实际测下来加了记忆的Agent在持续任务上的完成率能高出不少。最后再分享一个个人习惯我到现在做新Agent项目时第一版还是会坚持用最简单的SQLite加手工召回逻辑先跑通再换专门的向量库。别急着上复杂架构记忆组件最核心的问题不是“用什么库”而是“什么该记、什么该忘”。想明白了这两点其他都是实现细节。踩过几次坑之后我越来越觉得真正好用的记忆组件不是在后台悄悄塞给你一堆数据的黑盒而是能让你明确感受到“这个助手是真的记住了我的事”。希望这篇能帮你少走点弯路。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。