Files
ely-eye/CHECKLIST.md
T
ZacharyZhang-NY 181f6bf57e Give every memory a holder, an origin, a time and a scenario
An EvidenceAtom could say what was remembered and nothing else: not whose memory it
is, not where it came from, not when it was true, not in what situation it may be
used. The baseline measured the cost of that: the same ranker scores recall@10 0.412
over the whole corpus and 0.866 inside one holder's memory, so the missing holder
column is worth more than any ranking change in the ablation.

- schemas.py: MemoryOrigin, TimePrecision, Visibility; EvidenceAtom gains holder_id,
  origin, scenario, event_time / event_time_end / event_time_precision /
  event_time_source, valid_from / valid_to, supersedes, visibility, retention_until.
  `time` is renamed `ingested_at` — ingestion time gets one name, not two.
  A validator refuses valid_to before valid_from, event_time_end before event_time,
  and an atom that supersedes itself.
- migrations.py: schema v2 with PRAGMA user_version. A first-version database is
  upgraded in place (ALTER + RENAME COLUMN), rows intact, and a database newer than
  this build refuses to open instead of being silently downgraded.
- v_atoms_current hides memories that are closed, lapsed or not yet begun; unknown
  bounds read as visible. Time travel is db.atoms_as_of(moment, holder) because a
  SQLite view cannot take a parameter.
- evaluation_schemas.py: the proof and benchmark report shapes move out of schemas.py
  to keep it under the file-size limit; importers follow.
- backend/tests/test_memory_schema.py: 8 tests, including the v1 -> v2 upgrade and the
  validity timeline.

Verification of record: pytest backend/tests bench/tests -q -> 70 passed; ruff check on
every touched file -> all checks passed; mypy (fresh cache) on db.py, migrations.py,
schemas.py, evaluation_schemas.py and backend/tests -> no issues. evals.py carries 40
pre-existing mypy errors at HEAD and 40 after this change (measured on both copies);
its pyright findings are the same debt, and p0-6 rewrites that file. `peft` and
`pyarrow.parquet` "could not be resolved" is the documented pyright artifact: pi-lens
runs it with 0 config files, and both import at runtime.
2026-10-07 14:30:22 -04:00

16 KiB
Raw Blame History

CHECKLIST.md — 执行进度真相

真相来源:Gaps.md 的 P0→P1→P2。从上到下一条一条做,禁止跳项。 每完成一条:跑「验收」列的命令,把结果贴进 证据: 行,再打勾。 主 agent 独自完成。禁止 subagent、worktree、新分支、并行。 每条完成后用 /herdr 在右侧 split panel 起 pi review(strata/qwen3.8-flash-next-abliterated-iq3_s Sol High),最多 5 轮。

状态记号:[ ] 待办 [~] 进行中 [x] 已过审 [D] DEFERRED(代码就绪、未真跑,不计入 pass)


