Files
HomeAgent/docs/zh/memory-restructure-plan.md
JianFeeeee 1d52ba85d5 docs: 术语与架构措辞对齐(coldcontext / stablemem / security+device 模块)
1. 术语统一:
   - doc_query / doc_commit → coldcontext_query / coldcontext_commit
   - 「图记忆 / Graph 层」→「稳定记忆 / Stablemem 层(L3)」
   - 分层命名补上 L1/L2/L3 语义:Context(热) / Cold-context(冷) / Stablemem(持久)
   涉及 README.md、README_EN.md、assets/docs/{zh,en}/{ARCHITECTURE,OVERVIEW}.md

2. 补上本次最大的结构变化(文档里此前完全没有):
   - 安全防火墙作为**独立内核模块**:凭据闸门、工具黑白名单+沙箱、
     敏感词与提示词注入预警、按主体授权与插件命名限式;显式代理而非 monkey patch
   - device 数据面作为**独立内核模块**:工具注册与路由、inputch/outputch、设备登记;
     路由表在 internal/toolroute 以避免 core ↔ kernelboot 成环

3. 历史计划文档(memory-*.md / context-overflow-l4-design / entities-retirement /
   git-branching)加**归档说明**:明确术语已过时、现行口径以 ARCHITECTURE.md 为准。
   刻意不改写历史正文 —— 那是决策记录,改写等于伪造。

剩余旧词只出现在归档文档与 scripts/capability-bench/PLAN.md(评测历史记录)。
2026-10-27 09:58:14 +08:00

165 lines
10 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.

