上一篇讲的是怎么把大肥鱼部署进群。这一篇讲她脑子里的那部分——不是人设,是记忆。
人设回答“她是谁”,记忆回答“她记得什么”。这两件事经常被混在一起,但工程上完全是两条路:人设是一段固定的提示词,每次唤醒原样塞进去就行;记忆是每天都在长、会互相矛盾、需要被整理和取舍的东西。前者的难点在措辞,后者的难点在怎么保证不丢、不错、不越权。
记忆 v3 是我给桥接写的第三版记忆层。前两版都死在同一个地方:以为“把聊天记录喂给模型”就等于“模型记住了”。第三版换了个思路——先把事实存成不可变的经历,再把经历整理成可修正的记忆。这两层分开之后,大部分麻烦才有地方安放。
整篇文章就围绕这三层:最下面是只追加的经历,中间是记忆网络,最上面是召回后进入提示词的那几条。
先说一个不太好听的事实。
社区里有人做过一轮横向评测,结论是:一堆号称“解决记忆”的插件,没有一家打赢最朴素的“向量检索 + 重排”。我把这个结论当真——它大概率是对的,原因也不神秘:重排修的是“候选找对了但排序错了”,而这恰好是检索失败里占比最大的一类;大部分插件只是加了编排,没有加新的召回信号。
所以这篇文章不打算反驳它,只想回答另一个问题:在“向量 + 重排已经够用”的前提下,为什么还要做一套带证据链的记忆层,以及它在什么条件下才值得。
先说结论
整套东西只有一句话:一个本地的 SQLite 文件是唯一真相,其他一切都是它的投影。
- 聊天原文进“经历”表,只追加,不改不删;
- 整理出来的“记忆”进另一张表,每条带版本、证据、有效期;
- 向量索引、后台批次、注入的提示词,全都是从这两张表派生出来的,随时可以删掉重建。
这个约束听着朴素,但它决定了后面所有设计。写法上它其实是最经典的 event sourcing(事件溯源):状态不是被覆盖的,而是由一串不可变的事件推出来的。她昨天说错了今天改口,不是去覆盖原文,而是给记忆开新版本、给旧版本写一个结束时间;原文那一行永远在那儿,事后能查“她当时到底听到了什么”。
数据库是单文件,进程内打开,WAL 日志模式。没有向量数据库,没有外部服务,没有缓存中间件。一台家用机器上跑一个 QQ 机器人,这个规模用不着那些东西。
第一层:经历
每条消息进来,桥接先做一件事——归档成一条经历。
从一条消息到一句回答。关键在于中间那三段是解耦的,任何一段坏了都能单独重放。
几个刻意的选择:
编号自增,内容寻址。 归档游标走自增编号(单调递增);去重走消息内容的指纹(通常是消息 ID)。补拉历史时同一条消息会被拉好几次,靠指纹挡掉——这就是内容寻址去重的老办法。游标必须单调,所以不能用指纹。
只追加(append-only)。 归档路径里没有任何 UPDATE 或 DELETE。消息写错了也要能查出“当时那条是什么”——这是后面所有“记忆不能伪造”的前提。真需要清理,是单独的分区/归档方案,不是靠改行。
标来源。 实时收的、桥接停机后补拉的、从旧记忆层一次性搬过来的,三种来源分开记。正文质量不一样,混在一起会让“这条到底是不是她亲耳听到的”说不清。
附件单独记。 图片和表情按内容哈希(SHA-256)去重存盘,同一张表情被发一百次只存一份文件,但一百条经历各留一行引用。语音、视频、文件目前不归档——这是明确的边界,不是遗漏。
到这一步,系统还只是个“记性很好的录音机”。真正难的是下一步。
第二层:记忆
经历是流水,记忆是结论。把流水变成结论的过程,我管它叫整理(consolidation)。
一条记忆大概有这些维度:它属于哪个会话或哪个人、它是什么类型(事实 / 偏好 / 事件 / 关系……)、它的正文、它的重要程度、它是否还有效、以及它从什么时候开始成立、到什么时候结束、它当前是第几版、最后一次修改是什么时候。
重点是三件事:
类型。 不同类型召回时的权重和呈现方式不一样。问“她喜欢什么”和问“上周三发生了什么”,走的不是同一条路。
有效期(bitemporal 的简化版)。 记忆不是“真/假”的二元,是“在某个时间段内成立”。她三个月前养猫、现在不养了,两条都是真的,只是区间不同。写结束时间而不是删行,历史才查得回来。
状态。 有效、已撤销是两回事。撤销表示这条被明确否定了,召回时不再出现,但证据链还在。撤销 ≠ 删除。
配套还有三组东西:版本(每次修改留一条“改之前是什么”)、证据(这条记忆是从哪几条原始经历推出来的)、关系(记忆之间的支持、相关、矛盾、替代、合并)。
证据这一项是硬要求,也就是常说的 provenance(来源追溯)。没有证据的记忆是不合格的,她说的每一句话都要能往回指。这条规矩听起来很重,但它决定了整个系统能不能被信任。
另外还有一张别名表,专门治“群里同一个人换了三次昵称”这种问题。别名是显式映射,不靠模型猜。
这套设计的心脏:两条水位线
如果只能保留一个设计,我留这个。
会话有一个整理游标,指向“已经整理到哪一条经历了”,本质上是一个 watermark(水位线)。听起来简单,但实现里它是两条:
已交付 —— 这批正文已经交给模型了已确认 —— 模型明确说了“我看完了,结论是这样”两条线之间的那段,就是“发出去了但没确认”的部分。下一轮从这里重发。
为什么非要分成两条?
因为“把正文发出去”和“模型真的读懂了”是两件事,中间任何一步都可能断:DSH 卡死、回合超时、模型回了别的东西、进程被杀。如果只留一条游标,那么“发出去了”就等于“读过了”,一次失败就永久丢掉一段经历——而且是静默丢掉,日志里看不出任何异常。
所以现在的规则是:
- 唤醒开始时,把这次要交付的范围锁存下来(后面再进新消息也不影响这一批);
- 正文按批发送,一次默认四十条、六千字符封顶,“已交付”推进到本批最后一条;
- 模型整理完,必须显式确认——说出“整理出来了”或“这堆没什么值得记的”——才推进“已确认”;
- 回合正常结束不算读过。 模型没确认,下一轮从原位置重发;
- 桥接启动时扫一遍,把历史上被误推的游标退回到真实确认的位置。
第 4 条最反直觉。第一版我就是“回合跑完就推进”,结果一次模型只回了一句闲聊,那段经历就再也不会被整理。改成显式确认之后,最坏情况变成“同一批整理两遍”——这是一种幂等(idempotent)的失败:重复整理是可接受的,伪造“已处理”不可接受。
第 5 条是兜底。规矩定了,代码总会有旧版本、有边界情况,所以启动时对一遍账。原始事件一个字节都不动。
后台整理
如果只在被叫的时候整理,那机器人一天不被叫几次,记忆就一直是生的。所以有独立的后台整理任务。
它和前台聊天不是同一个东西:
- 它开独立会话,不带聊天上下文,不会把群里的气氛带进整理;
- 每批一个一次性令牌,只能访问几个受限的内部接口,只能读本会话的经历——最小权限原则;
- 绝不发送 QQ 消息。整理是内部动作,对外不可见;
- 每个会话同时只允许一个进行中的批次,状态机明确(排队 → 已交付 → 处理中 → 完成 / 失败);
- 全局串行:同一时刻只跑一个批次,跨会话排队。当前配置是每十分钟一批、每小时最多四批。
为什么全局串行?因为整理要调模型,并发几个批次只会互相抢带宽,而且出问题时完全分不清谁在干什么。串行的代价是吞吐有限,但目前真实进量远低于这个上限,没必要提前复杂化。真有哪天“待整理”长期堆到一百条以上,再谈按会话并发——那时候要考虑的就是背压(backpressure)怎么传递,而不是简单加并发。
分批还有个更硬的理由:上下文窗口本身有限。而且模型在长上下文里对中段信息的注意力会明显衰减——业内管这个现象叫 lost in the middle。一次塞两百条,不如分五次各塞四十条,后者的整理质量稳定得多。
还有个门槛,不是有积压就跑:至少十二条待整理才值得跑一批,但最多等两小时,再少也要整理。 这条是为了避免“每来两句话就整理一次”。太频繁的整理会把同一件事反复提炼,产生一堆近似重复记忆(近重复 / near-duplicate),反而伤召回。
每批都会记下模型这次的结论是“整理出了东西”还是“这堆不值得记”。后者也是合法结果——大量闲聊本来就不该进记忆,硬提炼只会让记忆库变成垃圾场。
失败的批次下一轮重试,因为游标只推进到确认的位置,重发是幂等的。运行期还有看门狗(watchdog):批次长时间不动,自动收成失败,下一轮重来。
召回:她是怎么“想起来”的
整理是写,召回是读。读这一侧比想象的难——你要在几百上千条记忆里,为当前这句话找出最多五条真正相关的,而且不能把无关的带上来。
四段依次收窄。前一段够用就不走下一段,向量永远排在最后。
现在是三个阶段,前一个不够才走下一个(向量旁路单独算一道闸,下一节说):
第一阶段,精确词。 全文检索,中文按二元组(bi-gram)切。命中就给分,但按词的稀有度折权——这是 IDF(逆文档频率)的思路,人人都有的词(比如每个群成员的名字)几乎不加分。第一版这里是每个命中词固定加两分,结果“小明最近怎么样”这类问句的前五名全被含人名的记忆占满,真正的答案被挤出去了。改成 IDF 折权之后,多词问句必须命中至少一个“有区分度”的实词才算数。
这里用的是 SQLite 自带的 FTS5,没有另外搭 Elasticsearch 之类的东西。规模在万级以下,FTS5 的倒排索引完全够用,运维成本是零。
第二阶段,近似语义。 完全不共享词面的改述,全文检索救不了(“点歌”和“唱一首”一个词都不重合)。这一层改用字符二元组的集合相似度,算余弦相似度(cosine similarity),再叠一层同义词扩展。仍然有门槛:至少一个非数字实词命中,否则两个都带日期的句子就能撞上。
第三阶段,图谱邻接。 沿着记忆之间的关系往外走一层,找“和上面这些记忆有关系”的记忆。只走一层,不递归——两层以上就是随机游走,出来的东西连解释都解释不了。
三阶段都带标记,这样事后能解释“为什么想起了这条”。
权限过滤在每个阶段之前完成,不是在结果上补——这是 fail-closed(默认拒绝) 的取向。会话可见性是一条硬边界:一个群的记忆不可能出现在另一个群的召回里。所有检索入口统一走同一个可见范围函数,没有例外。
RAG,我们是怎么理解的
先说清楚一件事:我们没有把“RAG”当成“向量检索”的同义词。
业界说 RAG(检索增强生成),默认指的是 切块(chunking) → 向量化(embedding) → 相似度 top-k → 塞进提示词。这条路对文档问答很好用,因为文档是静态的、事实是稳定的、切片边界是人为划的。但放到“个人记忆”这个场景里,它有两个硬伤:
第一,召回错了比召回不到更糟。 文档检索错了,用户能自己发现答案不对;个人记忆检索错了,机器人会非常自信地讲一件没发生过的事——这就是幻觉(hallucination),而且现场没人能立刻纠正她。所以我们的优先级是“宁可不说话,也不乱说”。用检索的指标说,我们在牺牲一部分召回率(recall)去换精确率(precision)。
第二,向量不知道“这是从哪来的”。 一个 embedding 只是一串数字,它没法告诉你这条结论基于哪几句原话、是什么时候听到的、后来有没有被推翻。而记忆系统如果丢了证据链,就退化成一本不可质证的小说。
所以我们的做法是:词法优先,语义补位,图谱兜底,向量只当候选。四种信号各管一段,谁都不独占。融合方式也不是简单的分数相加,而是分阶段串行——前一段的产出直接决定后一段要不要跑,天然避免了“不同量纲的分数怎么加权”这种永远调不好的问题。
(业界常用 RRF(倒数排名融合) 来合并多路召回,我在离线实验里也试过,效果不错;但它会强制每路都跑一遍,延迟和可控性都不如“按需进入下一段”,所以最后没有采用。)
这个结论我在开头提过一次,这里展开说。社区里那轮横评的失败方,问题不在“用了向量”,而在别的地方:
- 多出来的中间层没有带来新的召回信号,等于给同一条路加了几道收费站;
- cross-encoder 重排对“候选找到了但排序错了”的修复极其高效,而检索失败里这一类的占比最高,插件很难在这上面赢;
- 如果评测只看“问题答对没有”,知识库式的切片加向量天然占优,因为它优化的就是这一件事。
至于更深一层的原因,我看到的是两种东西被混为一谈:知识检索和记忆。前者的目标是“从一堆材料里找到相关段落”,后者的目标是“知道这个角色经历过什么、现在认为什么是对的”。前者用切片就够了,后者一旦切片,就丢了“这句话是谁说的、什么时候说的、后来改口没有”。
所以如果诉求是“把一堆文档喂进去,问什么答什么”,我的建议是别用任何记忆框架,直接上向量库加重排,简单、成熟、便宜。这话我放在这儿,是认真的,不是客套。
我们没走这条路,不是说它不好,是因为它解决的不是我们的问题。我们要的不是“答对一道题”,是“三个月后还能说清这条结论是从哪句话来的、当时是不是这个意思、后来有没有被推翻”。这个需求在纯 RAG 的评测表里根本不占格子。
顺带说一句“原文切片当记忆”这条路线。它确实简单:不丢信息、不占上下文,还能让模型自己给片段打标签。但它有个绕不开的代价——切片边界是人为的,一条被切成两半的话,既不能被修正,也不能被撤销,更没法表达“这条只在那两个月成立”。我们的做法是把原文完整留在经历层(信息同样不丢),在它上面单独长一层可修正的结论。切片的灵活换成了结论的可修正,这是两种不同的赌注。
向量旁路:加了,但只当候选
纯词法的天花板很明确:完全不共享词面的改述,命中率断崖式下跌。
2026 年 11 月我做了一轮本地 embedding 的离线 A/B:bge-small-zh-v1.5 的 int8 量化版,512 维,bi-encoder 结构,CLS pooling 加 L2 归一化,纯本地加载,不联网、不给主进程加依赖。在一百六十多条的真实记忆池里,改述档从五分之一提到了全中。看起来该直接上。
但误召压不住。 拿八条“库里根本没有这回事”的问句去测,向量第一名的余弦相似度落在 0.57–0.66,和正例命中的最低分 0.62 是重叠的。也就是说,不存在一个阈值能同时做到“该召回的召回来”和“不该召回的别上来”。
所以我把它做成了旁路候选,而不是替换主路径:
- 向量预编码走独立进程,索引单独存一份,不碰主库的字节;
- 只在词法零命中的时候才进闸门。词法有结果,向量完全不参与;
- 闸门还要求:有明确主体、有明确意图、分数不低于 0.68、第一名比第二名至少高出 0.02;
- 一次最多注入一条,并且明确标注「向量猜的,可能对错了人或事,还没确认」。
最后这条是产品决策,不是技术决策。让模型“想起”一条错误的事实,比什么都不想起来更糟。所以候选必须自带不确定性标签,让模型自己决定要不要用它说话。
顺带一提,我们没有上 ANN 索引(比如 HNSW / IVF)。几百到几千条向量,暴力算 cosine 也就毫秒级,引入近似最近邻只会换来一个“偶尔漏掉最优解”的新问题。等规模真上来了再说——而按现在的增长速度,那大概是几年后的事。
重排我认真试过。 在合成集上,它能把误召压到零,效果是真好。但它把 P95 延迟从个位数毫秒拉到三百六十毫秒上下,而我只给召回留了五十毫秒的增量预算。图记忆更贵——社区里有人估算过,一条信息的开销是传统向量记忆的好几倍。相比之下,本地 embedding 接近免费(电费除外),索引不到三兆,热路径 P50 只要零点零二毫秒。这些成本我一个人扛得起;那种“每次唤醒都跑一遍重排服务”的成本,扛不起。
这不是说重排不好。它只是把成本从“算力”换成了“预算”,而作为一个个人项目,预算比算力更早见底。
真实流量下这个开关叫 candidate。另外还有 shadow——只写日志、不注入,用来复盘。两个模式切换只改配置、不动数据。
目前的观察是:几百次进入闸门,真正被采纳的是零次。原因很实在——真实对话里大部分问句词法都能命中,进不了闸门;少数进了的,分数和边距都不够。所以阈值从 0.72 降到了 0.68 在观察。“一次都不采纳”本身是当前正确的行为,不是故障。 这条旁路的价值在于“需要的时候它在”,而不是“每天都得用上”。
我们是怎么评测的,以及它证明不了什么
这部分写出来可能会让文章显得没那么有说服力,但我觉得必须写。
评测一共四层:
- 手写样本:二十四个问句,分三档——问句里有原词、换了同义说法、完全不共享词面。前两档设了通过线,第三档只记录不设线,因为它本来就是用来暴露天花板的。
- 规模测试:临时库里种一千条和一万条,看库变大之后召回会不会退化。临时库,绝不碰生产数据。
- 合成集:二百八十八条正例、一百零六条负例,走真实服务层,专门量误召。负例就是“库里根本没有这回事”的问句。
- 真实回放:从生产库做一份只读快照,用最近三百次真实唤醒重建查询,看真实流量下闸门到底怎么表现。
所有阈值都是扫出来的,不是拍脑袋定的:0.75、0.72、0.70、0.68、0.65 各跑一遍,同时看正例命中数和负例误召数,选那条“误召在可接受范围、命中还能站得住”的线。它从来不是一条数学上最优的线,而是一条我愿意承担后果的线。
这套东西能证明的只有一件事:在固定样本上,改动没有让情况变坏。 它证明不了:
- 我们比别人好——没有做过跨框架的横向对比;
- 样本没有偏见——二十四个问句是我自己写的,写的时候就知道答案在哪;
- 真实体验——“她记得对不对”最终要人来判断,而这件事没有自动指标;
- 换个场景还成立——换语种、换话题分布、换用户习惯,结论可能全变。
社区里有一句话我很认同:评测是个专业活。所以本文里所有数字都只声称“在这套样本上”,不声称“最好”。要选型,拿自己的语料做一次 A/B,比看任何一篇博客都靠谱——包括这一篇。
几条不一样的地方
真要总结,和主流做法不一样的只有三条:真源只有两张表,向量、全文、关系索引都能删掉重建;证据链是硬约束,不是可选字段;“读过了”是一个显式动作,不是回合结束。
其余的选择——单文件、本地模型、用关联表存图、读写路径分开——是这个规模下的取舍,谈不上方法论。它们成立的前提是“一个群、一台机器、几千条记忆”,换个规模就该换做法。
矛盾与替代:她改口的时候
人会有前后矛盾,记忆系统必须能表达这件事,而不是二选一丢掉一边。
有两类容易混淆的关系,我一开始也写错过:
矛盾(contradicts): 两条记忆互相冲突,但没有判决。两边都还算数,都保持有效,都不写结束时间。召回时并列展示,让模型自己判断语境。
替代(supersedes): 明确的事实变更。新的覆盖旧的,旧的写结束时间。
区别在于“判决”。前者是“她说过两遍不一样的话”,后者是“她明确纠正过这件事”。把前者当后者处理,会把旧记忆的有效期误盖掉——那不是更新,是篡改历史。
替代有四道门槛:必须有明确证据、必须由模型显式发起、必须指向具体旧记忆、必须能通过校验。任何一条不满足,降级回“矛盾”,两边都留着。
还有第三种:撤销(retracted)。整条记忆不再参与召回,但版本和证据链保留。撤销 ≠ 删除。
这套东西实现下来,其实是在做一种最终一致(eventual consistency):短期内允许两条矛盾的记忆并存,但不允许悄悄丢掉任何一条,也不允许在没有判决的情况下假装已经一致。
写入侧的防重
记忆库最怕的不是少,是重复。
有次做规模测试,意外发现一个很反直觉的数字:同样一千条记忆,插“无关内容”时目标记忆的命中率百分之九十四,插“目标记忆的克隆”时只剩百分之十七。重复对召回的伤害,是库变大的五倍多。
社区里常说“记忆越多 AI 越傻”,我觉得这句话只对了一半:傻的不是“多”,是“多且没区分度”。一千条各不相同的事实不会让她变傻,一百条近似重复会把真正相关的那条挤到第五名之外。
而原来的写入口只挡“内容完全相同”。“同一件事换个说法”照常新建一条。
修法不是“相似就合并”。我第一版写的就是自动合并(相似度足够高就挂证据不新建),测了一下发现「撤离率 20%」和「撤离率 30%」的Dice 系数也很高——按相似度自动合并会把不同的数字吞掉,直接违背“记忆不能伪造”。
所以最终是:永远落库,只提示。 写入时算一遍字符二元组的相似度,在同范围同类型的最近几百条里找最像的那条(阈值 0.85),把“最像的是哪条”回给调用方。模型看到提示,应该去改那条,而不是记第二条。人不看提示也没关系,数据还在,事后能合。
备份:因为这东西丢了就真没了
主库是唯一真相,那它就必须能恢复。
备份走 SQLite 的在线备份 API,不是文件复制——WAL 状态下直接拷主库,拿到的可能是半个事务。在线接口保证拿到一个一致的快照,相当于数据库级别的快照隔离。
每次备份做的事:
integrity_check完整性检查,不通过直接失败;- 备份完成后对比行数,必须不少于活库,否则算失败;
- 做一次恢复演练:把备份拷到临时目录打开,查一遍关键表的计数;
- 打一份源码快照——记忆模块在版本控制里是未跟踪的,回滚代码覆盖不到。这条是吃过亏才加的。
当前配置:每三小时一次,保留最近二十份、每日三十份、每周十二份,取并集。另外镜像一份到另一块盘——同盘备份只能防误操作,防不了盘坏。
附件那边有单独的保留策略:超过九十天的文件删掉,但记录保留、状态改成“已清理”。也就是说以后还能查到“这条经历当时有过一张图”,只是图本身不在了。容量统计也算上了附件目录——第一版只算主库的几兆,实际占用一百多兆里九成是附件,阈值配了也永远打不到。
容量线设在 5GB。它不会删任何东西,只是异常增长的哨兵。
静默、退役和补拉
三个运维上绕不开的场景:
静默。 群里吵得厉害的时候可以让她闭嘴——停止回复和唤醒,但继续归档。后台整理也继续跑。所以静默结束之后,她是“知道这段时间发生了什么但没说话”的状态,而不是失忆。
旧记忆层已退役。 2026 年 10 月底起,v3 是唯一记忆层,旧的那套只读归档了。代价说清楚:现在没有回滚。整套关掉就意味着完全没有长期记忆,不会退回旧层顶着。
补拉。 桥接没在跑的时候错过的消息,靠平台的历史接口补。实测上限是两百条,而且分页接口基本不好使——所以只能补短缺口。当前每三十分钟补一次,重启、崩溃、短暂离线都能补上,整晚关机补不上,这是接受的能力边界。补来的经历标上来源,和实时归档区分开。
几个词,对照着看
| 词 | 在本文里的意思 |
|---|---|
| event sourcing | 不改状态,只追加事件,状态由事件推出来 |
| watermark | 游标水位线,表示“处理到哪了” |
| 幂等 idempotent | 同样的输入重放多次,结果一致 |
| provenance | 每条结论都能指回原始证据 |
| IDF | 词越稀有,权重越高;越常见越接近零分 |
| FTS5 | SQLite 自带的全文本检索扩展 |
| bi-encoding | 句子各自编码成向量,再算相似度 |
| 量化 quantization | 把 float 权重压成 int8,换速度和体积 |
| top-k | 只取相似度最高的前 k 条 |
| RRF | 多路召回按排名而不是分数融合 |
| cross-encoder 重排 | 问题和候选成对送进模型打分,准但慢 |
| lost in the middle | 长上下文里中段信息容易被模型忽略 |
| fail-closed | 拿不准就拒绝,而不是放行 |
| 近重复 | 内容高度相似但并非完全相同的记忆 |
最后表个态
说实话,这套东西不一定好。
它没有分布式,没有多副本,没有压测到十万条,也没有跟任何成熟的记忆框架做过正经的横向对比。它就是一个人在家里、在一台常开不常开的电脑上,一边被群里的事情追着跑、一边改出来的东西。里面不少设计是被具体的坑逼出来的,换一个场景可能完全不成立。
但它能用,而且用了几个月,知道它坏的时候会怎么坏:
- 整理卡住,最多是同一批经历多整理两遍,数据不丢;
- 向量猜错,会明确告诉你“这条还没确认”,不会装作知道;
- 数据库损坏有备份,备份有恢复演练,恢复不了会告警;
- 索引全删了也能重建,因为真正的东西只有那两张表。
社区里那轮评测说“小作坊打不过普适方案”,这句话我也认。按问答命中率这一个维度算,我们大概率也打不过一个调好的向量库。我们赌的是另一个维度:当你需要知道她为什么这么说的时候,这套东西答得上来。这个赌注值不值,得看你要的是工具还是角色。
对一个二十四小时待在群里的角色来说,我觉得这比“理论上更先进”重要。记忆系统最怕的不是不够聪明,是错得悄无声息。只要每一次错误都有痕迹、都能定位、都能重放,这套东西就可以一直改下去。
至于更漂亮的架构——分层存储、多副本、流式索引、自动遗忘曲线——等真到了那个规模再说。现在这样,够用。
文中配置取自 2026-11-12 的实际值,与设计文档里的默认值不完全一致;文档写的是口径,配置才是线上真实在跑的东西。文中三张示意图为自绘 SVG,概念图为本地生成,均无外部版权依赖。