阶段 0 — 先立量尺

  • b-1 下载真实语料并冻结
    • b-1.1 yubo2333/MMLongBench-Doc:真实长文档 PDF + 人写问题 + evidence_page_ids + unanswerable 标记
    • b-1.2 xiaowu0162/longmemeval-cleaned(清单原写 xiaowu0162/longmemeval,该 id 不存在→401):会话记忆 QA,含 knowledge-update / temporal-reasoning / multi-session 类别与 answer_session_ids(字段实名,非 evidence_session_ids)
    • b-1.3 openbmb/VisRAG-Ret-Test-{SlideVQA,InfoVQA,ChartQA,MP-DocVQA,ArxivQA,PlotQA}:query→页面图像 qrels
    • b-1.4 OS-Copilot/ScreenSpot-v2:真实 UI 截图 + 指令 + bbox(实测 bbox 布局为 [x,y,w,h]:1271/1272 在界内 vs xyxy 61/1272)
    • b-1.5 lmms-eval/Video-MME:真实视频 + QA;清单原写「只取 short 档」,实取分片归档里全部 129 个视频(short 105 / medium 76 / long 34 题),因为 mp4 只覆盖 900 个视频中的 129 个,再砍 short 会饿死 n
    • [~] b-1.6 3 个真实 git 仓库(pyparsing / regex / tokio)已下载冻结,但 b-3 的 code 集改用 code-search-net/code_search_net(2.2 万条 docstring→函数,天然 ground truth,n 大两个数量级);仓库级设置挪到 b-5
    • 验收:bench/corpus/manifest.jsonl 28 条,每条含 url、retrieved_at、sha256、annotation_source、license、files[];python -m bench.manifest_check 退出 0(实测 0);manifest sha256 = 8fc1ed861c891d778853d41107630869f9eb267a669c5d1038e389de80114347
  • b-2 构建多模态查询标注集(answerable n=2928)
    • b-2.1 统一 schema:{query_id, query{modality,text}, gold[{source_id,region}], scope{holder,scenario,as_of}, answerable, answer, origin};用 source_id 而非 gold_atom_ids,因为 atom 是新系统的产物、source 才是第三方语料的稳定身份;region 按模态受限(page/bbox/turn/path+symbol/{})
    • b-2.2 gold 映射:全部来自第三方标注(qrels / evidence_page_ids / answer_session_ids+has_answer / bbox / func_path+func_name),零模型判断
    • b-2.3 配比(answerable):文档 766≥90、图像 240≥60、截图 320≥40、视频 215≥30、代码 1237≥30、记忆 150≥90;unanswerable 2380/5308 = 44.8%≥15%
    • b-2.4 陷阱类别齐:知识更新 25 / 时间推理 25 / 多会话 25(LongMemEval 原生类别)+ 无答案 2380(4 种构造:cross-holder、held-out、native unanswerable、distractor)
    • 验收:python -m bench.validate_sets(清单原名 dataset_check/manifest_check,实作合并为一个)报 OK: 50789 sources, 2928 answerable queries, 2380 unanswerable 退出 0;它同时校验 gold 存在性、gold holder==query holder、observed_at <= as_of 时间可达性、以及每个 set 文件的 SHA256 是否仍等于冻结的 manifest(重跑两次 SHA 相同由此保证)
  • b-3 检索 benchmark:五路消融 + 延迟规模
    • b-3.1 指标实现:recall@k、MRR、nDCG@10,纯 Python,无模型(bench/metrics.py + bench/tests/test_metrics.py 11 条:Wilson 90/100→0.8257、10/10→0.7225、0/10→0.0 手算对齐;nDCG@10 1.5/1.6309297535714574;chance 公式经测试修正为 min(1.0, k/N))
    • b-3.2 消融五路已跑满 5,308 条(bench/systems.py:bm25_only / dense_only / graph_only / no_rerank / fusion_rerank,融合权重照抄产品 0.55/0.35/0.10,graph_only 如实返回常量序因为产品从不填 atom.relations)。recall@10:bm25 0.519、dense 0.424、graph 0.001、fusion 0.565、fusion+rerank 0.453 —— 加 reranker 反而掉 0.112,graph 等于 chance,融合比任何单信号高 0.046
    • b-3.3 延迟规模曲线(bench/run.py scale_curve,p50/p95);档位按语料实际取 500 / 2_000 / 10_000 / 50_000 个 source(清单原写 1k/10k/100k atom,语料冻结为 50,789 source,100k 档不存在)。首轮只有 50,000 一档出数:探针按 gold 是否在子集里筛,小前缀几乎不含 gold 就整档跳过 —— 已改为延迟不需要 gold(scale_probes + 测试),四档齐全
    • b-3.4 BASELINE 报告:在改动前的代码上跑,冻结提交
    • 验收:python -m bench.run --config bench/configs/ablation.json 出表(--config 已接,配置优先于默认、命令行仍可覆盖);每格带 n 与 Wilson 95% 下界;bench/reports/baseline_*.json 入库
  • b-4 外部基线
    • b-4.1 仓库内 Mem0/Zep 风格同构对照:扁平 store + holder 作用域 + 无失效(bench/counterpart.py:split_items 400 字切句、(holder, sha256(text)) 去重=Mem0、supersede 按 (holder, subject) 时间序关闭旧条目=Zep;visible(holder, as_of) 只对 temporal store 生效)+ bench/tests/test_counterpart.py 11 条
    • b-4.2 同语料同硬件跑,出对照数字:bench/reports/baseline_external.json + BASELINE_EXTERNAL.md(165,973 atom → 403,859 条 memory item,Zep 式 supersede 关闭 27,072 条;5,308 query 全量)
      • mem0_flat [holder scope] recall@10 0.866(Wilson 下界 0.855,MRR 0.629,p50 0.084 s)
      • mem0_flat [whole corpus] recall@10 0.412(p50 0.090 s)
      • zep_temporal [holder scope] 0.866;zep_temporal [whole corpus] 0.429
      • 读法:同一个 ranker、同一批 item,只把候选集从全库换成「这个 holder 的记忆」,recall@10 从 0.412 涨到 0.866(+0.454)——收益来自 holder 边界,不是更好的排序;Zep 式失效只在跨 holder 时有小幅正效应(conversation +0.091),在 holder 作用域内反而让 conversation 从 0.993 掉到 0.978、并把 5.8% 的不可答题变成拒答
      • 交叉校验:counterpart 的 whole-corpus 纯稠密 0.412 ≈ 产品 dense_only 0.424,两套 harness 互相印证
    • b-4.3 附录:真 Mem0 跑法与参考值,标明其 add() 依赖 LLM 的不可比因素(bench/reports/EXTERNAL_APPENDIX.md:Mem0 论文 LOCOMO 判分表 full-context 72.90 / Mem0^g 68.44 / Mem0 66.88 / Zep 65.99 / best RAG 60.97,附 token 预算与 p50/p95 延迟;四条不可比理由,其中关键一条是论文只报 search/answer 延迟、不报 add() 成本,Zep 的图是异步构建的)
    • 验收:bench/reports/baseline_external_*.json;对照基线与 ely-eye 同标注集同指标
  • b-5 反自证审计(bench/audit.py + bench/tests/test_audit.py 9 条)
    • b-5.1 单次尝试:禁 best-of-N 重摇 —— AST 规则:while 循环的测试或 break 守卫里出现分数词(pass_rate/target/recall/accuracy/score/hit_rate)即违规;函数名含 best_of/reroll/until_pass 即违规。已抓到产品现行违规 backend/src/ely_eye/evals.py:2351(Visual HashHop 达标即停)
    • b-5.2 禁达标即停:跑满 n 才算分 —— 报告的 meta.queries/answerable 必须等于冻结集 5308/2928,否则必须记录 limit_per_modality
    • b-5.3 每个分数旁必有 chance 基线行 —— 报告缺 chance.recall_at_10 或缺任一格的 hit_at_10_wilson_lower 即报错
    • b-5.4 producer ≠ verifier —— bench/metrics.py 不得 import ely_eye/transformers/sentence_transformers/torch/bench.systems/bench.counterpart;每条 gold 的 origin.dataset 必须在 bench/corpus/manifest.jsonl 里且 revision 等于冻结 revision(实测 6 个 set 全通过)
    • 验收:python -m bench.audit --self-test 退出 0(注入的 best-of-N 与缺 chance 两例都被拒);python -m bench.audit --product 现存 3 条违规退出 1(1 条产品重摇 + 2 份报告未落盘)
    • 遗留:b-1.6 挪过来的「仓库级代码检索设置」(pyparsing/regex/tokio 三仓库)尚未做成查询集,记在 P2

