# 我做了个会遗忘的 AI 记忆,召回率反而从 8% 涨到 100%

先说结论:让 AI 忘记,比让它记住更难,也更有用。

起因

我给一个聊天机器人做了记忆功能。第一版很简单:把最近 N 条对话塞回去,再加个向量库检索。

结果很糟:

  • 聊三天,它就不记得我说过腰不好
  • 问"我老家哪的",它答"达州"------但那是我一小时前说的
  • 更糟的是,它开始重复问同一件事

我去查数据,发现一个问题:它的"记忆"全是摘要,原话早就没了。

换个思路:记忆不是索引,是状态

主流方案(向量库、Mem0 这类)做的是:

复制代码
对话 → 切块 → embedding → 相似度检索 → 塞回上下文

它解决的是"从过去里找相关片段"。

但我要的不是找片段,是**"她现在怎么看我"**。

这两件事不一样:

检索索引 状态
所有片段一样新 有时间、有强弱
只增不减 会过期、会覆盖
解决"找到" 解决"记着什么"

所以我改成存结构化状态:

json 复制代码
{
  "facts":     [{"x": "他腰不好", "t": 1792976468000}],
  "events":    ["2026-10-26 ★ 生日蛋糕很好吃"],
  "thread":    "在说他晚上四节课",
  "wants":     ["想让他早点睡"],
  "evaluation":"他嘴上说没事,其实很累。"
}

九层,每层有不同的寿命:

层 寿命
当日原文 24 小时
短期(在聊什么) 几轮重写
事实 30 条上限,满了淘汰最弱
事件 只增不删,细节会糊
长期(关系转折) 永不模糊
看法(对他的判断) 永不模糊
叙事(关系史) 每 5 天主动变糊

遗忘不是删除,是降精度

这是整个设计里最反直觉的部分。

所有记忆系统的 KPI 都是召回率------没人主动往上钉降精度。但我要的是"像人",不是"像数据库"。

三层遗忘:

1. 强度淘汰

事实到 30 条时,算强度:

复制代码
强度 = 被提起次数 × 2 − 多久没提 × 0.5

最弱的先忘。常聊到的事(老家、考研、作息)永远忘不掉;只说一次的琐事会自然消失。

2. 叙事模糊

每 5 天把关系史重写一档更糊:

复制代码
「他答应带我去吃火锅」 → 「好像答应过要带我吃什么」

具体细节变成印象,情绪留下来。

3. 年龄标注

注入时标"23 天前,只记得大概",让她自己决定说得含糊。

写入协议:只增不删

这是踩过最大的坑。

第一版是"整段重写"------模型看最近 16 条对话,输出更新后的记忆。结果:记忆库三天从 25 条掉到 2 条。

因为没被提到的旧事实,模型就"忘了"写进去。

改成显式协议:

json 复制代码
{"add": ["新事实"], "drop": [要删的编号], "replace": [{"i": 3, "to": "改后"}]}

代码侧合并,模型只能"点名删除"。宁可留着,也别悄悄没了。

日终固化:不边聊边提炼

白天只存不提炼,第二天把前一天过一遍再固化。

因为白天你不知道这天最终走向哪。一句随口的话,到晚上才变成正事。

流程:

markdown 复制代码
白天 → days/2026-10-26.md(原话,不处理)
次日 → 分拣一次
        ├→ 长期库(值得记一辈子)
        ├→ 事件库(重要但不至于一辈子)
        ├→ 事实库(关于他的新信息)
        └→ 叙事(这一天压成一句)
       然后删掉原始档案

删原文是关键。 留着,她随时能检索到细节,模糊就形同虚设。

组装:分层 + 预算裁剪

每次调用前,按优先级拼九层,超预算就从低优先级砍:

yaml 复制代码
4000 token → 9 层全装(实际只用了 528)
 400 token → 5 层(丢掉事件、话题、念头、看法)
 120 token → 3 层(只剩:什么关系 / 在聊什么 / 想要什么)

意义:预算再紧,她也不会退化成客服------可能忘了你的老家,但仍然知道"我们很熟"和"我在等你下课"。

检索:30 行,零依赖

js 复制代码
// 字符二元组重叠 + 时间加权
const score = hit / Math.sqrt(g.size);
scored.push({ ...a, score: score * 0.85 + recency * 0.15 });

不用 embedding:中文不需要分词、零延迟、零成本。

这是取舍------我要的是"够用的检索 + 强的淘汰",不是"最强检索 + 弱淘汰"。

数据

44 次模型调用,关键词打分,三组对照:

类别 无记忆 最近 3 轮 本方案
事实 0% 67% 100%
事件 0% 67% 100%
未完成事项 0% 67% 100%
冲突更新 50% 50% 100%
关系细节 0% 100% 100%
总体 8% 77% 100%

代码:379 行,零依赖。

两个坑

1. 记得住 ≠ 说得出

冲突更新一度卡在 50%,但不是记忆错了------存储里两条事实都合并对了。问题是她为了耍脾气把问题绕开了。

改法不是改记忆,是加一句指令:问到你记得的事,先把记得的说出来,再反应。

2. 功能写了 ≠ 接上了

我加了三层库(长期/看法/感情值),代码都写好了,但注入语句挂掉,等于白做两天。

局限

  • 每类只有 3 题,波动 ±15%,样本太小
  • 只测了一个模型
  • 没有向量检索,语义召回弱
  • 没有多模态记忆

最后

这套东西最核心的一句话:

记忆的寿命不是由"存了多少"决定的,是由"忘了什么"决定的。

仓库:gitee.com/coco123/memcore(欢迎 star)

相关推荐
Rain的Java大神之路1 小时前
面试被LeetCode 27(移除元素)问懵?别再用两层 for 循环了:一道“简单题”吃透双指针原地删除,附面试加分话术 + 11 个避坑清单
后端·leetcode·面试
利威尔xu2 小时前
Go IM 源码实战(11):token 只是个 HASH,多端互踢的顺序错了就踢错人
后端·微服务·面试·架构·golang
猿究院--Cu-Sn合金2 小时前
Spring Boot + DeepSeek 求职 Agent 实践:工具注册、多轮调用与简历 JD 对比
数据库·人工智能·spring boot·后端·deepseek
程序员cxuan2 小时前
别学 subagent 了,很可能要凉了。
后端·agent·ai编程
小咕聊编程2 小时前
基于Django的个人摄影博客网站设计与开发
后端·python·django
未秃头的程序猿3 小时前
@ConditionalOnMissingBean 把我配好的 RabbitMQ 手动 ack 吃掉了
后端·rabbitmq
回家路上绕了弯3 小时前
当 AI 成为队友:团队协作使用 AI 的实践与思考
后端
陈随易3 小时前
副业搞钱点子之独立开发
前端·后端·程序员
MetaLite3 小时前
Spring Boot 加了 @Valid,嵌套参数为什么还没校验?
java·spring boot·后端