# 记忆系统重构计划:块节点统一载体
> **归档说明(2026-10-27)**:本文是历史计划,**术语已过时** ——
> `doc_query` / `doc_commit` 已改名为 `coldcontext_query` / `coldcontext_commit`,
> 图记忆(Graph)在对外语义上改称 **stablemem(稳定记忆)**。
> 现行架构与术语以 `assets/docs/zh/ARCHITECTURE.md` 为准;本文保留作为决策记录。
> 状态:**已执行完毕(2026-10-04)**。本文是当时的执行计划,保留作为决策记录。
> 日期:2026-10-02(计划) / 2026-10-04(完成)
>
> ## 执行结果摘要(2026-10-04 回填)
>
> | 步骤 | 状态 | 关键提交 |
> |---|---|---|
> | ① 重写设计文档 | ✅ | `6a0d6b7` `971cd72` `7248558` |
> | ② distill 沉库改块节点 | ✅ | `4b54c03` `1c260e3` `0586f6b` |
> | ③ memory_recall 加块召回路 | ✅ | `688ad08` `40a6acb` |
> | ④ memory_commit 止血 | ⚠️ 部分(见下) | `3bafe1c` |
> | ⑤ 存量迁移 | ✅ | `09f31e2` `e0a234c` `5a70452` |
> | ⑥ scene 挂接 | ❌ **取消**(见下) | — |
>
> ### 计划与实际的偏差(都是实测纠正的)
>
> **⑥ scene 挂接取消**:计划假设 blocks 是新层、scene 挂在它上面。
> 但 blocks 不是新层而是图节点升级体,scene 无处可挂。
>
> **蒸馏路线整体否决**(990b824):生产库 429 条实体里 **424 条(99%)无主语**,
> 而 `Split` 的第五道闸门「无主语不写悬空属性」会把 LLM 已拆对的字段全丢弃。
> 蒸馏命令已改为**只对有主语的实体跑**(b21410b),实际处理 5 条。
>
> **注意力网络路线否决**(`6a0d6b7` ~ `57eca02`):分域后 token F1 0.918
> 超过门槛,但**跨域不可泛化**(域外零字段),而通用 Agent 无法分域。
>
> ### 计划里未被采纳的替代方案
>
> - 「给块补语义描述」→ 实验否定(中性描述反而更差,排名 26 → 96,`7248558`)
> - 「给 URL 类事实换更强向量」→ 未测,但裸 URL 无文本可供语义匹配(`971cd72`)
>
> ### 现行架构
>
> ```
> memory_blocks 统一节点载体(文本/媒体都进这里)
> memory_block_edges source_kind → target_kind 多态端点,唯一边表
> graph_centroid 中心向量(迁移/蒸馏后自动重建)
> ```
>
> 原句由 `blk_src_<sha256>` 原句块承载(内容派生、幂等、不带向量),
> `sentences` 表的**写入点已清零**(迁移与蒸馏都不再写)。
>
> 详见 `production-recall-validation.md`(三十三~三十八节)、
> `recall-capability-tiers.md`(能力分层)、
> `vector-provider-decision.md`(provider 决策与缺口)。
> 背景:本会话前期的记忆系统改动(630d3f4 / 978bada / 69150d2 / 25d35f8)建立在对记忆架构的错误认知上,经用户逐条纠正后全部重审。本计划是重审后的执行路线。
> 前置事实核查:全部基于代码与真库实测,标注来源。
## 〇、认知纠错记录(为什么推翻重来)
| 错误认知(当时的做法) | 正确认知(用户纠正 + 代码核实) |
| --- | --- |
| 分层是 L0 对话/L1 文档/L2 graph/L3 blocks | L0=context / L1=doc / L2=graph。**blocks 不是新的一层**,是图节点从文本升级为带向量的多模态节点 |
| 给 entities 加向量字段(graph-vector-design.md) | **entities 该退场**。memory_blocks 已是带 vector+fingerprint 的节点载体(46f833c) |
| LLM 拆句产物 Commit 成 entities+relations(630d3f4) | 拆出的节点放入 block,保留原句;媒体引用是已删除的老设计(mediaref.go 只剩注释) |
| 三元组 = 我发明的 (主语,维度,值) | 三元组 =【节点】【关系边】【节点】;三元组设计文档在知识库 assets/knowledge/homeagent_architecture/content.md |
| provider 是蒸馏的配件 | provider 是内核的模型接入接口,内核对 tfidf 等传统算法与向量化 LLM **无感**,payload 一致才能动态接入 |
| 多模态要"引用"进图 | 多模态节点由 LLM 多模态能力拆分入库 |
| 场景/scene 表忽略不看 | scene 是第三种召回:条件型记忆不吃词法/向量相似,按场面**前缀匹配**直接取回(scene.go:12-38) |
沉痛教训:630d3f4 的 16 三元组端到端验证——验证的是拆分质量,不是正确的落库形态。**验证通过 ≠ 架构正确。**
## 一、目标形态(一句话)
```
入库:拆解器(provider 接口,内核无感)把对话/文档/媒体拆成
memory_blocks 节点(modality/text_content/vector/fingerprint)
+ memory_block_edges 结构边;原句保留为块。
召回:块向量检索为主 + scene 场景召回补条件型记忆;
entities+LIKE 是存量过渡路径,逐步退场。
```
## 二、既有设施盘点(不重复实现)
| 能力 | 已有 | 位置 | 状态 |
| --- | --- | --- | --- |
| 块节点存储 | `PutMemoryBlocks` / `MemoryBlocks` | internal/memory/block.go:67/144 | ✔ 可用 |
| 结构边 | `AddMemoryBlockEdge`(端点校验,测试 453/491 行) | block.go:199 | ✔ 可用 |
| 块检索 | `BlocksForNode`(按端点反查) | block.go:254 | ✔ 可用 |
| 向量检索 | `vector.Store`(Insert/SearchScored/Remove) | internal/memory/vector/store.go | ✔ 可用 |
| provider SPI | `pkg/embedding`(Embed/Info/Close,Fingerprint) | pkg/embedding/embedding.go | ✔ 可用 |
| VL 权重双模式 | qwen3vl(Embed 出向量)+ qwen3vlgen(tied head 生成) | 25d35f8 | ✔ 已验证 |
| 文本拆解 | distill.Extractor(schema 约束+闸门,16 三元组/幻觉0) | internal/memory/distill | 拆分逻辑可复用,**沉库去向要改** |
| 旧实体迁移先例 | `MigrateLegacyMediaEntities` | internal/memory/migrate.go:47 | ✔ 方向先例 |
| scene 召回 | `RecallByScene` | internal/memory/scene.go:323 | ✔ 已有 |
| 拆解生成模型 | ollama+qwen3:1.7b(schema 6/6 零幻觉) | providers/ollama | ✔ 端到端验证过 |
## 三、分步执行
### 第 1 步:重写两份设计文档 ✗ 未开始
- [ ] 删 `docs/zh/graph-vector-design.md`(给 entities 加向量——前提错误)
- [ ] 重写 `docs/zh/triple-distill-llm-design.md`:拆解产物=块节点;entities 标注退场路径
- [ ] 修正 `providers/qwen3vlgen` 与 `pkg/generation` 的定位注释:不是"蒸馏配件",是入库拆解器的模型面
- **验收**:文档里的每张表名/函数名都能 grep 到;无 entities 加向量字段的任何残留表述
### 第 2 步:distill 沉库改块节点(核心) ✗ 未开始
- [ ] `distill.Extractor.Split` 产出改为块结构(新增 `distill.Blocks` 类型,保留 Triple 输出做对照期)
- [ ] 原句 → text block(text_content=原句, vector, fingerprint)
- [ ] 字段 → text block(text_content=字段内容, vector, fingerprint)
- [ ] 边:原句 block —contains→ 字段 block
- [ ] 向量来源走 `pkg/embedding`(默认 provider 由配置决定;无 provider 时 vector 留空+fingerprint 空,**不静默编造向量**)
- [ ] pipeline.go 的 distillBatch 改调块写入
- **判据**:
- [ ] 拆一条真实记录后,MemoryBlocks/MemoryBlockEdges 能查到原句块+字段块+边
- [ ] 断电安全:PutMemoryBlocks 是事务(block.go 已有端点校验事务先例)
- [ ] 无 provider 时块仍入库(vector=''),检索退化为符号路,**不报错不编造**
- **变异自证**:把"contains 边"删掉,块查询测试必须红
### 第 3 步:memory_recall 增加块召回路 ✗ 未开始
- [ ] GraphDB 增加 `RecallBlocks(queryVec, topK)`:走 vector.SearchScored
- [ ] 工具层 `memory_recall`:块向量召回为主,entities LIKE 为存量兜底,输出统一格式
- [ ] 工具名/参数不变(调用方模型无感知)
- [ ] fingerprint 不匹配时**拒绝比较**(报"向量空间已变更,需回填"),绝不拿 2048 维查询比 512 维库
- **判据**(真库 + 跨记录探针,用本会话已验证的判据):
- [ ] 跨维度定位 ≥4/5(现状 LIKE 0/5)
- [ ] 端口类数字串不退化(向量弱、符号强——混合必要性)
- **变异自证**:去掉块召回路,跨维度判据必须退化到现状水平
### 第 4 步:memory_commit 同步止血 ✗ 未开始
- [ ] description 明确要求:拆解后走块形态;禁止 43-50 字符整句实体
- [ ] 工具实现侧加闸门:超长 subject 拒绝并提示(与 distill 闸门同款)
- **判据**:新写入的实体长度分布不再出现 >25 字符主体
### 第 5 步:存量迁移 ✗ 未开始
- [ ] 沿 `MigrateLegacyMediaEntities` 方向写 `MigrateLegacyTextEntities`:
188 实体 → 原句块(带向量)+ 字段块 + contains 边
- [ ] 独立命令入口(参照 homed-kb-migrate),**可回滚**:导出原库快照→迁移→对比→确认后替换
- [ ] 孤儿实体(49/108 无关系无原句)保持原样,不编造
- **判据**:迁移后跨维度探针 ≥4/5;原库快照可完整回滚
### 第 6 步:scene 挂接(可选,后置) ✗ 未开始
- [ ] 拆解块带 scene 键(memory_blocks.scene 字段已在 schema 里)
- [ ] 验证 `RecallByScene` 对块节点生效
- 前置:第 2-5 步稳定后再做
## 四、每步通用约束
1. **provider 边界**:内核不出现任何 provider 名分支;一切经 `pkg/embedding`/`pkg/generation` 接口
2. **禁止静默错误**:fingerprint/维度不匹配必须显式报;无向量必须显式留空
3. **回填必落盘**(document.go:137 陷阱:不落盘则判定条件永远成立、每次启动重算)
4. **变异自证**:每条关键判据写测试后,手动破坏对应实现,测试必须变红
5. **长任务纪律**:导出/回填等内存密集任务**绝不与测试并行**(本会话三次 OOM 教训)
6. **验证通过 ≠ 架构正确**:每步完成后对照本计划第一节逐条自检
## 五、暂不做
- entities 表的物理删除(存量过渡期内仍服务 LIKE 兜底)
- KV cache(qwen3vlgen 全量前向 O(N²),生成低频,已注释标明)
- 多模态拆解(第 6 步之后,依赖块路稳定)
- 切换生产实例 /var/tmp/ha-c 的 provider 配置(需人工确认)