阶段 1 — P0:让它成为记忆

  • p0-1 记忆模型加四维度
    • p0-1.1 holder_id + origin(self / observed / artifact / third_party)——字段与 DB CHECK 已上;「写入方不可伪造」是服务端盖章,归 p0-2.3
    • p0-1.2 双时间轴:event_time / event_time_end / event_time_precision / event_time_source + valid_from / valid_to + supersedes(旧 time 列重命名为 ingested_at,只有一个名字)
    • p0-1.3 scenario + visibility(scope=holder / visible_to / everyone + holders + purpose)+ retention_until(提升为真列,因为 current 视图要过滤它)
    • p0-1.4 PRAGMA user_version 迁移框架 + 旧库可重放(migrations.py:v1 库原地 ALTER + RENAME COLUMN 升到 v2,行不丢;版本高于本构建则 SystemExit)
    • p0-1.5 视图 v_atoms_current + 时间旅行查询——偏差记录:SQLite 视图不能带参数,所以 as-of 是 db.atoms_as_of(moment, holder=None),谓词与视图一致但去掉 retention(retention 是保存策略,不是当时是否为真)
    • 验收:pytest backend/tests/test_memory_schema.py 绿(8 条,含 v1→v2 升级不丢行、时间旅行返回历史版本);实际路径是 backend/tests/,不是 tests/
  • p0-2 写入路径
    • p0-2.1 remember / forget / supersede 三写,Python 单一写者
    • p0-2.2 MCP 工具面 + HTTP POST /api/memory{,/forget,/supersede} + CLI 三面同源
    • p0-2.3 服务端盖 provenance:ingested_at、holder_id、origin 由服务端写,客户端不可传
    • p0-2.4 forget 级联:派生 atom、卡带成员、向量、元信念
    • 验收:pytest tests/test_write_path.py tests/test_forget_cascade.py 绿;端到端「记住→重启→召回」
  • p0-3 检索改 SQL-only
    • p0-3.1 用 db.search_fts() 取代 list_atoms(),LIMIT top_k*10
    • p0-3.2 分词修复:CJK 分词 + camelCase/标识符切分 + 前缀
    • p0-3.3 空命中返回显式 no_match,不返回裸空数组
    • p0-3.4 过滤器:since/until/source/scenario/holder/modality/trust + 游标分页
    • p0-3.5 RRF 取代 0.55/0.35/0.10 加权混合
    • p0-3.6 MCP 与 HTTP 收敛到同一排序实现
    • 验收:MATCH "钥匙" 命中「我们的钥匙放在玄关抽屉里」;search atoms 命中 func SearchAtoms;100k atom p50 <100ms;pytest tests/test_retrieval_parity.py 绿
  • p0-4 卡带变成可挂载作用域
    • p0-4.1 mount / unmount / switch / append 四动词
    • p0-4.2 检索按已挂载卡带过滤(作用域,不再是全局)
    • p0-4.3 kv_fingerprint:model / tokenizer / chat template / adapter hash / rope / 量化 / 引擎版本
    • p0-4.4 fingerprint 不匹配则拒绝挂载,错误信息点名差异字段
    • 验收:pytest tests/test_cartridge_scope.py tests/test_kv_fingerprint.py 绿
  • p0-5 holder registry + who_knows
    • p0-5.1 holders 表 + registry CRUD
    • p0-5.2 who_knows 工具:返回 holder 与置信度,非空结果
    • p0-5.3 转介:visited-set + 深度上限 + 终止态 NO_HOLDER_KNOWS
    • p0-5.4 默认 answer-only 转介,不外泄原文
    • 验收:pytest tests/test_transactive.py 绿;钥匙测试场景通过
  • p0-6 证明套件去自证
    • p0-6.1 删 arena 答案入墨(evals.py:1408-1409 连接线、evals.py:1417-1426 白圈)
    • p0-6.2 删 best-of-N 重摇与达标即停
    • p0-6.3 删自造矛盾场景(出题者≠判分者)
    • p0-6.4 删缓存/证据图自触发式检查
    • p0-6.5 模型相关检查标 DEFERRED,不进 pass
    • 验收:python -m ely_eye.proofs --report 逐条 PASS/DEFERRED/REMOVED + 理由

