资讯动态

给AI Agent装上长期记忆:Hindsight记忆系统设计与实践

发布时间:2026/10/13 13:55:55 来源:尧图企业网站定制
说实话Agent 这个方向最让我头疼的不是模型能力不够也不是工具调用写得不顺而是它真的记不住事。你今天跟它聊过的偏好、喜欢的时间格式、上次项目的结论一到新会话就归零。Hindsight 就是我最近在开源社区里翻到的一个项目专门给这类“健忘”的 Agent 补上一套长期记忆系统。它不是简单做个向量库就完事而是把记忆当成一个完整的系统来设计写入、检索、遗忘、巩固都有相对成熟的方案。这篇文章我就围绕设计与实现把里面真正值得学的东西拆开讲也把我实际调试中踩过的坑一并晒出来给想给 Agent 加记忆的朋友一个能直接上手的参考。1. 先把 Agent 失忆的问题想清楚1.1 Agent 不是缺上下文而是缺记忆很多人一开始会觉得Agent 记不住事那不是因为上下文窗口不够吗把所有的对话历史都塞进去不就行了我一开始也这么干过。结果就是钱烧得快、响应慢而且最关键的是塞进去的 90% 都是没用的闲聊和重复信息真正关键的偏好和结论反而被淹没在大段历史里。上下文窗口是“工作台”不是“仓库”。人脑做事情从来不会把从小到大的经历全部倒带到眼前而是只把当前任务需要的那部分记忆调出来。Hindsight 的核心思路就是这个它给 Agent 装了一个“长期记忆层”把值得记住的信息抽取成结构化条目存下来需要的时候只把与当前问题相关的几条记忆捞出来塞进上下文。这样既控制成本又提升准确率。这个思路和普通 RAG 还不一样。普通 RAG 是把知识库拆成片段靠向量相似度去匹配适合做“查找资料”。但 Agent 的记忆更像是一个“个人履历”里面有用户偏好、历史决策、任务进度、踩过坑的教训。这些内容很多都带有时效性有的重要有的会过期。如果只是粗暴地建成向量库就会出现“昨天的过期结论今天还在干扰判断”这种问题。Hindsight 给出的解法值得参考它把记忆按生命周期和层级拆开不同种类的记忆用不同方式处理形成一个有“新陈代谢”的系统。这看起来比单纯套 RAG 复杂但踩过坑之后你会明白这一步省不掉。1.2 Hindsight 的记忆分层设计工作、情景、语义、程序读这个项目的源码时最吸引我的就是它借鉴了认知科学里关于人类记忆的分层方式没有把所有内容一锅端。它的分层结构大致是这样的工作记忆也就是当前的会话上下文、正在处理的临时信息。它在任务完成后就清空或转储。情景记忆记录具体发生过的事件比如“上周三用户要求把报表改成每周一凌晨三点跑”。语义记忆从情景里提炼出来的一般性知识比如“用户偏好每周一接收报表”“用户喜欢用中文术语而非英文缩写”。程序记忆也被称为技能记忆记录的是“怎么做”的经验比如“处理登录失败时先查 token 再查网络”。这个分层最大的好处是查询的时候可以按类型过滤。比如 Agent 在执行一个报表任务时优先捞语义记忆和程序记忆不用把大量早期情景翻出来。从工程实现上看它也方便做差异化处理情景记忆可以实时新增语义记忆要定期合并去重程序记忆的写入则要更谨慎因为技能类的东西错了会反复出错。我在自己实现时一开始没搞这么复杂只做了个“全部向量化入库”结果量大了之后检索质量直线下降。后来参考这种分层把数据表拆开再加上类型标签才真正解决了问题。所以说分层的意义不光是理论漂亮它对检索质量、存储成本和维护效率都有实打实的影响。1.3 为什么不用“一股脑全存”的笨办法我知道你可能会想既然要做长期记忆那不是存得越多越好吗真不是。存得越多噪音越多检索时命中的垃圾越多最终回答就越容易出错。Hindsight 在写入阶段就做了关键动作过滤和提炼。它不会把你和用户整整两小时的闲聊全存下来而是用模型从中抽取“有记忆价值”的信息比如用户明确表达过的偏好、承诺过的截止时间、任务的重要转折点等。这个思路本质上是在做“记忆的选择性编码”。你可以把它类比成写日记写日记不是把每分钟的流水账都记下来而是记下这天真正值得记住的事。AI 做这件事比人还更需要克制因为存储和检索成本都是真金白银而且垃圾记忆会让整个系统变傻。所以在动手之前第一件事不是选数据库而是想清楚一个问题什么信息对你的 Agent 来说值得记住这个问题想不通后面所有模块做得再漂亮都是白搭。Hindsight 的做法是提供了可配置的抽取规则和 Prompt 模板让开发者根据自己的领域自定义“值得记住”的判断标准。这一点我强烈建议你不要偷懒直接用自己的业务场景跑几轮你会发现默认规则往往不够贴合。2. 核心机制拆解写入、检索、遗忘是怎么协同的2.1 写入侧不是所有对话都值得记住Hindsight 的写入管线本质上是一条异步流水线原始对话 → 抽取判断 → 格式化 → 向量化 → 入库。它刻意把写入和读取分开不让记忆的存储阻塞对话主流程。具体拆开看它的关键步骤有三个第一步抽取。系统会拿着当前这一轮或一轮批量对话交给语言模型让它判断这段对话里有没有值得长期记住的信息。同时会给一个分类标签是偏好、事实、任务、还是技能。我见过一些简化实现是直接从原文里抠句子不走模型但效果很差因为用户表达意图时往往很口语化直接截取很难形成清晰的知识条目。第二步去重和归一化。存入前会和已有记忆做一次相似度比对如果发现类似的条目已经存在了就合并或跳过。这一环节很多人忽略结果就是跑一个月之后库里躺了几十条互相矛盾或高度重复的“用户喜欢简洁回答”。第三步重要性打分。这一步直接决定了后续的遗忘策略怎么走。Hindsight 里每个记忆条目都带一个重要性分数这个分数会随时间衰减。比如一个重要度 0.9 的偏好可能三个月后衰减到 0.4如果期间没有再次被强化就会被清理掉。而有些信息虽然重要但如果用户已经明确表示“这个不用了”就会被打上失效标签。写入侧还有一个容易被忽略的细节时效性字段。每条记忆都要记录“创建时间”和“最后访问时间”甚至有的要显式标注“有效截止时间”。没有时间戳的记忆系统基本没法做长期维护。我的建议是就算你不想一开始就实现遗忘机制也务必把时间字段先留好否则后续想加就得迁移数据。2.2 检索侧相关性、近因、重要性一起打分记忆写进去了关键的是能查出来。Hindsight 的检索不是单纯向量相似度而是算一个综合分数。我的理解是这样的最终得分 向量相似度 × 权重1 近因加权 × 权重2 重要性分数 × 权重3其中近因加权的典型实现是按时间衰减来算的比如公式权重 (0.5) ^ (年龄天数 / 半衰期)假设你设定半衰期是 30 天那么一个 30 天前的记忆权重就是 0.5一个 60 天前的就只剩下 0.25。这样做就是为了防止“旧记忆固执地占据注意力”。重要性分数则类似一个“信息价值标签”保证像“用户对某个知识点有过敏反应”这类信息即使时间久远也能被捞出来。检索的流程一般是两段式先在向量库里做 ANN近似最近邻召回候选集例如取 20 条再用上面的综合打分函数精排取前 5 到 8 条。这套方案的核心价值在于如果把所有历史记忆全塞进去打分性能上扛不住而向量召回能快速缩圈精排阶段又有足够时间做复杂计算。我在实际项目中踩过的坑是只做向量相似度时检索结果经常只追着字面相似跑。用户说“我又不喜欢那种风格了”系统查出来的却还是以前“喜欢某种风格”的记忆因为字面上有很多重叠。加了时间和重要性加权之后问题就减弱了不少因为新写入的“不喜欢”在近因权重上碾压了旧记忆。2.3 遗忘与巩固记忆的“新陈代谢”遗忘这件事听起来像是系统的缺陷但其实是长期记忆系统里最需要主动设计的能力。如果只存不删存储成本逐年上涨是小事真正致命的是陈旧错误信息对 Agent 判断的持续污染。Hindsight 实现了一套类似人脑记忆巩固的机制。它有两种操作一种是衰减一条记忆的权重随着时间降低另一种是再巩固当同一条记忆被反复检索或关联到时它的热度会提升半衰期会被拉长相当于“这个信息一直在用所以它更重要”。这背后的逻辑就是记忆不是越老越不重要而是“被用到的经验会反过来强化自己”。具体操作上系统会周期性地跑一个批次任务筛选出衰减到阈值以下的记忆做一次复核。复核的方式通常是让模型判断这条记忆是否已经被新记忆覆盖是否仍然有参考价值如果确认无效就归档或删除。这个任务不用很频繁每天跑一次就够。你可能会担心万一删错了怎么办Hindsight 的做法是软删除也就是先打上 archived 标记不参与检索保留在独立表里。如果后续发现某条被删的记忆很重要还能通过管理接口恢复。这套设计非常实用我在调优时经常手动把误删的东西捞回来所以强烈建议你在实现时也给删除留后路不要物理删除。3. 动手实现一个缩短版 Hindsight3.1 技术选型与基础架构Hindsight 本身的实现选用了一些常见技术栈但核心思路并不绑定某一家我用一套更通用的方案也能复刻出主要功能。推荐的最小可行组合是一个支持向量检索的数据库或本地向量库一个关系型数据库或文档库存记忆元数据一个增强语言模型做抽取和摘要一个异步任务队列跑写入和清理注意向量库和元数据不要混在同一个表里。向量库负责相似度搜索元数据负责存重要性、类型、时间等结构化信息两边通过记忆 ID 关联。这样做的原因是向量库对结构化过滤支持通常较弱而关系库做复杂查询更强。混合查询时先从向量库召回候选 ID再去关系库里做过滤和排序。数据模型上一条记忆的最小字段可以这样搞CREATE TABLE memory ( memory_id TEXT PRIMARY KEY, agent_id TEXT, memory_type TEXT, -- episodic / semantic / procedural content TEXT, importance REAL, -- 0.0 - 1.0 created_at DATETIME, last_accessed DATETIME, archived INTEGER DEFAULT 0 );再加上一张用于向量检索的 collection 表每条记录对应一个向量。两边用 memory_id 关联查询时先向量召回再带条件过滤。3.2 记忆写入模块先抽取再入库我做一个简化版写入函数的思路是这样的def write_memory(agent_id, dialog): # 1. 抽取判断这轮对话是否有值得记住的信息 extraction llm_extract(dialog) if not extraction.is_valuable: return # 2. 格式化把抽取结果变成结构化条目 memory_item MemoryItem( agent_idagent_id, memory_typeextraction.memory_type, contentextraction.content, importanceextraction.score ) # 3. 去重检查库中是否已有语义相似的记忆 embedding embed(memory_item.content) similar_ids vector_search(embedding, top_k1) if similar_ids and similarity 0.9: merge_or_skip(memory_item, similar_ids[0]) return # 4. 向量化并入库 vector_db.insert(memory_id, embedding) sql_db.insert(memory_item)这里的抽取 Prompt 很关键需要明确告诉模型提取用户明确表达的偏好、承诺、重要事实忽略寒暄和临时性讨论。我给过一版效果还不错的 Prompt 核心句大意就是“以下是一段对话请判断是否存在应当长期记住的信息。如果有请输出信息类型、内容摘要、重要程度 0 到 1。如果都是无关紧要的闲聊输出 IS_VALUABLEfalse。”实测中一个重要心得是不要追求抽取一次成功。宁可少抽不要误抽。一条误入库的错误记忆侵害力远大于两条漏掉的记忆。因为漏掉的只是“没记住”而错误记忆是“记错了”它会反复污染后续所有决策。3.3 检索增强模块把记忆注入 Agent 的 Prompt检索侧的实现思路在我看来更接近工程核心。整个过程可以拆成三步触发判断、候选召回、精排注入。触发判断的意思是不是每一轮对话都需要访问记忆库。如果用户只是在闲聊或者当前上下文已经足够就没有必要多此一举。Hindsight 有一个轻量判断逻辑如果当前上下文中已经包含足够的信息以支撑回答就跳过检索。候选召回的精简写法def retrieve_memories(query, agent_id, top_k8): # 1. 向量召回 query_embedding embed(query) candidates vector_db.search(query_embedding, top_k30) # 2. 精排计算综合分数 ranked [] for cand in candidates: near_weight 0.5 ** (age_days(cand) / 30.0) importance cand.importance final_score 0.6 * cand.similarity 0.3 * near_weight 0.1 * importance ranked.append((final_score, cand)) # 3. 过滤低质量结果并取 top filtered [c for c in ranked if c.similarity 0.75] return [c for _, c in sorted(filtered, reverseTrue)[:top_k]]注入时我通常会在 system prompt 里加一个“记忆上下文”区块把这些记忆条目逐条列清楚并且标明时间和类型。例如以下是从长期记忆中找回的相关信息请参考但不盲从 [情景记忆 · 2025-02-10] 用户明确表示不接受销售电话推销。 [语义记忆 · 2025-03-01] 用户偏好在周一早上收到周报。这里有一个很深的体会检索结果除了要带内容一定要带“元信息”。如果 Agent 只看到内容看不到时间、来源、类型它可能会把一条去年的事实当作现在的用户状态来用。加上了时间字段模型至少有机会判断“这条信息可能已过时”。另外聪明的做法是让 Agent 拥有“怀疑记忆”的权利也就是 prompt 里写清楚如果记忆与当前对话直接冲突以当前对话为准。3.4 参数调优半衰期、阈值、TopK 怎么定参数这个东西不可能有放之四海皆准的值但可以给一组我实测下来比较稳的起点向量召回量30 条。太少了会漏掉合适候选太多则精排压力大。精排输出数TopK5 到 8 条。超过 8 条模型处理起来会很累效果反而下降。相似度阈值0.75。低于这个值的大概率是不相关内容放进 prompt 只会制造噪音。时间衰减半衰期30 天适合一般助手型 Agent。如果是做客服7 到 14 天更合适因为客服场景里用户近期诉求的权重远大于半年前的诉求。重要性分数权重0.1 到 0.2 够了。目的是让“重要且旧”的信息能被捞出来但压制作用不能太强。调参的时候我的建议是先固定住 TopK 和阈值只是去调半衰期同时观察两个指标记忆命中率检索出来的记忆有几次被最终回答真正用到和记忆污染率几条记忆因为过时导致回答错误的比例二者一般会形成一个权衡。半衰期太长污染率高太短命中率低。做一组灰度实验挑一个让两者都相对能接受的值就行。这个过程没有捷径但一次调好之后系统能稳定跑很久。4. 跑起来之后踩过的坑4.1 检索命中率低问题出在阈值和模型第一次跑通系统后我发现了一个尴尬的情况明明库里已经有用户说过的偏好可查询时死活召不回来。排查后原因有两个。一个是相似度阈值设太高。我最初设了 0.85结果很多语义相关但字面不匹配的条目都被过滤掉了。这个问题的本质是embedding 模型的相似度分数分布和你想象的不一样有的模型 0.7 就已经是高度相关了。我的做法是跑一批真实样本统计“人工判断为相关”的那些条目的相似度分数分布再决定阈值而不是拍脑袋定。另一个原因是 embedding 模型太弱或者说它没有经过针对性的微调。如果你做的领域名词很专比如医疗术语、工程材料、法律条款通用 embedding 模型的召回效果会明显不够。这时不要急着换系统先花时间对比两三个领域内表现更好的模型看它们在同样数据集上的召回率表现。我换过一次模型之后命中率提升了接近一倍。4.2 记忆污染昨天的事在今天还在捣乱记忆污染是长期记忆系统里最让人抓狂的问题。表现出来就是用户昨天说“暂时不要执行自动部署”今天说“现在恢复自动部署”系统却因为昨天那条记忆的残余影响回答得特别犹豫。这个问题的根源在于旧记忆没有被及时标记为失效或者新记忆写入时缺少冲突检测。Hindsight 的做法是在写入新记忆前先把相关旧记忆找出来如果类型相同、主题相似、语义相反的就把旧记忆标记为 deprecated。这个步骤用一次向量检索 一个简单的语义冲突判断就能实现。我在自己实现时加了一个补充手段在检索结果里加入显式的时间衰减提示。当一条记忆的年龄超过一定值但重要性又高所以被召回了我会在 prompt 中给模型加一句提示“这是一条较旧的记忆请确认是否与当前情境仍然一致。”这个做法模型很吃这一套能显著降低过时记忆误用概率。4.3 存储膨胀与重复记忆跑了两个月之后我统计了一下记忆库里的数据量涨得比我预期快很多。打开明细一看里面有大量重复内容同一件事被抽取了三四次只是表述略有不同。这不仅占了空间还会在检索时频繁霸榜把其他有价值的记忆挤下去。这个问题需要在写入侧加一道更严格的去重。具体办法是在向量召回阶段除了召回过候选中同一条记忆之外再额外检索“重复率最高的一条”把相似度达到预设阈值的条目拦下来。如果相似度很高但想保留后者可以合并两条记录增加后者的重要性分数同时把旧的那条标记为 merged。另外我调整了抽取时机从“每一轮对话都尝试抽取”改成“一个任务或主题收敛后再批量抽取一次”。这样能大幅减少同一件事被反复写入的次数。虽然抽取实时性差了点但换来的是记忆库整洁度大幅提升很值。5. 再往后走记忆系统的进化方向5.1 从单机记忆到群体记忆单个 Agent 的记忆做得再漂亮也只是一个人的记忆。实际业务中你会发现有很多场景是一个团队/多个 Agent 共享一套集体经验。举个很简单的例子客服系统里不同座席 Agent 处理不同用户但“某个产品型号最近频繁出现某类故障”这种经验应当是共享的。Hindsight 的架构里本身就把 agent_id 作为了记忆表的重要维度这给多智能体场景留了口子。进一步做扩展时可以引入记忆的可见性和权限个人记忆只有自己能读写团队记忆有一份公共视图大家都能检索和贡献但写入要经过更严格的审批。这个设计有点像“个人笔记”和“团队 Wiki”的区别做得好协同效率提升非常明显。这部分我自己还在摸索。目前采用的折中方案是抽出语义记忆和程序记忆的上层部分定时汇总成团队记忆情景记忆保持个人隔离。既保证个性化又让经验可以横向流动。5.2 记忆可视化与用户反馈闭环长期记忆最大的隐性问题是用户不知道系统记住了什么不知道哪条记错了也不知道怎么纠正。这会导致用户对系统的信任一点点流失。我在研究过程中越发觉得记忆系统一定要“可查看、可纠正”。具体来说前端可以做一个记忆面板分类型展示当前智能体记住了哪些信息每条记忆后面附带“删除”和“纠正”按钮。用户纠正一条记忆之后系统不只是改内容还应该触发一次相关性级联调整类似主题的记忆的权重降一点纠正后的条目权重提高。这个闭环比任何算法优化都管用因为来自用户的显式反馈是最强的强化信号。Hindsight 在这方面提供了基础的管理接口但没有做成完整的可视化前端。不过它的数据模型设计得比较干净接一个简单的管理界面并不难这算是我认为最值得继续投入的方向。5.3 我对长期记忆的实践建议最后聊一点宏观层面的体会。做完了这套系统我对“Agent 记忆”这件事的看法变了很多。以前总觉得记忆是锦上添花现在认为它是一个合格 Agent 的基础设施。对比一下就知道一个带了长期记忆的客服 Agent 和一个每次会话都从零开始的客服 Agent前者的体验完全是另一个层级。但在实操层面我的建议却是“克制”。不要把记忆系统一开始就设计得特别庞大最好先跑一个只支持“用户偏好 重要事实”的最简版本观察它能不能稳定工作三个月。稳定之后再加入技能记忆、记忆共享、自动清理这些复杂能力。系统的复杂度是滚出来的不是设计出来的。要是开头就堆全套遇到问题你根本定位不了是哪里出的错。还有一个很值得记住的细节记忆系统必须能“被遗忘”。如果一个系统只进不出它最终会被信息垃圾淹没。设计时先把遗忘机制想好比把存储容量买大有用得多。人脑之所以记忆靠谱恰恰是因为它擅长忘记。Agent 也要学会这件事。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