巧妙之处、边界与代码地图
本章讲什么: 把前四章散落的 「精华」收拢成一张可带走的清单,诚实标出项目会在哪崩,和兄弟项目比一比取舍,最后给一张按符号名导航的代码地图。
1. 巧妙之处(可借鉴的技术)
零 LLM 是默认,LLM 是 opt-in。 整个项目最核心的成本哲学:捕获/压缩/取代/召回/衰减都不需要 LLM——合成压缩用正则(compress-synthetic.ts:76)、取代判定用 Jaccard(remember.ts:77)。只有想要更丰富的语义摘要(AUTO_COMPRESS)和 4 层巩固(CONSOLIDATION_ENABLED)才花 token。妙在:基础记忆能力对用户「免费」,贵的能力按需付费。
RRF 的动态权重归一化。 融合时把缺席流的权重归零再重新归一(hybrid-search.ts:194-206),让 BM25-only 和三路全开两种部署都不用改配置就排序合理。这是「优雅降级」落到排序公式里的写法。
对称的维度守卫。 向量维度不匹配会静默毁掉搜索(跨维余弦=0)。项目在写入侧(vectorIndexAddGuarded,search.ts:94)和加载侧(逐条 validateDimensions 否则拒绝启动,src/index.ts:405-447)都设了守卫——两头堵,而不是出了问题才查。
fire-and-forget hook 的正确退出。 纯遥测 hook 发出 fetch 不 await,再用 setTimeout(...).unref() 强制退出(AGENTS.md 详述)——否则 Node 事件循环会一直等 fetch settle,fire-and-forget 形同虚设。这是个容易写错的细节,项目把它写进了贡献规范。
删除要同步刷盘。 内存索引的删除如果只在内存里,硬退出后持久化快照会「复活」被删条目。所以 mem::forget 走 flushIndexSave() 同步刷(search.ts:63-74,remember.ts:243),而 add 走 debounce 异步刷——按操作的「能否丢失」区别对待。
Float32Array 序列化的 byteOffset 陷阱。 显式传 byteOffset+byteLength 防 Node Buffer pool 制造幽灵维度(vector-index.ts:8-21),修了四个连号 issue。值得任何做向量持久化的人抄走。
2. 边界与局限(诚实)
- 向量是暴力 KNN,没有 ANN 索引。
VectorIndex.search(vector-index.ts:49)对每条算余弦,O(N)。语料到几十万条时单次向量查询会线性变慢——没有 HNSW/IVF。索引也整个驻内存,大语料吃 RAM。 - 取代判定对语义改写不敏感。 Jaccard 是词集合交并比(
schema.ts:94);「用 yarn」和「采用 Yarn 包管理器」词重叠低,可能判不出是同一件事,留下两条并存记忆。 - 合成压缩信息密度低。 默认路径的
facts/concepts为空、narrative截断到 400 字(compress-synthetic.ts),confidence只有 0.3。不开 LLM 压缩时召回到的观察偏「流水账」。 - 召回侧有大 scope 全扫的隐患。
mem::context(context.ts:135)和取代判定都kv.list整个 scope。KV注释里多处提到 75K+ 节点的图谱已撞过 iii 调用超时(schema.ts:18-39),靠预 计算快照绕过——说明全扫在大体量下是真实瓶颈。 - 强一致性不保证。 跨函数的多步写(写观察 + 更新 session + 建索引)只在单会话 keyed-mutex 内串行,没有跨 scope 事务;部分失败靠 try/catch + 重启重建兜底,不是 ACID。
3. 横向对比(同 shelf 兄弟)
memory-context 这一格关注「agent 怎么把上下文存下来、找回来」。agentmemory 的取舍位置:
| 维度 | agentmemory 的选择 | 取舍 |
|---|---|---|
| 存储 | iii-engine 内置 SQLite KV,零外部 DB | 装得轻,但受单引擎调用超时/单机内存约束 |
| 检索 | BM25+向量+图谱 RRF 融合,自实现 | 三路兜底召回好,但向量是暴力 KNN 不扩展 |
| 捕获 | hook 全自动 + 零 LLM 默认 | 零摩擦零成本,但默认观察信息密度低 |
| 记忆模型 | 显式版本化 + 4 层巩固(LLM opt-in) | 模型完整,但巩固/语义层依赖 LLM |
| 集成面 | REST + MCP + 12 hooks,跨 10+ agent | 集成广,但一致性维护负担重(AGENTS.md 列了 8 处要同步) |
对比基线:很多「agent memory」方案直接挂外部向量库(Pinecone/Chroma)+ 纯向量召回。agentmemory 反过来——自带轻量存储 + 多路融合 + 零 LLM 默认,换来「装一个 npm 包就能跑、不烧 token」,代价是检索在超大语料下的可扩展性。