阶段 2 — P1:让它成为产品

  • p1-1 图像进 dense
    • p1-1.1 选 embedding ≤500MB,双语 + 图像;记录模型 SHA 与体积
    • p1-1.2 图像 atom 入索引;query 侧支持图像
    • p1-1.3 视觉 token 计数改 32×32 口径(1280×720 ≈ 900)
    • p1-1.4 VLM 描述:代码路径就绪,标 DEFERRED
    • 验收:无文字图像被图像查询召回有 recall 数字;pytest tests/test_visual_tokens.py 绿
  • p1-2 音频摄入
    • p1-2.1 Modality 加 audio/transcript;未知扩展名不再静默跳过
    • p1-2.2 ASR 接入,转写 atom 带词级时间戳
    • 验收:pytest tests/test_audio_ingest.py 绿;ASR 跑通或明确 DEFERRED
  • p1-3 adapter 加载
    • p1-3.1 按卡带/场景解析 6 个 adapter,不再只有 runtime.py:317
    • p1-3.2 加载结果可观测:日志 + GET /api/runtime/adapters
    • 验收:状态端点列每个 adapter 的 loaded/deferred + 原因;桩权重测试证明加载分支执行
  • p1-4 工程地基
    • p1-4.1 pytest 从零到有(后端)
    • p1-4.2 go test 从零到有(MCP)
    • p1-4.3 CI 在 Linux runner 上跑真测试
    • p1-4.4 Dockerfile 不再 COPY 缺失的 PRD.md
    • p1-4.5 去掉 RTX 4090 / Ubuntu 硬编码,改可配置
    • p1-4.6 补 .sh 脚本,与 .ps1 对等
    • p1-4.7 LICENSE
    • p1-4.8 backup / restore / doctor
    • 验收:CI 绿;干净 clone 上 docker build 成功;桩 nvidia-smi 证明设备名不再致命
  • p1-5 产品面
    • p1-5.1 GET /api/search
    • p1-5.2 前端:搜索、浏览、forget、挂载卡带、时间显示、图像渲染
    • p1-5.3 types.ts 补回 time / source_id / trust / relations
    • p1-5.4 12 个未用端点:接上或删除
    • 验收:E2E 测试覆盖五项;grep 无未接端点残留
  • p1-6 代码卫生
    • p1-6.1 evals.py 2413 行拆到 ≤500 行/文件
    • p1-6.2 删 dead code 与未调用函数
    • p1-6.3 删 v1/v2/v3 废弃端口
    • p1-6.4 注释压到 ≤1 行
    • 验收:python -m bench.filesize_check 报无文件 >500 行;vulture/grep 证据

