开了 `entityExtractionEnabled` 跑了一段时间后看了一眼 SQLite,发现活跃记忆只有 ~1650 条(另有 ~3140 条已归档),但 `entities` 表已经涨到了 **12,700+ 行**(配了 38,500+ 条关系边),库体积也跟着涨到了 400MB+。 只读统计了一下这 1.27 万个实体的分布,主要有三块水分: 1. **记忆归档后留下的孤儿实体占了 41%(5,240+ 行)** 这批实体通过 `canonical_memory_id`、`entity_attrs`、`entity_relations` 关联的所有记忆其实都已经 `archived = 1` 了,但归档记忆和 `sleep` 维护时没有回收无活跃引用的实体,节点和边一直留在图谱里。 2. **全库只提过 1 次的实体占了 72%(`mention_count = 1` 共 9,170+ 行)** 真正出现 4 次以上的核心实体只有 ~1,390 个(11%)。单次实体里混进了不少命令行参数(如 `--preset`)、路径片段(如 `/root/`)、协议报错短语等一次性词,进图谱基本起不到串联作用。 3. **`src/store.js:4592` `findEntityByName` 仅做字面精确匹配(`WHERE name = ?`),同一对象裂变成十几个节点** - 纯大小写/空格差异就有近 400 组重复(如 `Python` / `python`、`Web Profile` / `web profile`); - 带上 `@scope/` 和版本号后缀更明显:比如光 `dsh-mneme` 自己在表里就裂成了 16 个独立实体(`dsh-mneme`、`@modusensus/dsh-mneme`、`dsh-mneme@0.9.4`、`dsh-mneme 0.9.5`、`@modusensus/dsh-mneme@0.10.0` 等),`DSH` 也裂成了 9 个。这会导致 `entityRecall` 和 `graphAnchoring` 命中其中一个名字时,挂在其它版本分身底下的边串不起来。 几点小建议供参考: - **入库归一化**:匹配实体时忽略大小写与首尾空白;遇到包名带 `@x.y.z` / `vX.Y` 版本后缀时剥离到主包名(具体版本号如果有用更适合落 `entity_attrs`); - **孤儿回收**:在 `sleep` 维护轮加一步轻量清理,把名下已经没有任何活跃记忆引用的孤儿实体(及对应死边)清掉或归档; - **抽取收紧**:提示词里明确排除命令行 flag、裸路径片段、网段等非命名实体。
Activity
Merge pull request #690 from slow-stack/fix/687-688-cross-type-and-en…
modusensus commentedon Nov 13, 2026
写入侧已在 #690 落地(合并 commit
eb47f674),存量按评审结论不动:findEntityByName改为lower(trim(name))+type双键匹配,并加表达式索引;createEntity落库只去首尾空白、保留原始大小写,不剥版本号后缀(DeepSeek-V3/V4是两个不同对象)。entities.canonical_memory_id:v0.7.1 引入、从未接过线(sqlite_master里无 view/trigger/index 引用它,25,474 行全 NULL),走幂等迁移(PRAGMA table_info判断 +ALTER TABLE ... DROP COLUMN)。迁移成本实测:25,474 行旧 schema 首开 396ms、二次 24ms。存量实体不合并:实体 id 是
randomUUID(),合并要重指 59,715 行关系表且无唯一约束,会造出重复边;按报告口径实测,mention_count=1占 75%、按entity_relations.memory_id连接口径的真孤儿是 17.9%(不是 41%),量级不足以支撑一次高风险重写。写入侧归一化会止住新增重复(同类型内lower+折叠空白实测可减 173 个实体,0.68%)。注意:降级是单向门 —— 旧代码没有
addColumn,把插件降回仍带该列的版本后UPDATE/INSERT会报no such column,实体与关系停止落库(记忆写入不受影响),直到重新升级。降级前请先备份memory.db。这条已写进 CHANGELOG。