这一节把「一次读取」和「一次写入」拆开讲透:读取路径有哪八个环节、每一步的输入输出是什么、 一条记录里到底存了什么、四个状态如何演化,以及我们为这些决定付出了什么代价。
点左侧任一环节查看它在做什么,也可以依次点一遍——注意整条链路上没有一步是「和全部记忆做注意力」, 这是显存可控的根本原因。
STAGE 01 / 08
括号里的数字是实测值,不是设计目标。
/remember 之类的标记。记录不是一段文本,而是一条带地址、带版本关系、带状态的结构化数据。下面左侧是全部字段, 右侧是一条真实记录(逐字取自评测结果里的命中记录)。
同一实体和属性出现新值时,旧值被标记为 superseded,而不是被覆盖或删除。 这是「时间冲突」类问题能答对的前提:既要给出当前有效值,也要能说清旧值去哪了。
写入路径的教训:检索救不回不存在的记录
早期版本的退役授权完全依赖一个学习打分(阈值 ≥0.95),没有任何结构校验。 结果是新写入把已有记录误判为旧值并退役:一次 20 条记录的测试里 active 只剩 12 条、retract 高达 16 次、目标记录只存活 8/16。 修好之后:active 12/20 → 20/20,retract 16 → 0,目标记录存活 8/16 → 16/16,端到端 37.50% → 68.75%。 这件事的结论很直白——任何排序器都救不回一条已经不存在的记录。
决定 1 · 两段式读取
粗索引筛页 → 页内重排,而不是对全量记录做注意力。
好处:单次读取的候选规模被压到固定上限(≤4 页 / ≤2 条),成本与记忆总量解耦。 代价:两段都可能出错,且错误会叠加——粗索引错了就再没有机会。
决定 2 · 用地址索引而非向量库
语义地址 + LSH 桶放在模型包内,不引入外部检索服务。
好处:部署时没有额外进程与网络依赖,读取耗时稳定(批次 B 26.35 ms)。 代价:召回质量受地址质量限制,零字面重叠的改写问法曾只有 18.40% 的 Top-1。
决定 3 · 有界 GPU cache
热点记录进 GPU,上限 256 条 / 131,072 token,另有动态保留。
好处:显存占用可预测,两个批次的实际用量分别是 15,885 / 17,990 token,fallback 与分配失败均为 0。 代价:cold page 会带来额外取数路径——不过本次两个批次都是 embedded / process-RAM 模式,cold page 为 0。
决定 4 · 嵌入式 weight-shard 持久化
记忆快照写进模型包的 memory safetensors 切片,默认不用 SQLite、不用磁盘分页。
好处:运行时依赖最少,重启只需加载权重切片、不回放历史聊天。 代价:模型包会随记忆增长变大,且固化更新需要重新保存权重切片——这是明确的工程负担,不是白拿的。