阶段 3 — P2:规模化、安全、分层与共享

  • p2-1 规模
    • p2-1.1 sqlite-vec vec0 增量索引
    • p2-1.2 MRL 截断 + int8/binary 粗排 + reranker 精排(reranker ≤2GB)
    • p2-1.3 100k 实测;1M 外推表 + 假设
    • 验收:100k dense 腿 p50 <100ms 实测;新 atom 立即可查有测试
  • p2-2 安全与隐私
    • p2-2.1 FastAPI 鉴权 + Origin 校验
    • p2-2.2 写入信任分级与隔离:未佐证不得被引用
    • p2-2.3 注入消毒:外来文本里的指令进隔离区
    • p2-2.4 擦除级联(GDPR 风格 forget)
    • 验收:无 token 写请求被拒;投毒用例在隔离区;级联测试绿
  • p2-3 短期/长期分层
    • p2-3.1 工作记忆层
    • p2-3.2 巩固台账 + watermark,可重放不重复计数
    • p2-3.3 importance / recall_count / last_recalled_at / decay / archived_at
    • p2-3.4 冲突记录持久化
    • 验收:LongMemEval 风格的知识更新与时间推理两类有分数;被 supersede 的旧事实不当作现行
  • p2-4 共享记忆
    • p2-4.1 多用户/多 agent 作用域 + ACL
    • p2-4.2 默认可见性:answer-only 转介
    • p2-4.3 能力卡:让外部 agent 能路由进 ely-eye
    • 验收:两 holder 互不可见;越权被拒;一次真实跨 holder 会话证明能力卡可用
  • p2-5 文档单一真相
    • p2-5.1 AGENTS.md 重写并停止 gitignore
    • p2-5.2 README.md 每条宣称指向可重跑命令
    • p2-5.3 TRAINING_ARTIFACTS.md 未跑分数删除
    • p2-5.4 Gaps.md 更新为已闭合状态
    • 验收:grep 无未跑模型分数残留;每条数字可重跑

终验

  • final-accept Gaps.md §6「钥匙测试」7 条场景逐条跑通
    • 验收:核对表列场景、命令、实测数字、通过与否;未通过不标绿;benchmark 报告与基线、外部基线、chance 行并排