我给本地记忆加 read model,是为了保留原始记录,同时让整理后的记录进 SQLite/FTS,模型只读能追溯的结果。
windows-bot-memory/read-model
给 AI 助手做长期记忆时,我最早踩过的坑是:以为“存下来”就等于“以后能可靠召回”。
本地机器人每天会接触很多材料:云端机器人同步、Windows 本地任务、项目索引、Codex profile、每日整理、人工笔记。这些东西都能存下来,但真让 AI 直接搜,结果很容易乱。
我现在更愿意先建一个可重建的 read model,再谈召回。
原始快照负责留证
原始快照先负责保真。
它回答的是:
当时到底发生过什么?这类数据适合长期保留,但不适合直接作为默认记忆。原因很简单:原始层里有大量过程噪声、临时状态、失败记录、重复片段和不适合公开复用的上下文。
如果 AI 每次都直接扫原始层,它很容易把“当时的一句话”当成“长期规则”,也很容易把某个项目里的局部事实带到另一个项目。
所以原始快照我只拿来回查,不拿它当日常召回入口。
normalized 层负责变干净
中间的 normalized 层更像一张干净的中间表。
它不追求永久不可变,也不追求人工阅读体验,而是把不同来源变成统一记录:
- 来源命名空间。
- 工作区或项目 id。
- 原始证据位置。
- 可搜索正文。
- 记录类型。
- 是否适合默认召回。
- 是否经过整理。
这一层最关键的是可重建。
如果规则改了,可以从原始快照重新生成。如果索引坏了,可以删掉再来。如果某个来源后来发现不该混入,也可以重新过滤,而不是在一堆已经污染的搜索结果里手工修。
这和编译成果很像:normalized 层很重要,可源头仍然是原始快照。
SQLite/FTS 是本地 read model
给召回用的,是更稳定的 read model。
对本地 Windows 机器来说,SQLite/FTS5 很适合当这个底座:
- 不需要常驻服务。
- 可以随文件备份。
- 能从 normalized 记录重建。
- 查询失败模式简单。
- 可以返回来源和片段。
- 对小服务器和本地机器都友好。
向量检索可以作为增强,但不该是唯一入口。
长期记忆系统最容易出事的地方,是它想起了一条内容,却说不清这条内容从哪来。FTS 的朴素性在这里反而是优点。
召回前先分强弱证据
记录不应该拥有同样权重。
我更愿意把记忆分成强弱两类:
强证据包括:
- 人工整理过的事实。
- 明确规则。
- 项目索引。
- 当前任务状态。
- 已确认的偏好。
- 维护摘要。
弱证据包括:
- 原始聊天。
- 收件箱导出。
- 运行日志。
- 临时候选。
- 失败中间态。
弱证据有用,主要用来回查;默认回答里少直接用。
如果某条弱证据反复被用到,应该经过整理流程提升为强证据,而不是一直靠原文命中。
AI 读的是解释过的结果
我不希望模型只拿到一堆文件片段。它需要的是带来源说明的召回结果:
命中了什么来自哪里属于哪个项目证据强弱如何是否适合当前工作区这样它才能在回答时做两件事:
- 知道这条信息能不能用。
- 说清楚它是从哪里来的。
如果召回结果只是一段文本,模型就只能靠语义相似度猜。猜对时看起来很聪明,猜错时会把不同项目的事实混在一起。
对本地助手来说,宁愿少想起一点,也别把项目串了。
公开写作也要走 read model
这套记忆系统还有一个副作用:它可以帮助写博客。
但博客素材也不适合直接从原始层拿。安全一点的流程是:
原始证据 -> normalized 记录 -> 摘要/索引 -> 博客候选 -> 草稿审计可公开的是工程取舍、抽象流程和验证方法;不该公开的是原始上下文、运行现场和个人标识。
read model 在这里相当于第一道过滤层。写作时我处理的是整理后的安全素材,而不是直接翻私有记录。
现在我的判断
我现在不再把长期记忆当成一个大文件夹,也不想把所有东西都塞进向量库。
更稳的结构是:
raw snapshots -> normalized records -> SQLite/FTS read model -> scoped recall模型能力变强以后,这层数据整理反而更省心:哪些能跨项目用,哪些只是当时的噪声,都先在数据层标清楚。
先把 read model 建好,再让 AI 读。召回来源清楚,项目不容易串,博客素材也能先筛一遍。
继续读