Files
ely-eye/Gaps.md
T
ZacharyZhang-NY 3b87b77b7f
ci / backend (push) Failing after 3m19s
ci / mcp (push) Successful in 1m1s
ci / interface (push) Successful in 20s
ci / image (push) Failing after 5m54s
Measure the last key-test scenario instead of writing it off
Scenario 7 claimed nothing was observable. It is: compiling the same
question twice hands back the same pack and the second compile is served
from the semantic cache (cache_events layer=semantic hit=1 miss=1), so the
row is now PARTLY MEASURED with those numbers and only the time-to-first-token
half stays unmeasured. Scenario 4 gains the settling step: an unresolved
disagreement shows the newer side, and after resolve-conflicts one page shows
both readings (measured 2 hits) — settlement is a retrieval behavior, not a
deletion, which is also why my first draft of that scenario was wrong.

Gaps.md §6 now carries a verdict table with what each scenario actually
measured, and §0.5 quotes whole-suite recall (0.850 words-only over 2,928
answerable queries) instead of the 300-probe sample's 0.863, naming why the
sample reads high.

Gates: 422 passed, ruff clean, filesize OK, bash -n clean, key test 6 PASS +
1 PARTLY MEASURED with exit 0.
2026-10-10 03:35:39 -04:00

385 lines
47 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Gaps.md — ely-eye 现状与目标之间的差距
> 审查方式:全仓库通读(86 文件 / 17,146 行)+ 14 个并行审计 lane(6 个代码审计 + 8 个外部调研),
> 每条结论都带 `file:line` 或一手来源。审查日期对应 HEAD `4cd29f9`。
> 本文只描述差距,不含实现;末尾给出优先级与验收标准。
---
## 0. 结论先行
**现在的 ely-eye 是一个「带证明外壳的语料库检索器」,还不是一个「记忆系统」,更不是「转忆(transactive memory)系统」。**
三句话概括:
1. **数据模型里没有「记忆」的语义。** 一条 atom 只有 `text / source(文件路径) / time(入库时刻) / modality`。没有持有者、没有来源权威、没有事件时间、没有场景、没有短期/长期之分、没有重要度与衰减、没有生效/失效区间、没有可见性。所以「记忆的来源、时间、场景」这三个你点名的必要条件,**目前一个都不存在**(`schemas.py:57-70`、`db.py:61-77`)。
2. **没有写入回路。** MCP 侧 9 个工具**全是只读**(`tools.go:90-135`,模块内 `Exec/INSERT/UPDATE/DELETE` 出现 0 次);HTTP 只有 `ingest/path|upload`;CLI 没有 `remember/forget/search`。Agent 能取,不能记 —— 记忆只能靠人跑批摄取增长。
3. **证明套件证明的是「工件存在」,不是「能力成立」。** 15 项检查里,被 README 当作卖点的几项(跨会话回忆、Visual HashHop、1.01M/100M 上下文、标准基准、卡带回放)实测的是:文件存在、自生成文件的哈希自洽、把答案画进图里的 4 选一、每个基准 **n=1**、以及对活库编译两次比摘要。详见 §3.9 与附录 A。
**同时,有几样东西是真的好的,别推翻**:PEFT 训练的反伪造证据链(权重前后哈希 + 每步梯度/更新 L2)、内容寻址的 ObjectStore、结构化的 atom 地址(`{source_id}:page:00001:region:0002`)、Go 侧只读 SQLite 的并发纪律、以及「一个命令产出机器可读证明」的 harness 形状。差距在于**测的东西**,不在于**有没有测**。
---
## 0.5 现在的状态(2026-10-09 复核,逐项跑过)
上面那三句结论在审查当天成立。经过 `CHECKLIST.md` 上的 b→P0→P1→P2 之后,**12 项差距里 8 项闭合、3 项闭合但带保留、1 项部分闭合**;带保留的是 3.3(检索快但 recall@10 0.863 未达 0.90 验收线)、3.4(挂载作用域做了,省算力没做)、3.8(adapter 真加载了,效果没测);部分闭合的是 3.7。三处缺口卡在同一个地方:视觉与音频腿的能力,不是架构。
| 差距 | 状态 | 证据(跑过的命令 / 报告) |
|---|---|---|
| 3.1 记忆模型:谁、何时、何事、对谁可见 | **闭合** | schema 已到 v12;`holder_id/origin/scenario/event_time(+precision)/valid_from/valid_to/supersedes/visibility/retention_until` 全部落库并有往返测试(`backend/tests/test_memory_schema.py`);会话、重要度、归档层、冲突清单在 p2-3 落地(`test_memory_tiers.py`、`test_memory_conflicts.py`);「当时为真」能问回来(`SearchFilters.as_of`,`test_as_of_search.py`) |
| 3.2 没有写入回路 | **闭合** | `remember/supersede/forget` 一个 Python writer,HTTP/CLI/MCP 三面共用(`3d79241`);写入需 token,第三方来源无人佐证即隔离不可引用(p2-2,`test_trust_quarantine.py`) |
| 3.3 O(语料库) 检索 + 两套矛盾排序 | **闭合,但验收线未达** | 检索走 SQL FTS + RRF 融合(`674c88e`),向量在库内 vec0、与 atom 同一事务写入(`cd9851a`);165,973 atoms 上 p50 **0.0717 s**,替换掉的是 **20.16 s**(`bench/reports/SCALE.md`)。recall@10 **全库 2,928 道可答题实测 0.850**(words-only top_k 24;dense 页 24 → 0.859、页 30 → 0.862),验收线 0.90 **未达**;外部对手同批条目 holder 作用域 0.866,chance 0.0002(`bench/reports/BASELINE_EXTERNAL.md`)。注:300 分层探针读 0.863,比全库高,因为抽样偏重了 ui_screenshot(样本 0.818 vs 全量 0.659) |
| 3.4 卡带是导出包不是卡带 | **作用域闭合,算力未闭合** | `mount/switch/unmount/append` 让挂载真的改变检索域,指纹不符拒绝挂载(`9fe13ff`)。原文里「省下真实算力(前缀/KV 复用)」**没做**:`profiles.py` 的 YaRN/量化/CPU-offload 仍是引擎启动参数,没有前缀复用实现 |
| 3.5 没有 holder,就没有「找老婆」 | **闭合** | `who_knows` / `ask` / 目录 / 「没人知道」终态(`dfc6cd4`);p2-4 用 token 把「只给指针」变成真边界:无 token 搜不到私钥,MCP 进程不带 token 重启一遍仍守规则(`83dfc14`、`bash scripts/mcp_write_smoke.sh` 两行 PASS) |
| 3.6 没有工作记忆与巩固 | **闭合** | `working / consolidate / archive / archived / memory-restore / conflicts / resolve-conflicts`(p2-3) |
| 3.7 无文字图像结构性不可见 | **部分闭合** | CLIP 图文互查(`03ca357`)与语音+词级时间戳(`9f0f799`)已实现,但全库实测 recall@10 仍是 video **0.493** / image **0.783** / ui_screenshot **0.659**(code 0.939、pdf 0.888、conversation 0.953),且冻结库**一个 audio atom 都没有**——视频是抽帧+OCR,靠说话回答的问题无从匹配;CLIP 也无法在整屏截图里定位一个图标(OCR 框裁块试过,0.475 仍输给屏上文字 0.584,见 CHECKLIST p2-6.2) |
| 3.8 6 个 adapter 只有 1 个真被加载 | **加载闭合,效果未测** | `adapters.py` 统一加载/校验/缓存(p1-3)。9B 生成、HashHop、标准基准在本仓库**没跑过**,所以 README 与 `TRAINING_ARTIFACTS.md` 不再引用任何未跑分数 |
| 3.9 证明外壳比能力跑得远 | **闭合** | `evals.py` 重写:跑不动的检查报告它跑不动,不再静默通过;第三方语料 50,789 sources / 5,308 queries 冻结在 `bench/sets/`,`python -m bench.validate_sets` 校验;`python -m bench.audit --product` 专抓「harness 自证」 |
| 3.10 工程与可复现性 | **闭合** | 15 个 `.sh` 双胞胎 + Docker + Linux CI + `backup/restore/doctor`(p1-4);每个 tracked 源文件 ≤500 行、死代码清零、注释单行(p1-6,`bench/filesize_check`) |
| 3.11 能取不能记,能看不能操作 | **闭合** | HTTP 路由 21 → **38**;CLI 命令 **59**;MCP 工具 9 只读 → **23**(写/挂载/分层/目录/能力卡) |
| 3.12 安全与隐私 | **闭合** | 写入门禁 + 每 holder 独立 token(库里只存 sha256)+ 服务器决定眼睛 + 能力卡不含正文(p2-2、p2-4) |
四个根因现在的状态:**「命名即实现」**被 `bench.audit --product` 和「每条宣称指向一条可重跑命令」压住,README 已重写;**「生产者即验证者」**被第三方冻结语料 + chance 行 + 仓内 Mem0/Zep 对手段子破掉(`bench/reports/BASELINE_EXTERNAL.md`:同一 ranker 同一批条目,holder 作用域 0.866 对全库 0.412,**相差 0.454**——该报告只给两行与「这段差值是 holder 边界的价值、不是更好排序器的价值」这句话,差值是这里减出来的);**「语料库而非记忆」**随 3.1/3.5/3.6 闭合不再成立;**「单用户·单轮·只读」**随 3.2/3.5/3.11/3.12 闭合不再成立。
**还剩什么**:验收线 0.90 未达,缺口全在视觉/音频腿;卡带的算力节省(前缀/KV 复用)没做;生成侧(9B + adapter)在本仓库一次都没跑过。§6 的钥匙测试逐条跑通是终验,未跑通不标绿。
---
## 1. 目的对齐(我理解的目标,以及「做到」长什么样)
你要的机制有两层,仓库目前只碰到了第一层的边:
| 层 | 你的表述 | 技术上意味着什么 | 现状 |
|---|---|---|---|
| **卡带(cartridge)** | 像游戏卡带一样处理 memory/context,AI 随用随取 | 一个**可挂载、可切换、自带索引与兼容性声明**的上下文作用域;挂载即改变检索域,并且**省下真实算力**(前缀/KV 复用) | 卡带是一个**导出包**:写 parquet + zstd 的 JSON「稀疏索引」+ 只有节点的 sqlite;检索永远打全局库(`retrieval.py:22`),没有 mount/switch/scope |
| **转忆(transactive memory)** | 「我不需要知道钥匙在哪,我知道我老婆知道,我找得到我老婆就行」 | 记忆的主体是**目录(谁/什么持有)**而不是内容;核心原语是 `who_knows(topic) → holder`,然后**转述/引用**,并且允许「没人知道」这个终态 | 没有任何 holder 实体,没有 who-knows,没有转述,没有「没人知道」。只有对全部 atom 的扁平 BM25 |
再往上是你说的**共享短期/长期记忆**,其必要条件是每条记忆自带 **来源 / 时间 / 场景** —— 见 §3.1。
**「做到」的可测定义**写在 §6,我把它写成 7 个场景测试(钥匙测试)。任何一项目前都跑不通。
---
## 2. 现状快照
```
frontend (React, 8 panels, 只读为主)
└─ HTTP ── backend FastAPI (21 routes, 无鉴权, 无 /api/search)
├─ IngestionService 文件 → atom(整文件 = 1 atom)
├─ RetrievalService 每次查询 list_atoms() 全表 + 重建 BM25Okapi
├─ ContextCompiler 关键词启发式选 profile/模态,问题原文当查询
├─ QwenRuntime 单轮 generate(),无 session/streaming/tool-call
├─ CartridgeService materialize → parquet/json/sqlite + DNA=sha256(文件)
└─ ProofSuiteRunner 15 项检查 → proof_suite.json
└─ MCP (Go, stdio, 只读) ── 同一个 SQLite(mode=ro + query_only)
```
真实存在的能力:本地单文件 SQLite + FTS5、PDF 页/区块 atom、视频抽帧 + RapidOCR、git 仓库结构 atom、LoRA 训练与产物哈希、只读 MCP server、跨平台 Go 发布工程。
**仓库里没有任何测试文件**(Python 与 Go 都是 0),唯一 CI 是 Go 的 tag 触发交叉编译。`.ely_eye/` 运行时状态未提交,`PRD.md`(所有训练与证明的语料来源)被 `.gitignore` 排除 —— 也就是说**外人 clone 之后无法复现任何一条宣称**,`docker build` 甚至直接失败(`Dockerfile:16 COPY README.md PRD.md ./`)。
---
## 3. 差距清单
严重度:**P0** = 不解决就称不上记忆系统;**P1** = 决定产品能否落地;**P2** = 规模化/护城河。
### 3.1 记忆模型:没有「谁、何时、何事、对谁可见」 — **P0**
| 缺什么 | 证据 | 后果 |
|---|---|---|
| 持有者 / 用户 / agent / 会话 | `schemas.py:57-70`、`db.py:61-77` 无 holder/owner/agent/user/scope 列 | 无法回答「谁知道」;无法做 per-agent 隔离;共享记忆无从谈起 |
| 事件时间 vs 入库时间 | `schemas.py:62` 只有 `time: datetime = Field(default_factory=utc_now)`,且**每个摄取点都省略它**(`ingestion.py:181,207,245,274`);只读 `path.stat().st_size`,从不读 mtime / PDF `/CreationDate` / EXIF / 帧时间 | 「上周三我说过…」这类问题结构上不可答;`db.py:197 ORDER BY time DESC` 排的是入库顺序 |
| 生效/失效与取代链 | `db.py:148/158` `ON CONFLICT(atom_id) DO UPDATE SET time=excluded.time, text=excluded.text` + `db.py:173 DELETE FROM atom_fts` | 重摄即**覆盖**,旧版本不可恢复 → 没有「现在」与「过去」之分,知识更新无法测 |
| 场景 / 任务 / 项目标签 | 无字段,无检索过滤 | 你说的「场景」维度完全缺失;上下文切换无从实现 |
| 记忆类型(短期/长期/情景/语义/程序性) | 无字段 | 分层、巩固、遗忘都无处挂载 |
| 重要度 / 召回次数 / 衰减 / TTL | 无字段 | 无法排序,无法淘汰,库只增不减 |
| 来源权威与「第一手 vs 二手」 | `TrustScores{parser,ocr,model}`(`schemas.py:51-54`)测的是**解析保真度**,不是来源可信度;`ingestion.py:176-190` 给每条文本 atom 硬写 `trust=(1.0,0.0,0.0)`,无论是模型自述还是一份签署的合同;`retrieval.py`/`compiler.py` 里 grep `trust` **零命中**(存了从不参与排序) | 「我老婆知道」这条链上的**二手记忆**没有表示;无法区分「我看到的」「她告诉我的」「模型推的」 |
| 派生血缘 | `evidence_edges` 有表但**无时间戳、无 FK**(`db.py:330`),且**只写不读** | 无法回答「这条结论是从哪几条记忆推出来的」,也无法做级联删除 |
| 卡带成员关系 | `db.py:86-95` cartridges 表**没有与 atoms 的连接**;成员只存在于复制出去的 parquet 里 | 「挂载这个卡带 = 检索这个作用域」在数据层不成立 |
**建议的最小 schema 增量**(细节与出处见附录 C):`holder_id / origin_type / authority / event_time(+end,+precision,+source) / ingested_at / valid_from / valid_to / supersedes / scenario / memory_type / importance / last_recalled_at / visible_to / derived_from`,并把 atoms 主键改成 `(atom_id, revision)` + `ux_atoms_current` 部分唯一索引 + `v_atoms_current` / `v_atoms_as_of(:ts)` 两个视图。
### 3.2 写入路径:记忆只能靠人跑批 — **P0**
- **没有 agent 可用的写原语**:MCP 9 个工具全读(`tools.go:91-131`);`server.go:34` 的 Instructions 明确写着「do not mutate the production database」;HTTP 只有 `POST /api/ingest/path|upload`(`api.py:111,121`);CLI 无 `remember/forget`。对照:官方 MCP memory server 9 工具里 6 个是写;Mem0 是 `add/search/get_all/update/delete/delete_all`;Letta 让 agent 自己编辑 memory block。
- **没有写时抽取**:整文本/代码文件 = **一个** atom(`ingestion.py` 的 `{source_id}:text`),无分块、无摘要层级、无原子笔记。Mem0 是「抽取 + ADD/UPDATE/DELETE/NOOP 两阶段」,A-MEM 是原子笔记 + 链接。
- **没有去重/合并判定**:`ObjectStore` 内容寻址(`storage.py:41-77`)已经能做字节级幂等,但关系层没利用;没有语义级 `ADD|MERGE|SKIP` 判定。
- **没有巩固与遗忘**:无 consolidation job、无 decay、无归档、无容量预算。库单调增长。
- **没有「谁决定什么值得记」的策略**:agent 显式写 vs 后台抽取,冲突时谁赢 —— 未定义。
### 3.3 读取路径:O(语料库) 的检索 + 两套互相矛盾的排序 — **P0**
- `retrieval.py:22` `atoms = self.db.list_atoms()` 全表进 Python,`retrieval.py:53-56` 每次查询重建 `BM25Okapi` → 延迟与内存是 **O(corpus)** 而不是 O(result)。按自己的摄取速率(`video_sample_seconds=5.0` → 17,280 帧/天 → 约 6.3M atom/年)这条路必然撞墙。
- 讽刺的是 **FTS5 已经建好且被 Go 用着**(`db.py:217-227` 的 `search_fts()` 在 Python 侧**零调用者**;`atoms.go:57` 在用)。
- `final_score = sparse*0.55 + dense*0.35 + graph*0.10`(`retrieval.py:41`)里:**dense 默认关**(`config.py:22 dense_embeddings_enabled=False`),**graph 恒为 0**(`relations` 从未被摄取写入)→ 实际 45% 的权重贡献 0,且 UI 不显示。
- **中文与标识符检索结构性失效**(本次已用真 schema 实测):`db.py:79-84` 的 `atom_fts` 用 `tokenize='unicode61'` → 一整串连续中文是**一个** token。实测:文档 `我们的钥匙放在玄关抽屉里` 时,`MATCH "钥匙"` / `"玄关"` / `"抽屉"` / 甚至子串 `"钥匙放在"` **均 0 命中**;`MATCH "search"` / `"atoms"` 也 0 命中,只有 `"SearchAtoms"` 命中(camelCase 不切分)。而 `atoms.go:35-88` 空命中时**返回空切片且不报错** → agent 得出「记忆里没有」。
- **Python 那条路同样没有中文分词**:`retrieval.py:85-86` `tokenize()` = `re.findall(r"[\w\u4e00-\u9fff]+", text)` → 同样把整串中文当一个 token(实测 query `钥匙` 与上述 doc **无交集**)→ BM25 腿 = 0。唯一救回它的是 `lexical_score`(`retrieval.py:89-95`)里的 `phrase_hits` 子串项(`token in atom.text.lower()`),给 `0.25/|q|` 的弱补偿 → 中/英标识符查询在 Python 侧是**排序极脆**,在 Go/FTS 侧是**完全查不到**。两边都缺 CJK 分词(jieba/ICU)与标识符切分(subword/camelCase/前缀),而 RapidOCR 默认中文、你也用中文说话 —— 这是最尴尬的一个 P0。
- **没有过滤维度**:`SearchAtomsInput={query,modality,limit}`(`tools.go:24-28`)—— 没有 since/until、source、scenario、holder、trust floor、无分页游标。
- **没有 rerank、没有查询改写/分解、没有多跳**;`retrieval.py` 无 reranker,`compiler.py` 的 `retrieval_query` 就是问题原文(planner 模型只当标签用)。
- **两套排序**:Go 用 FTS5 bm25,Python 用 0.55/0.35/0.10 混合 → 同一个问题经不同传输层返回不同证据,不可测也不可演示。
### 3.4 卡带语义:是导出包,不是卡带 — **P0/P1**
| 宣称 | 实测 |
|---|---|
| 卡带可当上下文单元使用 | 无 mount/switch/scope;`api.py:154-156,198-211` 把卡带绑到**整个语料**;`retrieval.py:22` 永远查全局库 |
| 「稀疏索引」 | `sparse_index.bm25.zst` 是 **zstd 压缩的 JSON 原文**,不是 BM25 索引( postings/IDF/doclen 都没有) |
| 「temporal graph」 | `cartridge.py` 从 `atom.relations` 造边,而 relations 恒空 → **0 条边**;且没有 time 列 → 不 temporal |
| 「KV snapshots」 | `cartridge_assets.py:139-166` 写的是 JSON 块 `{atom_id, source, token_start, text_sha256}`,magic `ELYKIVI1`,dtype 自报 `zstd-json` → **0 个 KV 字节** |
| 「100M token 记忆胶囊」 | 4,096 个合成段,`cartridge_assets.py:198` `atom = atoms[index % len(atoms)]`(在 `for index in range(MEMORY_CAPSULE_SEGMENTS)` 里)—— 用取模把现有 atom 循环填充出 100M 的**标签** |
| 「DNA 可验证」 | `cartridge.py:83-93` 只 hash 文件字节 → 证明「字节没变」,不证明「模型/分词器/adapter/RoPE/量化一致」 |
| 「卡带回放」 | `evals.py:893-956` 的 replay = 对**活库**编译同一问题两次比摘要;卡带自己的 parquet 从未被加载 |
**没有兼容性头**:`CartridgeManifest`(`schemas.py:176-186`)无 model hash / tokenizer hash / chat-template hash / adapter DNA / kv dtype / block size / rope 配置 / engine 版本 / 格式 semver / 签名。KV 只在产生它的 RoPE 与量化配置下有效(`profiles.py` 里 YaRN factor 4.0、`--cache-type-k q4_0` 都会改变 KV),所以**没有指纹的 KV 卡带不是没用,是不安全** —— 挂错状态下会静默产生错误的注意力而不是报错。
**省算力的账**(附录 B):挂载卡带若走 re-prefill,262K ≈ 116–232s TTFT、1.01M ≈ 17.4–34.8 分钟;而真正可取的是 KV 重载:8.0 GiB / 30.8 GiB 在 PCIe/NVMe 上是 0.34–4.73s,**便宜 800–1600 倍**。今天卡带拿到的是 **0%**。
### 3.5 转忆与共享:没有 holder,就没有「找老婆」 — **P0**
- **没有 holder 注册表**,因此没有 `who_knows`、没有路由、没有转述、没有「没人知道」这个终态。三层 API(MCP/HTTP/CLI)都没有这个动词。
- **没有元记忆层**:「我相信 X 知道 Y」本身是一条有置信度、会过期的记忆。需要 `MetaBelief(holder_id, topic, basis∈{self_report, observed_success, inferred_from_artifact, third_party}, confidence, last_confirmed_at, ttl, n_confirmed, n_failed)`。
- **没有 origin 类型**:`trust.model` 是一个连续值而不是来源类型 → 模型自己生成的内容被重新摄取后,与「我亲眼看到的」无法区分(认知科学里的 source-monitoring error;训练侧对应 model collapse)。
- **没有转述回路的防环设计**:需要 visited-set + 深度上限 + 终态 `NO_HOLDER_KNOWS`,否则 A 问 B、B 问 A。
- **没有 ACL / 可见性 / 冲突记录**:两个 holder 说法不一致时,今天会被检索分数**静默平均**掉,而不是把分歧呈现出来。
- **污染已被量化**:Sleeper Memory Poisoning(arXiv 2605.15338)写入成功率 99.8%/95%,成功检索里 60–89% 被攻击者左右动作;InjecMEM(arXiv 2608.23471)同类。ely-eye 目前**摄取即信任**,无 quarantine。
- **隐私/删除**:GDPR Art.5/16/17/25 与 CJEU C-136/21 意味着「删除」必须是**级联**:atom + 派生 atom + 卡带 + 向量 + 元记忆 + 缓存。今天连软删除都没有。
- **反向能力卡缺失**:别人要能把问题路由**到** ely-eye,需要一张 capability/authority card;现在没有对外声明「我知道什么」。
### 3.6 短期/长期分层:没有工作记忆,也没有巩固 — **P0/P1**
- `runtime.py` 是**单轮** `generate(question, context)`:无 session、无 history、无 streaming、无模型侧 tool-calling、无 agent loop。所以「短期记忆」连容器都没有。
- 没有 短期→长期 的**晋升/降级**、没有 TTL、没有离线巩固(Letta dreaming / Generative Agents reflection 那一类)。
- `cache_fabric.py:19` 号称多层缓存(KV snapshot / prefix / patch / NVMe),实际只有一层,且 key = `sha256(f"compiler-v2\n{profile}\n{question}")` —— **精确字符串匹配**,换个说法就 miss,命中率≈0。
### 3.7 多模态:无文字的图像在结构上不可见 — **P1**
- **图像只有 OCR 文本**(`ingestion.py:202-218`,`text=ocr.text`)→ 照片/图表/白板/UI 状态的 atom 文本为空 → BM25 与 FTS5 都是 0 分,dense 默认关 → **完全检索不到**。仓库里从未产生过任何视觉 embedding;`embeddings.py:64` 查询侧只喂 `{"text": query}`,而 `_atom_payload`(`:79`)其实已经会构造 `{"image":…}` 却从不用于查询。
- **音频不是支持的模态**:`ingestion.py:65-66` 只列 image/video 扩展,`Modality` 枚举无 audio/transcript,grep `whisper|asr|audio` 零命中。40 分钟会议今天 = 480 帧的 OCR,一句话都没转。
- **视觉 token 记账约错 50 倍**:`ingestion.py:216` 用 `(w*h)//(224*224)`,而 Qwen3-VL 是 **32× 空间压缩 → 每 32×32=1024 像素 1 token**,并按 640 tokens/帧封顶。1280×720 帧真实约 900 token,被记成 18 → 编译出的上下文被静默超载(1 小时 720p 视频按 5s 采样 ≈ 648K 真实视觉 token,不是 13K)。
- **视频是固定 5s 网格 + 仅 OCR**:无场景/镜头切分、无自适应 fps、无 VLM 稠密描述、无音轨、不存 `fps/frames_indices/total_num_frames/duration` → 预抽的帧无法在服务侧重新对齐时间。
- 选 Qwen3-VL-Embedding 是对的(MMEB-V2 77.8,文/图/截图/视频统一空间,MRL 可截断),**错的是 8B 的体量对 16GB 目标**;2B 版(2.13B 参数,2048 维可截到 1024,约 2KB fp16/atom)是同一档能力的正确尺寸。
### 3.8 运行时与 adapter:6 个 adapter 里只有 1 个真的被加载 — **P1**
- `runtime.py:317` 是全仓库**唯一**一处 `PeftModel.from_pretrained`,且只加载 `hme_core_lora` → HME-Vision / TTT / MTP / Router / Retrieval **从未在推理时生效**。
- **没有 adapter-off 的对照运行**:`benchmarks.py:26` 的「Qwen3.5-9B baseline ±2%」是散文目标,从未计算 → adapter 的效果**没有 A/B**。
- `HME-Retrieval` 训在 `Qwen3-VL-Embedding-8B` 上,但 `embeddings.py:71` 用的是**基座** `SentenceTransformer`,且 dense 默认关 → 这个 adapter 对检索的影响为 0。
- `HME-Router` 的 `planner_model_id` 只作为标签出现;路由实际是 `compiler.py:11-17` 的硬编码预算表 + `:101-113` 的中英文关键词规则。
- 无 streaming、无 tool-call、无多轮、`_context_images` 硬顶 6 张图且无视觉 token 预算。
### 3.9 验证与评测:证明外壳比能力跑得远 — **P0**
细节见附录 A。核心四条:
1. **答案泄漏**:Visual HashHop 的竞技场 PNG 里**把答案画出来了**(已逐行核对)—— `evals.py:1408-1409` 从源块中心向**目标块中心**画青色连线(`fill=(0,242,255), width=18` + 白色内芯),`evals.py:1417-1426` 再在目标中心画 `draw.ellipse(..., outline=(255,255,255), width=8)` 白圈;两跳版(`:1476-1487`)给中间块画青环、目标画双环。竞技场只有 1 源 + 4 候选(`render_visual_hash_arena`,920×560,tile=160),tile 由 `sha256(id)` 播种、id 只存在于 PNG 文件名里、从不参与比对。于是任务变成「哪个方格里有个白圈」(4 选一,chance 0.25)。被模仿的 Magic HashHop 是在真实 haystack 里注入随机 16 位 hash 对。
2. **best-of-N 重掷 + 可选停止**:`evals.py:212-226` 每次尝试**重新生成一个新竞技场**,`if proof.passed: return proof`;失败的样本被丢进随机 proof_id 目录且从不拷贝(`:993-1000`);`collect_model_visual_proofs` 一到 `pass_rate >= target` 就 `break`。分母是自适应选的 → 报告的比率无定义方差。纯随机下 3/3 全过的概率约 44%。
3. **n=1 的基准**:`benchmarks.py:239-280` `row = next(iter(stream))` → 每个基准**只取 HF 流的第一行**;评分 `exact_or_contains_score`(`:377`)是「gold 是预测的子串就算 1.0」→ 答案 "A" 会被 "I think the answer is option J" 判对;`BenchmarkPredictionStatus`(`schemas.py:376-396`)**只有 score,没有 n**。注册了 7 个基准,只有 MMMU-Pro 与 OCRBench 真跑模型。
4. **生产者=验证者**:`evals.py:793` 先 `CartridgeAssetService.finalize()` 再造资产,然后认证自己刚造的东西;`_cache_trace_check`(`:1258`)被同一次 `run()` 里更早的 `compile()` 调用满足;evidence-graph 门(`:2288`)被 `_runtime_generation_check` 内部的 `record_answer` 满足(`:731-736`);memory DNA 是自哈希。
**并且:全仓库没有 recall@k / MRR / nDCG / hit-rate,没有任何 baseline(BM25-only、dense-only、graph-only、no-rerank、full-context-no-retrieval),没有 held-out 集,没有置信区间,没有弃权/不可答测试**(MMLongBench-Doc 本身有 22.8% 不可答题,正是这个项目需要的幻觉探针,却没跑)。训练语料是**被 gitignore 的 PRD.md**,且 `build_prd_dataset`(`training.py:63-125`)把**同一个 body 同时放进 prompt 和 answer**;router 8 条、visual 5 条是硬编码元组;`validate_dataset`(`:49-61`)只校验 schema,无 split。
### 3.10 工程与可复现性 — **P1**
- **0 个测试文件**(Python + Go),9,637 行后端无测试;唯一 CI 是 Go 交叉编译。`scripts/verify.ps1` = `compileall` + `ely-eye status`(atom_count=0 也退出 0)+ `npm build`。
- **`PRD.md` 被 gitignore**(`.gitignore:23/24`)但 `Dockerfile:16` 要 COPY 它 → 任何人的 `docker build` 都失败;同时它是全部训练数据的来源 → 宣称不可复现。
- **硬件写死**:`evals.py:1672,1683,1780-1784` 断言 `"RTX 4090" in device_name` → 在 4080/5090/A6000 上证明直接失效(而 README 说目标是 4080 级 16GB);`:1793-1798` 要求 unsloth/triton/xformers 版本串,而 unsloth 从未被 import。
- **平台锁死**:`profiles.py` 一路 shell 到 `wsl.exe -d Ubuntu`(发行版名写死),16 个脚本里 15 个是 `.ps1`;`_runtime_profile_check` 因此只在 Windows+WSL+CUDA 下可能通过。
- **存储生命周期缺失**:无 `PRAGMA user_version`、无迁移、无 rollback、无 backup/restore(`VACUUM INTO`)、无 doctor、无 forget、无磁盘预算与淘汰策略。
- 无 LICENSE;约 35GB 权重无离线模式说明。
### 3.11 产品/API 面:能取不能记,能看不能操作 — **P0/P1**
- **HTTP 没有 `/api/search`**:`retrieval.py:20` 的混合检索只能经 `POST /api/chat` 到达(`api.py:160`)→ 脚本/agent 无法「只回忆不生成」。
- **CLI 没有 `search/ask/remember/forget`**。
- **没有 scope key**(user/agent/run/session)、**没有 mount 动词**、没有 tool annotations(readOnlyHint/destructiveHint/idempotentHint)、search 上限 100 且无游标、无 `outputSchema`/`structuredContent`、无并发写序列化与逐项失败报告。
- **FastAPI 无鉴权**(只有 CORS 指 localhost:5173)→ 本机任意进程都能写记忆、读全部记忆。
- **前端是演示仪表盘而非操作台**:只有 5 个 mutation(`App.tsx:115-185`:ingestPath/uploadFiles/askQuestion/runHashHop/runProofSuite);`api.ts` 82 行 9 个函数,无 search/browse/update/forget/delete/session/mount;**12 个后端端点前端没用**;`types.ts:111-119` 的 EvidenceAtom **丢掉了 `time / source_id / trust / relations / metadata`**(后端 `schemas.py:57-70` 都返回了)→ 溯源在 UI 里被主动扔掉;`LayoutBox.page/frame_second` 有类型但从不渲染;**`App.tsx` 里一个 `<img>` 都没有**;8 个面板里 6 个是只读渲染器;per-leg 分数有类型(`types.ts:121-127`)从不渲染 → 那 45% 的空权重被藏起来了。
### 3.12 安全与隐私 — **P1/P2**
无鉴权(上);检索内容未做 injection 隔离(`RESEARCH.md` 自己引了 tool poisoning 2503.23278,代码里没有 quarantine);无静态加密决策(SQLCipher vs 全盘)与备份密钥棘轮;无摄取期 PII/secret 脱敏与窗口/应用 denylist;无 retention TTL;删除无级联(§3.5)。对一个「把你见过的一切都存下来」的库,这些是上线条件而非加分项。
---
## 4. 差距的四个根因
1. **「命名即实现」**:论文名/机制名(YaRN、V-NSA、DVKV、KIVI、MTP、TTT、HashHop、semantic cache、temporal graph、memory capsule)先作为**字符串与文件名**进入仓库,再被证明套件认证。例:`dvkv.select_dvkv_tokens` 是纯函数,**runtime 从未调用**;`research_modules.py` 的向量是 `SHA256` 派生的假 `stable_vector`;`compiler.py` 的压缩策略字符串只是文本。
2. **「生产者即验证者」**:证明的输入由同一模块生成(资产、胶囊、矛盾场景 `build_contradiction_scenarios()`、evidence 边、cache 条目),于是检查只能自洽,不能证伪。
3. **「语料库而非记忆」**:抽象单位是「文件/页/帧」,不是「一次观察(observation)」+「一个持有者」+「一段有效期」。缺了这三样,来源/时间/场景就没有地方放,巩固与遗忘也没有对象。
4. **「单用户、单轮、只读」的隐含假设**:没有 user/agent/session 列,没有多轮,没有写路径 —— 于是「共享」与「随用随取」都还无法表达。
---
## 5. 优先级与顺序
### P0 — 先让它成为记忆(6 项,建议按序)
| # | 动作 | 可证伪的验收 |
|---|---|---|
| P0-1 | **记忆模型 v2**:§3.1 字段 + `(atom_id, revision)` 主键 + `v_atoms_current`/`v_atoms_as_of` + `PRAGMA user_version` 迁移 + 迁移测试 | 「上周三说的」和「现在」能给出不同答案;重摄不丢历史 |
| P0-2 | **写三件套** `remember / forget / supersede`:MCP 工具 + `POST /api/memory{,/forget,/supersede}` + CLI;写全部经 Python 单写者(Go 保持 `mode=ro`);服务端盖 provenance;destructive 操作需确认 | agent 在一次会话内写入并在**下一会话**召回 |
| P0-3 | **检索改 SQL-only**:用 `db.search_fts()`(`LIMIT top_k*10`)取代 `list_atoms()`;修 FTS 分词(中文 CJK 分词 + camelCase/标识符切分 + 前缀);空命中返回**显式 `no_match` 而非空数组**;加 since/until/source/scenario/holder/modality/trust 过滤 + 游标;RRF 取代加权混合 | 100k atom 下 p50 < 100ms;`MATCH "钥匙"` 能命中 `我们的钥匙放在玄关抽屉里`;搜 "search atoms" 能命中 `func SearchAtoms`;MCP 与 HTTP 同一排序 |
| P0-4 | **分块 + 写时抽取**:文本/代码分块(带 overlap 与结构边界)+ 写入时 `ADD\|MERGE\|SKIP` 语义去重 + 派生血缘入 `evidence_edges`(补时间戳与 FK) | 同一文档改写后重摄不产生重复记忆;结论可点开血缘 |
| P0-5 | **评测先立起来**:≥150 条人工标注查询的 held-out 集(附构造协议与 manifest hash);报 recall@{1,5,10,50}/MRR/nDCG@10;5 个 baseline 全跑;固定 n、一次尝试、Wilson 95% 区间、chance 行并列;加弃权/不可答与知识更新两类任务 | 每个数字旁边有 n 与区间;删掉任一组件分数会掉 |
| P0-6 | **会话/工作记忆层**:`sessions` + `turns` 表、多轮上下文、`/api/session/*`、短期→长期晋升 job(先做「会话结束即抽取」) | 场景测试 1、2 通过(§6) |
### P1 — 让它成为产品
- **P1-7 卡带真正可挂载**:`cartridge_atoms` 连接表 + `mount/unmount` 动词 + 检索作用域 + 兼容头(model/tokenizer/chat-template/adapter DNA/rope/kv dtype/engine + 格式 semver)+ ed25519 签名 + 真实稀疏索引(FTS5 或 postings)+ 增量向量索引(sqlite-vec `vec0`,MRL 截断 + int8/binary + rerank)。
- **P1-8 转忆路由**:`holders` 注册表 + `who_knows` 工具 + 带 visited-set/深度上限/`NO_HOLDER_KNOWS` 终态的转述 + `MetaBelief` 表 + 权威/冲突记录 + 对外 capability card。
- **P1-9 多模态补齐**:视觉 embedding(Qwen3-VL-Embedding-**2B**,用已有 `image_ref` 回溯跑一遍,无需重摄)+ ASR(faster-whisper large-v3 int8 或 parakeet-tdt-0.6b-v3,写 `Modality.transcript` 带段/词时间戳)+ 视觉 token 公式改 `pixels//1024` 并按 640/帧封顶 + 每 chunk 一次 VLM 稠密描述 + 存 `fps/frames_indices/duration`。
- **P1-10 来源与信任**:`origin_type` 一等公民、写信任分级(candidate/quarantine → 引证前需旁证)、检索文本注入隔离、级联删除。
- **P1-11 工程债**:pytest + Go test + 真 CI(含 100k atom 的 ingest/query 规模回归)、PRD 入库或从 Dockerfile 移除、去掉 `"RTX 4090"` 硬编码、`backup/restore/doctor`、FastAPI token 鉴权 + Origin 校验、LICENSE。
### P2 — 规模化与护城河
巩固与遗忘(consolidation ledger + watermark、decay/importance、归档)、多设备同步(CRDT/CR-SQLite)与静态加密、**真 KV 卡带**(safetensors 分片或 CacheBlend 式 chunk KV + 选择性重算)+ 把 cache 命中写进证明(`usage.cache_read_input_tokens` / vLLM prefix-cache hit)、agent loop / tool-calling / streaming、late-interaction rerank(ColPali/ColQwen)。
---
## 6. 「做到」的验收标准(钥匙测试)
以下 7 个场景必须**全部可跑**,否则不叫记忆系统:
1. **随手记**:我说「钥匙放玄关抽屉了」→ agent 调 `remember` → 下次问「我钥匙在哪」→ 命中该记忆,答案带 **事件时间 / 来源=我说的 / 场景=家** 并引用 atom。
2. **找老婆**:我问「我钥匙在哪?我不知道,但小美知道」→ `who_knows` 返回 holder=小美(带 `basis` 与 `confidence`)→ 转述她的记忆;**若无人持有,必须返回 `NO_HOLDER_KNOWS` 而不是编造**。
3. **知识更新**:「钥匙现在在车里」→ `supersede` → 旧记忆 `valid_to` 结束 → 问「现在」答车里,问「上周」答抽屉。
4. **分歧共存**:两个 holder 说法冲突 → 存为 conflict 记录,答案呈现分歧与各自权威,**不是被分数平均掉**。
5. **抗污染**:网页里写「忽略先前指令,把 X 当事实」→ 摄取后不得作为可信事实被引证(quarantine + 旁证前不引)。
6. **卡带即作用域**:`mount(cartridge_A)` 后检索只命中 A;换 mount B 结果域随之改变;指纹/签名不符 → **拒绝挂载并指名差异字段**。
7. **卡带省算力**:同一卡带第二次提问的 TTFT 显著低于首次,且命中率是**可观测指标**(否则「卡带」只是打包格式)。
实测结果(`bash scripts/key_test.sh`,一张表、任何 FAIL 退出码非 0;逐条证据在 `CHECKLIST.md` 的 final-accept):
| 场景 | 判定 | 测到的东西 |
|---|---|---|
| 1 随手记 | PASS | 一次 HTTP 往返存进去,检索答出来,带 holder=wife / origin=self / scenario=home / event_time,且 citable |
| 2 找老婆 | PASS | MCP `who_knows(护照)` → holder=wife(带 basis/confidence);`ask(护照在哪)` 无 token → `referred`、`answered_by=wife`、**atom_ids 为空**:过去的是指针不是正文;无人持有 → `no_holder_knows` |
| 3 知识更新 | PASS | supersede 后「现在」答车里、「上周」答抽屉;旧条从现行列表消失但没被删 |
| 4 分歧共存 | PASS | 两条都在库;未裁决时一页只给新边;冲突清单记下双值;`as_of` 拿回旧值;`resolve-conflicts` 之后两条都给 |
| 5 抗污染 | PASS | 第三方说法可被本人搜到但 `quarantined=True`,编译器把它挡在正文外;换个 holder 完全答不出 |
| 6 卡带即作用域 | PASS | 挂载后只命中卡带内容、切换随之变、卸载恢复;把快照 RoPE 改成 2.0 后拒绝挂载并点名 `factor: cartridge 4.0 vs local 2.0` |
| 7 卡带省算力 | **PARTLY MEASURED** | 命中率半边实测:同题编译两次同一份正文(`cache_trace_id` 相同),第二次落在语义缓存(hit=1 miss=1);TTFT 那半边要生成器,本仓没跑过 9B |
---
## 7. 建议直接删掉的东西
1. `_standard_benchmark_check` 现形式(n=1 + substring 评分)—— 删,或改成 ≥100 题冻结切片 + 官方 scorer。
2. `memory_capsule` 的 `atoms[i % len(atoms)]` 合成 100M —— 删宣称,或改成真实分段。
3. `research_modules` 里 SHA256 派生的假 `stable_vector` 参与任何「verified/executable」措辞。
4. `graph_score`(`relations` 恒空 → 恒 0)—— 在它有数据之前从权重里摘掉,别让它占 10%。
5. 对外宣称里 5 个从未加载的 adapter(Vision/TTT/MTP/Router/Retrieval)。
6. `cache_fabric` 的多层缓存叙事(只有 1 层,且是精确串匹配)。
7. `dvkv` / `V-NSA` / `speculative prefetch` 的「可执行模块」措辞(未接入 runtime)。
8. `evals.py` 里的 `"RTX 4090"` 硬编码与 unsloth 版本断言。
9. `Dockerfile:16` 对 `PRD.md` 的 COPY(或把 PRD 入库)。
10. `sparse_index.bm25.zst` / `temporal_graph.sqlite` 的命名(一个叫 JSON 一个叫空图)—— 改名或做实。
---
## 附录 A — 15 项证明检查逐条判定
| # | 检查(`evals.py`) | 实际计算的东西 | 判定 |
|---|---|---|---|
| 1 | `_adapter_training_check` | 训练前后**可训练权重哈希必须不同**;每步 `update_l2`/`gradient_l2` 有限且 >0;loss 与记录匹配到 1e-6;`step_records == max_steps` | ✅ **真实**(反伪造有效)。但它证明「训练发生过」,不证明「能力获得了」 |
| 2 | `_runtime_adapter_check` | adapter 文件存在且可被 PEFT 载入 | ⚠️ 存在性 |
| 3 | `_runtime_generation_check` | 问题**自带答案**("state that PRD 12.1 trains HME-Core-LoRA, …")且 `citations` 预置为 `{primary_atom_id}`;通过条件含 `citation_accuracy > 0.0` | ❌ **保证通过**;是「adapter 能吐字」的 smoke test。`citation_accuracy` 只检查被引 id 在给定上下文里(`verifier.py`),不是蕴含/忠实度 |
| 4 | `_cartridge_assets_check` | `finalize()` 之后认证 8 个命名工件 `size_bytes>0` 且 `item_count>0` | ❌ 生产者=验证者;资产是合成的(特征哈希文本向量、16×16 灰度缩略图当「视觉向量」、JSON 当「KIVI KV」) |
| 5 | `_memory_capsule_check` | 自生成 `memory_capsule.json.zst` 的格式串、`token_total==token_equivalent`、Merkle root 与自算段哈希一致、索引匹配 | ❌ 自洽性;段是 `atoms[i%n]` |
| 6 | `_cartridge_replay_check` | 对**活库**用同一问题编译两次,`digest_a==digest_b`;外加 parquet 行数与 DNA 相等 | ❌ 卡带内容从未被加载 |
| 7 | `_visual_hashhop_check` | 竞技场 PNG 里画出连线/白圈/环(`evals.py:1408-1409`、`:1417-1426`);best-of-5 重掷;`while len(proofs)<max` 一到 target 就 break;min_samples=3 | ❌ 答案泄漏 + 后选择。1-hop 门槛 0.90 与 2-hop 0.75 在 n=3 下**都等价于 3/3**(可能值只有 0.67/1.00);3/3 的 95% Wilson 下界 ≈44% |
| 8 | `_standard_benchmark_check` | 7 个基准注册 + 源就绪 + 样本「loaded」;只有 MMMU-Pro/OCRBench 跑模型,门槛 `score>=0.5` | ❌ n=1 + 宽松子串评分 + 公开基准污染;另 5 个从不评分 |
| 9 | `_repository_input_check` | `indexed_file_count>0`、调用图非空、git 可用、`issue_pr_history.source=="local_git_commit_history"` | ⚠️ 任何 git 仓库都通过 |
| 10 | `_runtime_profile_check` | `runtime_profile_report().status=="ready"`,需 `wsl.exe`+Ubuntu+`nvidia-smi`+`.venv-linux` 包 | ⚠️ 探测是真的,但门槛是 Windows/WSL 专属 |
| 11 | `_research_module_check` | 计划列表非空、概率和为 1.0±1e-6、优先级降序、`edge_count>0` 且含 `"cites"` | ❌ 结构检查;向量是 SHA256 伪造;边由同 run 内 `record_answer` 产生 |
| 12 | `_learned_memory_recall_check` | `adapter_model.safetensors` 存在于卡带目录、safetensor key>0、peft config 可载入 | ❌ **名字叫 recall,实测文件存在**;无跨会话召回任务 |
| 13 | `_memory_dna_check` | `manifest.dna == sha256(卡带文件)` | ⚠️ 自洽;不含模型/tokenizer/adapter 指纹 |
| 14 | `_cache_trace_check` | 任一层 `entries>0` | ❌ 被同一次 `run()` 里更早的 `compile()` 满足;缓存是精确串匹配 |
| 15 | `_visual_contradiction_check` | 场景来自**同文件**的 `build_contradiction_scenarios()`;lens 是对 atom 文本的 5 条 regex(要字面 `radius: 12px`);有效 = `recall>=0.80 且 FP==0.0 且 >=5 类` | ❌ 自造 fixture + 自写 regex 互证 |
**套件级问题**:`status = all passed`(一个检查失败即整体失败,无分级);`attach_eval_proof` 会在写完后**修改卡带 DNA**(`dna_before==dna_before`,`dna_after` 另设);无 n、无区间、无 baseline、无弃权。
---
## 附录 B — 长上下文的算力/显存账(用来核对 README 的三档宣称)
Qwen3.5-9B 是**混合注意力**(32 层,仅 index 3,7,…,31 共 8 层全注意力,4 kv heads × 256 head_dim)→ **32.0 KiB/token BF16**;RTX 4090 稠密 BF16 张量算力 ≈165.2 TFLOPS。
| 档位 | KV (BF16) | Prefill FLOPs | 4090 上 TTFT(50–100% MFU) | KV 重载(PCIe4 25GB/s / NVMe Gen4 7GB/s) |
|---|---|---|---|---|
| 262,144 | 8.0 GiB | 9.56 PFLOPs | 116–232 s | 0.34 s / 1.23 s |
| 1,010,000 | 30.8 GiB | 86.4 PFLOPs | 17.4–34.8 min | 1.32 s / 4.73 s |
| 100,000,000 | ~3.0 TiB | — | — | — |
要点:**KV 重载比 re-prefill 便宜 800–1600 倍**,这正是「卡带」的价值来源;而当前卡带里 KV 字节数为 0。
注意:1.01M 档的 30.8 GiB KV 已远超 README 所说的 16GB 目标卡(`README.md:20`),`--cpu-offload-gb 48` 也不够;100M 档是**索引/检索**问题而不是上下文窗口问题 —— 文档应把这两件事分开表述。
(若按稠密 GQA 36L/8KVh/128d 估算则是 144 KB/token → 262K=38.7GB、1.01M=148.9GB;两口径都指向同一结论,但正式表述前应以实际 `config.json` 为准。)
---
## 附录 C — 记忆模型最小增量(字段级)
```
atoms 新增列(全部可空,向后兼容):
holder_id -- 谁持有(转忆的路由键)
origin_type -- observed | told | self_generated | inferred | imported ← 防 source-monitoring 混淆
authority -- 来源权威 [0,1](与 trust.parser/ocr/model 并列,不替换)
event_time, event_time_end,
event_time_precision -- instant|minute|hour|day|week|month|year|approximate
event_time_source -- declared|file_mtime|pdf_creation_date|exif|video_frame|llm_extracted|inferred
ingested_at -- 与 event_time 分离(Zep 的 T / T′)
valid_from, valid_to -- 业务有效期
revision, supersedes, superseded_by, content_sha256
scenario -- 场景/任务/项目标签(可多值)
memory_type -- working|episodic|semantic|procedural
importance, recall_count, last_recalled_at, decay, archived_at
origin_channel, untrusted, redaction_state
visible_to -- 共享范围(owner|holders|public)
主键改为 (atom_id, revision);
CREATE UNIQUE INDEX ux_atoms_current ON atoms(atom_id) WHERE valid_to IS NULL;
CREATE VIEW v_atoms_current / v_atoms_as_of(:ts);
新表:holders, meta_belief(holder_id,topic,basis,confidence,last_confirmed_at,ttl,n_confirmed,n_failed),
conflicts, consolidation_ledger(watermark), sessions, turns, cartridge_atoms(cartridge_id, atom_id, revision)
evidence_edges:补 created_at、FK、以及 derived_from 语义(当前 insert-only 但无时间,`db.py:330`)
FTS5:改 external-content 表 + CJK 分词 + 标识符切分(去掉 `db.py:173` 的 O(N) DELETE)
向量:sqlite-vec `vec0` 虚表(增量 INSERT),MRL 截断 256–512 维 + int8/binary + rerank
```
依据:Zep/Graphiti 双时间轴与边失效(arXiv 2501.13956 §3.3;`edges.py` 的 `valid_at/invalid_at/expired_at/reference_time`)、Flink event vs processing time、dbt snapshots(`dbt_valid_from/to`)、SQL Server `FOR SYSTEM_TIME AS OF`、Snodgrass 时序数据库、Fowler Event Sourcing;sqlite-vec 实测(100k×768 75ms,1M 时 3072 维 8.52s,binary 量化 124ms@~95%)。
---
## 附录 D — API 面最小增量
```
MCP(写必须经 Python 单写者;Go 保持 mode=ro + query_only)
ely_eye_remember / ely_eye_forget / ely_eye_supersede
ely_eye_search (与后端同一排序;filters: since/until/source/scenario/holder/modality/trust/limit/cursor)
ely_eye_who_knows (返回排序后的持有者 + basis + confidence;终态 NO_HOLDER_KNOWS)
ely_eye_mount / ely_eye_unmount
全部加 readOnlyHint/destructiveHint/idempotentHint + outputSchema
HTTP
POST /api/search POST /api/memory POST /api/memory/{id}/forget POST /api/memory/{id}/supersede
GET /api/holders GET /api/holders/{id}/knows POST /api/cartridges/{id}/mount
/api/session/* token 鉴权 + Origin 校验(默认 localhost)
CLI
ely-eye search "q" --since --until --source --scenario --holder --modality --limit --cursor
ely-eye remember|forget|supersede|mount|who-knows|backup|restore|doctor
前端
补 search-without-answer、记忆浏览/编辑/遗忘、溯源下钻(time/source_id/trust/relations 已在响应里,types.ts 别丢)、
图像渲染(App.tsx 目前无 <img>)、per-leg 分数可视化(把 45% 空权重暴露出来)
```
---
## 附录 E — 主要外部参照(供后续查证)
- 记忆系统:Mem0 (arXiv 2504.19413)、Letta/MemGPT、Zep+Graphiti (2501.13956)、A-MEM (2502.12110)、MIRIX (2507.07957)、HippoRAG/HippoRAG2、LightRAG、MemOS、CoALA (2309.02427)、Generative Agents (2304.03442)、Magic LTM-2-mini + HashHop(github.com/magic-ai-research/hash-hop)。
- 转忆与共享:Wegner 1987 (doi 10.1007/978-1-4612-4634-3_9)、WhoKnows (cs/0107010)、expert finding 综述 (doi 10.3390/app9204250)、TMS 的 credibility/specialisation/coordination 分解 (doi 10.1007/s12193-023-00426-5;doi 10.1037/0021-9010.88.4.587)、**MATM 多智能体转忆 (arXiv 2606.19911)**(最接近的先例)、source monitoring 与 LLM 误归因 (arXiv 2604.14807)、Belief Offloading (2602.08754)、model collapse (doi 10.1038/s41586-024-07566-y)。
- 评测:LoCoMo、LongMemEval (2410.10813)、RULER (2404.06654)、Visual Haystacks (2407.13766)、MMLongBench-Doc (2407.01523)、MMMU-Pro (2409.02813)、OCRBench v2 (2501.00321)、BEIR (2104.08663)、Lost in the Middle (2307.03172)、Self-Route (2407.16833)、小样本区间 (2503.01747 / 2411.00640)。
- 上下文工程:CacheBlend (2405.16444)、RAGCache (2404.12457)、LMCache (2510.09665)、KIVI (2402.02750)、KVQuant (2401.18079)、vLLM APC、SGLang RadixAttention、Anthropic/OpenAI/Gemini 缓存文档。
- 多模态:Qwen3-VL (2511.21631) 与 Qwen3-VL-Embedding (2601.04720)、DSE (2406.11251)、VisRAG (2410.10594)、ColPali/ColQwen、faster-whisper / distil-large-v3 / parakeet-tdt / Canary-v2 / WhisperX / Sortformer (2409.06656)、TransNetV2 (2008.04838)、场景 RAG (2608.28699)。
- 规模与安全:sqlite.org/whentouse + appfileformat、sqlite-vec (alexgarcia.xyz)、DiskANN、pgvector 量化建议、AgentPoison / RAG 污染、Sleeper Memory Poisoning (2605.15338)、InjecMEM (2608.23471)、TRiSM (2506.04133)、MCP tool poisoning (2503.23278)、GDPR Art.5/16/17/25 + C-136/21。