Repository navigation
v7.0.2 — 精准记忆/召回/极限压缩
Mnemosyne OS 7.0.2 — 精准记忆 / 精准召回 / 极限上下文精确压缩
这一版的唯一目标:让「精准记忆 · 精准召回 · 极短上下文下的精确压缩」从声称的能力
变成可复现、可断言的事实。
为什么要出这一版
7.0.1 已经把向量模型与向量库接上了,但精准仍然达不到"可以承诺"的程度。深查之后确认:
问题不在参数没调好,而在机制本身——以下几条让"100% 精准"在结构上不可能达成:
| # | 症状 | 根因 |
|---|---|---|
| P0-1 | 长会话「越聊越偏」,正确答案从 rank1 掉到 rank3 | access_boost 是加性且上限 +0.20(与整路权重同量级),而计数对"凡进入结果列表"无条件累加 |
| P0-2 | 无关查询也返回高分;一切"分数阈值"规则失效 | 五路打分各自 x/max(x) 归一化 → 最高分恒为 1.0 → 融合分没有绝对含义(实测无关查询 top1 1.0200 > 相关查询 0.6922),且旧行为无论如何填满 k 条 |
| D1 | graph_query("用户") 恒为空 |
抽取器把 用户的显卡是 RTX 4090 的主语抽成 用户的显卡 → 图节点永远不是 用户;5 路融合里权重 0.10 的图通道长期空转 |
| D3 | budget_tokens=40 实际塞进 150+ token |
无分词器时兜底用「4 字符 ≈ 1 token」(英文经验值),中文被低估 3~4 倍 → 压缩降级路径永不触发 |
| D6 | 同一条偏好被反复重述 → 库里堆多条 0.98+ 重复 | 写入侧无语义去重(只有事后 dedup) |
| D7 | 熔断窗口内的向量写入被静默丢弃,那批记忆永久失去语义索引且无人察觉 | 插件 add() 裸 return(返回 None),调用方无法区分"成功 / 真失败 / 被丢弃" |
| D8 | 正确答案因"多了一个字"丢掉图通道的分 | 记录侧图节点取自 entities 的中文滑窗碎片,切出什么高度依赖字数对齐 —— 本质是抽签 |
| D9 | 边的 memory_id 全是 NULL → "这条记忆连了哪些图节点"永远查不到 |
add_edges(memory_id=…) 对 dict 型边完全忽略该参数 |
| D10 | 正文里写了日期反而被扣分 | 时间通道用正文抽出的 event_time:"购于 2026-03-15"被衰减到 ≈0.24,而"备用显卡"(无日期)拿 1.00 |
| D11 | 纯改写问句被相关性下限误杀(问"我喜欢什么"得到"没有相关记忆") | 改写对余弦 0.4900 与无关项 top1 0.5275 分布重叠 → 单一阈值无法两全,必须靠结构化标签通道兜住 |
新增:AIC 记忆胶囊(Adaptive Information Capsule)
问题的本质:用户端上下文常常只有 1~2 轮、几百 token。传统做法把"压缩"等同于
"丢记忆"(模型看不到信息)或"生成式摘要"(有损改写,会凭空造出原文没有的数字/时间/
结论)——两者都让"精准"变成概率事件。
这一版的做法:把压缩重新定义为「分层 + 可寻址」。压缩产物不是更短的近似文本,而是
{稳定指针 ref} + {结构化事实} + {内容原子} [+ {抽取式要点}]
由此同时满足三条可断言的硬性质:
| 性质 | 含义 |
|---|---|
| 无损可逆 | ref 指回原文;expand(ref) 逐字取回,并校验内容哈希(错指针/被改写的记录不会被静默交付) |
| 零幻觉 | 要点是抽取式的(句子必须逐字来自原文),事实来自规则三元组,全程不做任何生成 |
| 事实守恒 | 数字 / 日期 / 金额 / 型号 / URL / 邮箱 / 引号原话("原子")在所有压缩层级都完整保留 |
分层阶梯:full(原文)→ gist → facts → pointer → atoms(仅指针+原子)。
指针与原子永不裁剪——它们分别是"可逆"与"事实不丢"的唯一凭据。
于是"预算不够"不再是"信息消失",而是"精度分层":预算再小,可被追问的事实
(多少钱、什么时候、哪个型号)依然在场。
胶囊是内容寻址的:同一记忆在同一预算下产出逐字节相同的胶囊 →
上游 LLM 的前缀/KV 缓存能稳定命中。省钱与"精准"在这里不冲突。
新增 MCP 工具
capsule:取单条记忆的胶囊(预算内),返回text/ref/level/
atoms/tokens/min_tokens/lossless/over_budget+ 自检结果。expand:按指针逐字取回原文,返回verified(哈希校验)。recall_health:召回质量只读指标 —— 空结果率 / top1 分趋势 / 相关性下限剔除量 /
置信带与通道分布 / 延迟分位 / 向量后端成败 / 胶囊缓存命中率 / 图边规模 / 最近 20 轮明细。- 工具数 18 → 20(7.0.1 为 14)。
修复清单
- P0-1
access_boost:加性 +0.20 → 乘性、封顶 +5%;计数语义收窄为"融合分 ≥ top1 的
90% 才 +1"、上限 5。实测对照:仅因"先前问过 6 个无关问题",干扰项 +0.12
(= 6 × 0.02,与公式逐位吻合)——这是"越聊越偏"的直接机制,上下文再大也救不了。 - P0-2 相关性下限:阈值由实测标定(相关簇 cosine min 0.5222 / 无关簇 max 0.4064
→REL_MIN_SEMANTIC=0.45),未过线者在排序之后剔除(不动候选下标,避免与倒排/doc_tf
的下标语义错位)。允许返回空结果:对 Agent 而言"没找到相关记忆"比"给 5 条无关记忆"安全得多。
边界:目标定位(forget/ 更正 /dedup)走apply_floor=False—— 用户描述天然含糊,
返回空等于"无法遗忘";安全性改由dry_run先行 + 报告带置信带保证。 - 置信带跨调用污染(本轮新发现):
_relevance_band挂在跨调用共享的索引记录 dict 上,
导致"取完结果 A 后再发一次无关查询 B"会把 A 的置信带静默改写(实测复现)。
修法:输出边界做快照(返回副本)+ 每轮打分前清标记 + 缓存命中重贴 +sync_record保持
访问计数会话内一致。 - 图通道:模式化正则优先(
A的B是C→ 主语取 A、属性进qualifier)+ 主语去定语归一 +
查询侧代词归一(我/我的→用户)+ 邻接图多跳。实测graph_query('用户')由空 →
10 个邻居节点;并新增存量数据图边懒回填(幂等)——只修抽取器只能让新写入有图,
老记忆仍然查不到,"改好了"必须真的能被用户看到。 budget_tokens真正参与取舍:continue→ prefix packing;装不下原文时
降级为胶囊而非丢弃,装不进正文的进precision_index(指针+原子,不占正文预算)。- 预算口径诚实:改用 CJK 感知的保守估算(CJK 逐字计 1、拉丁按词计 1),
避免"预算 40 却塞进 150+ token"的假通过。 - 写入侧语义去重 + 原子守恒硬约束:近似重复(≥0.95)并入既有记忆(内容以最新为准、
tags 取并集、置信度取高者、version+1);但凡"改一个字就是另一个事实"的片段不同者,
相似度再高也绝不合并 —— 否则"用户的年假是 5 天"会吃掉"…15 天",数据没丢但事实被改错,
且用户与审计都看不出发生了什么。方向取舍:宁可漏合并,绝不误合并。 - 编码单一收口:
sanitize_str()成为唯一实现(sqlite_backend._sanitize委托调用),
带按来源计数;订正encode(errors='replace')用?(U+003F) 而非 U+FFFD。 - 向量写丢弃可见化:插件
add()返回布尔,丢弃原因分开计数
(dropped_breaker可重试 /dropped_unavailable/dropped_no_collection);
doctor新增vector_ops/vector_backend(含熔断器剩余时间)/graph三段。 - F9-b 确证修复:
recall输出投影带出superseded_by(实测)。 - 内容原子补
num3/ident两条规则:6333、1024、bge-m3、gpt-4o、H100
这类"取值与身份标识"过去不算原子("嵌入模型是多少维"的答案1024会被丢掉);
并把计数归一为"原文里的字面出现次数",消除多规则命中同一片段的计数膨胀。
验收(全部实测,三套缺一不可)
| 验收 | 结果 |
|---|---|
scripts/verify_recall_quality.py(端到端质量对照,需真实向量栈) |
10/10 通过 |
| — precision@1 | 8/8(冷启动)· 8/8(预热后)· 8/8(先问 4 个无关问题后)· 8/8(先问 28 个无关问题后)← 7.0.1 同类实验退化到 6/8 |
| — 假阳性 | 无关查询返回 0 条(7.0.1 行为是无论如何填满 5 条) |
| — 极短上下文 | 30 token 预算下 8/8 问句的关键取值仍可见 |
| — 延迟 | 召回 p50 0.21 ms |
scripts/verify_memory_lifecycle.py(MCP stdio,需真实向量栈) |
35/35 通过 |
scripts/verify_precision_recall.py(离线回归,不连外部服务) |
94/94 通过 |
| 分层阶梯 | 实测 full → gist → pointer → atoms 随预算收缩逐级降级 |
expand 可逆性 |
逐字往返字节一致(116/116);错哈希被拒(verified=false) |
py_compile |
60 文件 / 0 失败 |
| 文档完整性 | 96 处版本声明升级;含 127.0.0.1 的行零破损 |
为什么必须三套都跑:只有离线跑通,不代表 MCP 面可用;只有 MCP 跑通,
也不代表机制没退化("分数全塌到常数底"时 MCP 断言仍可能全绿);
而前两者都通过,也不代表排序正确 —— D8–D10 就是这样躲过前两套、
最终被"端到端质量对照"照出来的。
兼容性
记忆库格式、MCP 协议版本、CLI 命令面、配置文件全部不变,已有记忆库可直接沿用。
存量数据会在首次 recall / graph_query 时自动补齐图边(幂等)。
相关性下限的复标定(0.45 → 0.55)
初版 0.45 在8 条记忆(无关项与查询毫无话题交集)上标定,太乐观;补上同主题干扰项
("备用显卡"/"病假天数"/"备用端口")后复标定:
| 阈值 | 无关查询放过的条数 | 相关查询正确答案保留 |
|---|---|---|
| 0.45 | 5 | 8/8 |
| 0.55 | 0 | 8/8 |
| 0.60 | 0 | 7/8(丢掉那条真答案) |
已知边界:相关簇下沿(0.5708)与无关簇上沿(0.5275)只差 0.043,该阈值对语料分布
敏感。因此 recall_health 新增 sem_top1 / sem_background 供发现"整库同主题"
后复标定。
已知未做(诚实标注)
- 词表/键控注入式数据投毒、跨用户推理数据泄漏(
memory/claim的信任边界)
仍属 7.0.1 已标注的开放项,本版未触碰。 - 纯改写且与库内内容无词面/标签锚点的问句,可能被判为"无相关记忆"(实测该情形余弦
0.4900 与无关项 top1 0.5275 分布重叠,单一阈值无法两全)。本版用标签通道兜住了
偏好/意图类(最常见的一类),更泛化的改写仍可能落空 —— 这是刻意的保守取舍
(返回空比给错证据安全)。 - 图通道仍为规则抽取(无 LLM 参与):中文复杂句(多从句、省略主语)的召回率有限,
它只是 5 路融合中权重 0.10 的一路,不承担主要召回职责。
English summary: 7.0.2 turns "precise memory / precise recall / precise compression under
tiny context" from a claim into an assertable fact. It fixes the mechanisms that made it
structurally impossible (access-count feedback loop, missing relevance floor, dead graph
channel, no-op token budget, dishonest token accounting, silent vector-write drops) and adds
AIC memory capsules: compression is redefined as layered + addressable — a stable
pointer plus structured facts plus "atoms" (numbers, dates, amounts, model IDs, URLs) that are
preserved at every level, with expand(ref) returning the original verbatim and
hash-verified. Extractive only, so zero hallucination; content-addressed, so byte-identical
capsules keep upstream KV caches warm. Acceptance: 34/34 lifecycle assertions (real vector
stack) and 94/94 offline precision assertions. Backward compatible; existing stores keep working.