# 语义架构规范 v2（SEMANTIC-ARCHITECTURE-SPEC）

> **效力**：本文件是 dsh-auto-memory **语义/检索侧的规范性约束**。此后任何涉及检索、嵌入、分块、融合、重排、注入内容取舍的改动，**必须先在此规范里找到条款依据**；规范没写的做法不得直接进主干（先补条款 → 再施工）。
> **配套**：三层架构的接口与预算见 `docs/internal/THREE-LAYER-CONTRACT.md`（C1–C7）；排期与阶段门见 `docs/internal/TODO-GRAPH.html`、`TODO-BACKLOG.md`。
> **建立**：2026-09-14。依据 = 用户指定的规范来源（B 站《7 分钟了解 10 种 RAG 策略》BV17J3B6PEGK）+ 通用 advanced-RAG 技术谱系（见附录）。
> **版本**：**v2**（2026-09-14，条款增删 → 按 §6 第 4 条升版；变更记录见 §0.1）。

---

## 0. 引用纪律（怎么用这份规范）

1. 提改动时**引条款号**（例："这次改动落在 S2.1 查询改写"），并在卡片/日志里写明。
2. 每条条款都有 **判据（能失败的断言）**。没有判据的条款视为未生效，不得作为"已实现"申报。
3. 改动**必须带离线对照**（S7）：改前基线 / 改后数字 / 样本数 / 判据是否翻过。
4. 规范与实现冲突时：**要么改实现，要么走变更程序改规范**（§6），不允许"实现先跑，规范后补"。

### 0.1 变更记录

**v2（2026-09-14）** —— **依据「用户 2026-09-14 裁定」**。三处改动：

1. **§7 立场（成本模型）改写为分档表述，并承认此前的定调错误。** 旧 §7 的成本对照表按「轻度用户」写，**并把兼容档的约束（检索路径零 LLM 调用）升格成全项目硬约束（S9 标 MUST）**，等于禁止首要用户（最优档）使用更重的方案。**这是定调错误**：首要用户 = 项目作者本人 = 重度档/最优档，以**最优**为设计目标；兼容档的准确含义是**「降级路径必须存在」**，不是「按最省设计」。
2. **S9 由单条 MUST 改为分档条款**：S9.1（兼容档 MUST：检索路径零额外 LLM token + 弱设备可跑）／S9.2（最优档：允许更重方案含 LLM，但每条必须带「成本 + 门控 + 降级路径」三件套）／S9.3（两档共用同一套接口与判据）／S9.4（禁止把"只能跑通兼容档"的设计当目标形态）。旧 S9.1 的"三件套"要求移入新 S9.2；旧 S9.2 的"决策层本地承担"移入新 S9.1；旧 S9.3 的"成本归属"并回 §7。
3. **数值口径统一到代码真值**（见 §0.2）：消除规范 / 契约 / 代码三方漂移（`B2` 曾 2000 与 2400 并存；`injectBudgetChars` 曾写 4800；`tier0MaxTokens` 的 400 与 `B0`=800 被误当作矛盾）。**自本次起，规范里出现的每个数值都有代码出处，且由一致性守卫测试机械校验**（`tests/smoke/smoke-test-doc-code-consistency-pre.mjs`：代码默认值 ↔ 文档「默认」标注，任一侧漂移即报红）。

### 0.2 数值口径（**代码真值**，与文档不一致时以本节为准；守卫测试按本节模式解析）

| 字段 | 代码真值 | 出处（实读） | 口径说明 |
| --- | --- | --- | --- |
| `injectBudgetChars` | 默认 **8000** | `lib/index.js:226` | **可配置项**（设置页「记忆窗口 → 注入预算」可调）。沿革：1600 → 2000（2026-09-14 用户裁定，Tier-0 目录要在原预算内"免费"塞入是不可能的）→ **8000**（2026-09-15 用户裁定：实测 2000 下用户级记忆只有约 **9%** 能进注入，"在场但只看到个开头"）。**口径（勿用"越大越好"调它）**：本门是「**摘要内联**」额度，全文靠 `memory_recall_pre` / `memory_read_pre` 按需下钻；要装下更多语料应抬 Tier-0 目录配额（`tier0BudgetShare` / `tier0MaxTokens`），而不是把本值推到几万。8000 字符 ≈ 4000 token ≈ 1M 窗口的 0.4%；配合**分级注入**（完整版每 `snapshotMinGapRounds` 轮一次）平均约 1300 token/轮。历史文档里的 `4800` 是更早的旧默认值，已作废。 |
| `tier0CatalogEnabled` | 默认 **true** | `lib/index.js:220` | C5 Tier-0 常驻目录开关；设 false 回到旧快照。 |
| `tier0MaxTokens` | 默认 **400**；硬上限 `B0` = 800 | `lib/index.js:224`（默认值）、`lib/tier-layer-inject-pre.js:33`（`B0`） | **400 与 800 不是矛盾，而是「默认值 vs 上限」之别**：800 是硬上限（超设无效），默认取上限的一半（实测 9 条仅 234 token，留余量且不挤证据层）。 |
| `tier0BudgetShare` | 默认 **0.25** | `lib/index.js:227` | Tier-0 目录最多占「注入预算」的比例；与 `tier0MaxTokens` 取小生效。 |
| `l0IndexEnabled` | 默认 **true** | `lib/index.js:416` | C3 L0 向量索引接线开关（**2026-09-14 用户裁定改为默认开**）；设 false 即回到"零 IO、零嵌入、零目录"。 |
| `B0`（Tier-0 token 上限） | **800** token | `lib/tier-layer-inject-pre.js:33` | 硬上限（`TIER_BUDGET_PRE_V1.B0`）。 |
| `B2`（Tier-2 单块字符上限） | **2400** 字符 | `lib/tier-layer-inject-pre.js:36` | `TIER_BUDGET_PRE_V1.B2` = 2400。此前契约 §1.2 / §5 曾写 2000 → **已统一到 2400**（见 §0.1 第 3 条）。 |

---

## 1. 总链与分层（固定骨架）

```
①产生 → ②存储 → ③选取 → ④注入 → ⑤晋升 → ⑥呈现
```

语义只在前四步产生价值：**②存储**（分块/嵌入/索引）、**③选取**（查询理解 → 召回 → 精排 → 决策）、**④注入**（压缩/预算/降级标注）。①⑤⑥ 不含语义算力（⑥ 只负责把结论显示给人看）。

**三层检索（接口已冻结）**：Tier-0 常驻目录（≤ `B0`=800 token，**实际默认 `tier0MaxTokens` = 400**）→ Tier-1 L0 摘要（≤ `L1`=140 字/条，`K`=8）→ Tier-2 原文块（单块 ≤ `B2`=2400 字）。语义算法改造**只允许改层内的算法**，不得改层间的接口形状。
数值一律以 §0.2「数值口径（代码真值）」为准（`B2`=2400 出自 `lib/tier-layer-inject-pre.js:36`；历史上出现的 2000 是旧写法，**已统一到 2400**）。

---

## 2. 规范条款

### S1 存储：分块与增量（MUST）

- **S1.1** 每个可检索单元必须有**内容寻址的稳定 ID**（块 ID = f(记忆 ID, 记录摘要, 序号)），且**块 ID 必须同时是嵌入缓存键的一部分**。
- **S1.2** 嵌入缓存键 = **引擎身份**（引擎 + 模型 + 维度 + 归一化方式）+ 块 ID。**换引擎即整库重建**（双引擎是 **OR** 关系：兼容档 = 端侧 JS `multilingual-e5`，最优档 = Python `BGE-M3`；两档共用同一套接口，见 S9.3）。
- **S1.3** 写一条记忆**不得**引起全库重嵌：只嵌入新增块，删除/修改走 **supersede 声明 + tombstone**（屏蔽必须落在持久层，且检索层与注入层双层生效）。
- **现状（证据）**：块 ID 已内容寻址（`python/m7_embedding_pre_v1.py:75` `chunk_id_for`）但**未用作缓存键**；索引身份是**整份语料哈希** `memoryIndexVersion`（`lib/index-sync-pre.js:57`），任何细节改动 → miv 变 → 全量重发 + 全量重嵌。
- **缺口**：S1.2 未落（缓存键无引擎身份）；S1.3 未落（现为全量重算，即"草台"根因）。
- **归属**：阶段 C（P0-④「记忆增删 × 少重建」）。

### S2 选取-查询侧：先理解查询，再检索（SHOULD，本仓最大空白）

- **S2.1 查询改写/扩展**：检索前把原始 query（用户消息 / CoT 段 / 助手输出）规范化为**检索式查询**（去指示词、补省略主语、展开本仓专有名词）。
- **S2.2 多查询（multi-query）**：对复杂 query 生成 2–4 个改写，各自召回后**共同融合**（不是取并集后就地打分）。
- **S2.3 HyDE 类假设文档**：仅在短查询、零词法命中时启用（成本门控），生成假设答案再嵌入检索。
- **S2.4 查询侧一切加工必须在 `SHOULD` 预算内**（离线可关、失败即回退原始 query，绝不因改写失败导致检索为空）。
- **现状**：**三项全无**。现在直接把原始段文本送去检索（`lib/context-host-pre.js:347` 用 `buildObserveWindowText` 的原文）。
- **缺口**：这是"语义算法底层改进"里**收益最大、风险最低**的一档（不碰索引、不碰接口）。
- **归属**：阶段 C。

### S3 选取-召回侧：混合臂 + 融合（MUST）

- **S3.1** 至少两臂：词法臂 + 稠密语义臂；时间/来源可作软性第三臂（**只提升不硬过滤**）。
- **S3.2** 融合必须**在排名空间**做（RRF 等，`k`=60 复用 `FUSION_RRF_K_PRE_V1`）；**禁止在分数空间加权**（不同臂的分数量纲不可比）。
- **S3.3** 单臂失败必须**降级可运行**（语义臂不可用 → 纯词法），但**必须显式标注降级**（见 S5.3），不得静默。
- **S3.4** 语义臂的可用性必须**可被外部观测**（不能只在日志里）。
- **现状**：✅ 已实现且合规 —— 词法（BM25 式 + 词法包含）+ 稠密（`c3 dense_search` → `_jsSemanticRank`）+ 时间臂；RRF 融合在 `lib/index.js:4068`（`P8`），host 侧 `fuseD6Pre`（`lib/context-host-pre.js:368`）；降级链 python → JS → 纯词法。
- **缺口**：S3.4 部分（本轮 C7 已让 0-1 分与排名进注入文本；仍需一条"引擎/臂身份"标注）。

### S4 选取-精排：召回与打分分离（SHOULD）

- **S4.1** 召回与精排分两级：召回要"全"（宽候选），精排要"准"（窄结果）。当前只有一级（融合序直接当最终序）。
- **S4.2** 精排器可以是 cross-encoder（重）或 LLM 判断（贵），**必须离线可评估**；无资源时允许用"融合序 + fv2 决策"作为近似，但要在规范里标为**近似**。
- **现状**：❌ 无独立精排级。现有"精排"实际由 fv2 决策（emit/prefetch/suppress）承担**注不注入**的判据，不负责**排序**。
- **归属**：阶段 C；先做近似（S4.2 后半），有资源再上 cross-encoder。

### S5 注入侧：压缩 + 预算 + 降级（MUST）

- **S5.1 上下文压缩**：注入的是"提炼后的结论"，不是原文转储（Tier-0 每条 1 行）。
- **S5.2 预算分层**：Tier-0 ≤ `B0`、Tier-1 ≤ `L1/K`、Tier-2 单块 ≤ `B2`；**per-layer 配额**（project ≤ 60%·`B0`，whiteboard/user 各保底 10%）。
- **S5.3 不静默降级**：任何一层缺数据都要**显式标注**（I7）；索引未就绪**不得静默丢弃**注入。
- **S5.4 可观测**：注入块必须带 **0-1 相似度 + 批内排名**，且按分值降序。
- **现状**：S5.1 ✅（C4 Tier-0 生成器，真实语料 788 token ≤ 800）；S5.2 部分（配额待 C5 落）；S5.3 ❌（07:43–08:35 的 `index-not-ready` 静默丢弃即违规现场）；S5.4 ✅（本轮 C7）。
- **归属**：C5 + P0-④e。

### S6 决策：自适应检索（MUST，本仓强项）

- **S6.1** 每次注入前必须有一次显式决策（要不要检索/注入），判据可解释：`lane`（explicit/proactive）+ `intent` 概率 + 融合 `margin` + **echo veto**（防复述用户刚说的话）+ 硬门（有害/纠正/过期/越界）。
- **S6.2** 决策必须记录**可复算的特征快照**（intent/dense/margin/candN/hit），供离线回放。
- **S6.3** 冷却与预算门必须与决策同级（防连续唤起烧 token）。
- **现状**：✅ 已实现（fv2 策略工件 `lib/policies/activation_policy_pre_v2.json`；JS 判定核 `lib/context-host-pre.js:417+`；shadow 日志 `~/.dsh/memory/semantic-pre/activation-shadow.jsonl`）。
- **注意**：S6 是本仓**领先项**，改造其他条款时**不得削弱**它。

### S7 评估：没有实验就没有资格改算法（MUST）

- **S7.1** 任何语义/检索改动必须产出**三策略对照**：A 只给上层目录 ／ B 上层 + 命中条摘要 ／ C 上层 + 候选集全灌（上限 `injectBudgetChars`）。
- **S7.2** 指标固定四项：**命中率**（top-1/top-3/top-5）、**下探后答案可达率**、**注入 token 成本**、**噪声比**。
- **S7.3** 判据必须**能失败**（能力可达性）：判据写成"若实现被改坏则报红"，不允许"声明了就算过"。
- **现状基线（2026-09-14，词法臂，语义臂卡死期间）**：3 条事实 → L0 top-1 命中 **1/3**、top-5 命中 **2/3**；命中原因**全是词法、0 条带语义分**；观察到单字母 token 污染（`词法×3(agent,检索,a)`）。
- **归属**：E1/E3（P1-⑯）。**语义臂已恢复，可正式跑**。

### S8 观测与再现（SHOULD）

- **S8.1** 每条注入/召回都要能回答：**用了哪条记忆、多像（0-1）、为什么选它、来自哪个引擎**。
- **S8.2** 语料/索引/向量三件套的身份必须能在一次诊断里全部打印（否则无法复现"分数整段消失"这类故障）。
- **现状**：S8.1 部分（C7 给了相似度与排名；引擎身份未标注）；S8.2 部分（`~/.dsh/dsh-auto-memory-pre.json` 诊断 + 向量文件时间戳，本轮排查靠它定位）。

### S9 成本：**分档条款**（兼容档 MUST 零额外 LLM token；最优档允许更重方案，但必须带三件套）

> **定调更正（2026-09-14 用户裁定）**：S9 在 v1 里写成**单条 MUST —— "检索路径默认零 LLM 调用"**。那是把**兼容档**的约束升格成了**全项目硬约束**，等于用规范禁止首要用户（最优档）使用更重的方案。**这是定调错误**，v2 改为分档：

- **S9.1（兼容档 · MUST）** 兼容档的检索路径（分块 → 嵌入 → 召回 → 融合 → 决策 → 压缩 → 注入）必须**零额外 LLM token**，且必须在**弱设备**（无独显、内存紧张）上跑得动 —— 嵌入走端侧小模型（JS `multilingual-e5` 档）或纯词法，决策层由**本地模型或规则**承担（当前 = fv2 线性分类器，0 token），压缩用本地规则。**"降级路径必须存在"是这一档的唯一硬含义**：任一环节不可用时必须能退到更轻的实现并**显式标注**（S5.3 / I7），而不是整条链路失败。
- **S9.2（最优档 · 允许更重方案，含 LLM）** 最优档（首要用户 = 项目作者本人）**允许**在检索路径引入 LLM 的方案（如 LLM 查询改写、LLM 精排、LLM 自省、多轮检索），但**每条方案必须同时给出三件套，缺一不可**：
  1. **成本**：谁付费、按什么计价（每轮 token 数 / 每轮延迟 / 设备算力），必须是可测的数字而不是形容词；
  2. **门控条件**：什么情况下才启用（查询复杂度阈值、冷却、预算余量、用户开关），以及谁有权把它关掉；
  3. **无 LLM 时的降级路径**：LLM 不可用 / 超预算 / 报错时退化成什么（规则实现或端侧小模型），且这次退化**必须显式标注**。
  三件套不齐的方案**不予采纳**（此条沿用 v1 S9.1 的要求，只是作用域从"全项目一律禁止"改为"最优档若要用就必须补齐"）。
- **S9.3（两档共用同一套接口与判据）** 分档**只换引擎与预算，不改契约形状**：Tier-0 / Tier-1 / Tier-2 的接口、字段、注入块的形状、条款判据在两档下完全一致（这正是三层接口已冻结的原因）。**禁止**为某一档设计专用接口、专用字段或专用判据；也**禁止**把两档做成两套代码路径 —— 只有"同一路径 + 不同引擎/预算"。
- **S9.4（禁止把兼容档形态当目标形态）** **禁止把"只能跑通兼容档"的设计当作目标形态。** 任何在弱档才成立的设计（纯词法、无精排、零端侧模型）都必须标注为**降级形态**，不得写成"目标架构"或据此否决最优档的更强方案；反之，最优档的重方案也必须证明它**不破坏 S9.1 的降级路径**（否则就等于砍掉了第二目标）。
- **现状**：两档各自合规 —— 兼容档 ✅（决策 = fv2 本地 LR，嵌入 = 端侧 e5，压缩 = 本地规则）；最优档 ✅（Python `BGE-M3` 语义引擎在用，属"更重的本地模型"，不是 LLM 路径）。⚠️ 风险点仍是 S2 的查询改写：兼容档按 S9.1 必须本地做（规则 + 术语表 + 端侧小模型）；最优档若要用 LLM 改写，**按 S9.2 补三件套**，不许一句"效果好"就进主干。
- **归属**：贯穿阶段 C；审计纳入 P1-⑨（审计对象 = **兼容档是否守住 S9.1 + 最优档每条重方案是否有三件套**，而不是"路径上有没有 LLM"这道一刀切）。

---

## 3. 与三层契约（C1–C7）的关系

| 规范条款 | 对应契约项 | 状态 |
| --- | --- | --- |
| S5.4（注入可见相似度） | **C7** | ✅ 已落（29 断言） |
| S5.1/S5.2（压缩 + 配额） | **C4 / C5** | C4 ✅，C5 ✅（套件 83 断言 0 失败；待宿主重启复核） |
| S1.3（增量/少重建） | P0-④ / P0-④e | 待做（草台根因） |
| S2/S4（查询侧 + 精排） | 规范新增，无对应 | 待做（本轮立规） |
| S3/S6（混合臂 + 决策） | 已有实现 | ✅ 合规，改造时不得削弱 |

**结论**：本轮之前，检索侧的"算法"只有 S3/S6 是对的；S1/S2/S4/S5 是草台。规范立起来之后，**改造范围就是 S1/S2/S4/S5**。

---

## 4. 阶段门与起点（"什么时候开始做语义算法底层改进"）

**硬约束（用户已定）**：三层架构（Tier-0/L0/原文）必须**先过真实验收**，再进语义算力改造 —— 三层是语义的**接口**，接口没冻结就改算法 = 白改。

| 阶段 | 内容 | 起点条件 | 可否并行 |
| --- | --- | --- | --- |
| **A · 接口冻结** | C3（接线 L0 向量索引 + 显式落 layer/status）→ C5（Tier-0 常驻 + 闸门 + 配额 + I7 降级标注）→ C6（三层验收套件） | 现在，已开工 | — |
| **B · 立规与审计**（本轮已启动） | 本规范（**已升至 v2**：§7/S9 分档更正 + 数值口径统一）；按 S1–S8 逐条审我方实现；跑 E1/E3 三策略对照，把 S7 基线补齐（**语义臂已恢复**） | 只要不跨界改接口即可开工 | ✅ 与 A 并行 |
| **C · 算法改造** | 按 §2 的施工清单动算法：S1 增量缓存 → S2 查询侧加工 → S4 精排 → S5.3 降级标注收口 | **A 全绿 + 宿主重启复核注入 Score 钉子** | ❌ 不得与 A 抢同一批文件 |

**直接回答**：
- **语义算法（真改造）的起点 = 阶段 A 通过验收的时刻**（C3/C5/C6 全绿，且注入块里能看到 `Score: 0.xx (rank n/m)`）。按当前进度，A 是**今天到明天**这一档的工作量。
- **但语义工作今天就已经开始了**，走的是 B 线：立规范（本轮）+ 补实验基线（E1/E3，今天可跑）。B 线不碰接口，不需要等 A。
- 阶段 C 的施工顺序（按风险/收益比）：**S1.3 增量少重建**（止血，草台根因）→ **S5.3 降级标注**（止血，防"分数整段消失"复发）→ **S2 查询侧加工**（收益最大）→ **S4 精排**（最后做，最贵）。

### 4.1 不采纳项（明确写下，免得后面照搬）

- **迭代式 RAG（多轮检索/自省循环）：不作为默认形态（两档皆然）。** 我们的场景是**每轮自动注入**，多轮检索会把延迟与 token 乘 2 以上；而"要不要检索"已由 S6（fv2 决策 + echo veto + 冷却）承担。迭代式 RAG 适用于"单次检索明显不够 + 允许用户等"的问答场景，不是本仓形态。**最优档若仍想提出**（例如把"自省一轮"作为可选增强），按 **S9.2** 补齐成本 + 门控 + 降级路径三件套后可以讨论 —— 但**兼容档的默认路径不采纳**（S9.1）。
- **重排（S4）：先做近似级，cross-encoder 留给最优档。** 在候选池（`K`=8 / `B0`=800）与 S7 实验判据就绪前直接上 cross-encoder，只会增加延迟而无从证明它比 RRF 融合序更好 —— 所以**先做 S4.2 的近似级，用实验说话**；等判据就绪后，cross-encoder 作为**最优档**的重方案上线（按 S9.2 标注成本），**兼容档保持近似级不变**。
- **多引擎混排：禁止。** 双引擎是 **OR** 关系（JS `multilingual-e5` 或 Python `BGE-M3`），切换即整库重建；两套向量空间混排即错误结果（S1.2）。切换引擎属**换档**（S9.3 允许换引擎与预算），不属"改契约形状"。

---

## 5. 施工清单（阶段 C，按顺序）

1. **S1.3** 块级增量：块 ID 进缓存键，只嵌新增块；修改走 supersede，屏蔽落持久层（检索+注入双层）。
2. **S5.3** 索引未就绪不得静默丢弃：显式降级标注 + 防抖（防"写记忆把索引饿死"）。
3. **S2.1/S2.2** 查询改写与多查询：**先落 S9.1 的本地实现**（规则 + 术语表 + 端侧小模型，离线可关、失败回退原文）；最优档若要升级为 LLM 改写，按 **S9.2** 带三件套另立一条，不覆盖本地路径。
4. **S2.3** HyDE（有成本门控时才开 —— 该档的"成本门控"就是 S9.2 的第 2 件套）。
5. **S4.2** 精排近似 → cross-encoder（兼容档停在近似级；最优档上 cross-encoder 时按 S9.2 标注成本）。

**每条都必须带**：S7 的三策略对照 + 一条能失败的断言 + 引用的条款号。

---

## 6. 变更程序

1. 改实现前，先在 §2 找到/新增条款；新增条款需写明 **判据**。
2. 判定标准冲突时以**用户拍板**为准，并把结论写回本规范（标注日期与来源）。
3. 每次改完，在 `TODO-GRAPH.html` 对应卡片引用条款号（如「按 S2.1」）。
4. 规范版本号只在**条款增删**时递增（v1 → v2）；措辞澄清不改版本。

---

## 7. 立场：在「RAG 已死」的语境下，我们为什么仍走检索路线（**分档成本模型**）

**判据不是"RAG vs 长上下文"，而是"谁付费" —— 而"谁付费"必须分档回答。**

### 7.1 先把定调更正：这是一份**分档**规范，不是"轻量版"规范

**首要用户 = 项目作者本人 = 最优档（重度档）**：以**最优**为设计目标 —— 愿意承担更重的算法、更重的本地模型（当前 = Python 语义引擎 `BGE-M3` 档）、更长的注入预算，换检索质量。

**第二目标 = 兼容档**：「兼容」的准确含义是 **"降级路径必须存在"**，**不是** "按最省设计"。兼容档存在的意义是让同一套架构在弱设备 / 零额外 token 的普通用户机器上**也能跑通**，而不是给全项目的设计目标定一个最省的下限。

> **我们此前把兼容档的约束当成了全项目硬约束，这是定调错误。** 旧 §7 的成本对照表是按"轻度用户"写的，旧 S9 又标 MUST 写"检索路径默认零 LLM 调用"——两者合起来等于：**用规范禁止最优档使用更重的方案**（而最优档才是首要用户）。v2 起：
> - §7（本节）的成本模型**分档表述**（两档各自的"谁付费"、各自该选什么）；
> - S9 **降级为分档条款**（S9.1 兼容档 MUST / S9.2 最优档带三件套 / S9.3 共用接口与判据 / S9.4 禁止把兼容档形态当目标）；
> - **判定依据**：用户 2026-09-14 裁定。

### 7.2 「RAG 已死」的两个替代方案：成立，但各带隐含前提

（均已核验，见附录 F）

| 替代方案 | 真实主张 | 隐含前提 | 对最优档 | 对兼容档（轻度用户） |
| --- | --- | --- | --- | --- |
| **LLM Wiki**（Karpathy） | 传统 RAG 的病是**没有知识积累**：每次查询都从零重新发现。改为让 LLM 渐进维护一份持久 wiki（实体页/概念页/交叉引用/矛盾标注/综合结论），靠 `index.md` + `log.md` 导航 | 写入侧与维护侧**由 LLM 长期承担**；原文明确说在"约 100 份资料、数百页"规模下**可避开嵌入式 RAG 基础设施** | ✅ 可用：写入侧 token 可接受（我们已模板化），查询侧多花 token 换整合质量，最优档付得起 | ⚠️ 写入侧 token 可接受（可模板化），但查询侧仍要 LLM 读页 —— 只能作为**可选增强**，不能作为这一档的默认路径 |
| **Grep agentic**（Claude Code） | "早期版本用了 RAG + 本地向量库，很快发现 **agentic search 更好**"；"模型驱动的 glob 和 grep 打败了一切"；GrepTool 默认只回文件名（控信息量）、`head_limit` 250 防淹没 | **每轮多轮 LLM 工具调用**（token 乘数）+ 语料是**精确 token 可匹配**的（代码/路径/标识符） | ⚠️ 可以做**补充臂**（最优档付得起多轮 token），但我们的语料是自然语言记忆，**没有可 grep 的字面**这一条对它同样成立 | ❌ 多轮即乘数；且自然语言记忆**没有可 grep 的字面** |

### 7.3 两档的成本对照（**各自"谁付费"**）

| 路线 | 谁付费 | 最优档（首要用户） | 兼容档（第二目标） |
| --- | --- | --- | --- |
| 长上下文 / 全灌 | **贵侧（token）**：每轮 token ∝ 语料规模 | ⚠️ 语料增长 = 成本线性增长；即便最优档，注入预算（`injectBudgetChars` 默认 8000 字符，可配）也是硬约束 | ❌ 最不可取：语料增长即成本增长，而这一档正是 token 敏感 |
| Grep agentic | **贵侧（token）**：每轮多次 LLM 调用的乘数 | ⚠️ 可作补充臂；探索式检索的收益靠多轮 token 买 | ❌ 最贵的一档 |
| LLM Wiki | **贵侧（token）**：写入/维护侧 LLM token（查询侧读页也要） | ✅ 可上：整合质量换 token，我们已把写入模板化（自动沉淀 + 结构化日志） | ⚠️ 可用，但必须模板化写入；查询侧读页的 token 只能按需触发 |
| **本地检索（本仓主线）** | **设备侧（算力）**：一次性索引 + 每轮端侧嵌入，0 token | ✅ 主线；**允许在检索路径叠加 LLM 增强**（按 S9.2 带三件套），因为这是首要用户 | ✅ 唯一完全契合的一档：零额外 LLM token、弱设备可跑 |

**两条推出结论（按档读，不要拉平）：**

1. **本质规律不变**：把成本从**设备**挪到 **token**，就是"轻档付不起、重档可能付得起"。所以对兼容档这类改动**默认否决**（S9.1）；对最优档则是**可选项，按 S9.2 三件套评估**。
2. **RAG 没死，死的是两种东西**：① 把检索质量**外包给大模型**的 RAG（LLM 改写 / LLM 重排 / 多轮自省）—— 在**兼容档**它确实"死"了（付不起），在**最优档**它是**可选增强**；② **不分场景**的朴素 RAG。本地检索恰恰是唯一能"**用小模型换大模型 token**"的机制，这在兼容档最不该死，在最优档是**底座**（而不是上限）。

### 7.4 我们的答案：吸收两条路线的优点，按档决定谁付哪部分成本

1. **吸收 LLM Wiki 的"索引即自然语言"**：我们的 Tier-0 目录 + L0 摘要 = Karpathy 的 `index.md`（先读索引、再深入），而**不是**只有向量的黑盒块。层级 = sources（原文）／wiki（笔记、白板、每日日志，LLM 写）／schema（本规范 + 项目笔记）。
2. **吸收 agentic 的"要不要搜由智能判断"**：**兼容档**把判断交给**本地线性分类器（fv2，0 token）**，即"零 token 的 agentic 检索"；**最优档**允许在判断链上叠加更重的模型（按 S9.2 三件套），但**默认路径仍是 fv2**——因为它是两档共用接口的一部分（S9.3）。
3. **按档分配成本**：兼容档拒绝"路径上调用 LLM"（S9.1）；最优档不拒绝，但每一条重方案都要付清三件套（S9.2）。

### 7.5 由此推出的工程约束（**已进 S9 与阶段 C**）

- 端侧嵌入必须**增量**（S1.3）：这是**两档共同**的硬要求，但理由各一条 —— 兼容档：每写一条记忆就全量重嵌 → 弱设备上风扇、电耗、卡顿；最优档：索引永远追不上写节奏 → **静默丢弃注入**（后者在两档都会发生，只是重档机器跑得更快、更晚崩）。
- 查询侧加工（S2）按档实现：**兼容档必须本地实现**（规则：去指示词、抽实体 + 本仓术语表 + 端侧小模型），**不许**调大模型；**最优档可以引入 LLM 改写/精排**，但按 S9.2 补齐成本 + 门控 + 降级路径，且降级后要退回 S9.1 那条本地路径。
- **绝不把"只能跑通兼容档"当目标形态**（S9.4）：阶段 C 的施工顺序（S1.3 → S5.3 → S2 → S4）是**风险/收益排序**，不是"只做轻档也能做的那些"；其中 S4 精排的 cross-encoder 档就是为最优档准备的重方案。

---

## 8. wiki 层（白板）与「记忆涌现」—— 与 Karpathy LLM Wiki 的对应

### 8.1 三条路线的位置（用标准词汇说清我们是什么）

| 路线 | 触发方式 | 谁决定"要看什么" | 成本落点 |
| --- | --- | --- | --- |
| Karpathy · LLM Wiki | **pull**：LLM 先读 `index.md`，再决定深入哪页 | LLM | 查询侧 token |
| Claude Code · grep | **pull + 多轮**：LLM 决定搜什么、要不要继续搜 | LLM | 每轮多次 token |
| **本仓** | **push 主 + pull 辅**：监控 CoT/输入/输出 → 注入 reference；语义端 RAG 供模型主动搜 | **本地 LR（fv2）** | **设备算力** |

**"记忆涌现"就是 push。** 两条既有路线**只有 pull**，而 pull 有一个共性的硬伤：**它依赖模型自己意识到"我该去查了"** —— 查不到的东西，模型不知道自己不知道。push 把这个环节从模型手里拿掉，代价是**误注入风险**（见 8.2）。

### 8.2 push 的三条防线（没有观测的涌现 = 幻觉注入）

1. **决策门**：intent LR + 融合 margin + echo veto（禁复述用户刚说的话）+ 硬门（有害/纠正/过期/越界）+ 冷却。**默认路径全部零 token**（S9.1；这是两档共用的默认决策路径，最优档若要叠加 LLM 判断按 S9.2 付三件套）。
2. **可观测**：每次涌现必须能回答"哪条、多像（0-1）、为什么、来自哪个引擎"（S8.1；相似度与排名 = C7 已落）。
3. **触发源分级**：监控 **CoT** 信息价值最高（模型自己在想什么），但**自我强化风险也最高**；user / tool / assistant 段风险更低。→ **待验证约束**：CoT 段应比其它段用**更严阈值 + 更长冷却**，且需用 S7 实验证明"CoT 触发带来的收益 > 噪声成本"，否则不得放宽。

### 8.3 白板 = wiki 层的落地契约（不建状态机，只在写入一个门设防）

对应 Karpathy 的三层与其两个特殊文件：

| LLM Wiki 构件 | 本仓对应物 | 状态 |
| --- | --- | --- |
| 原始资料（不可变） | 会话记录、外部文档 | ✅ |
| **Wiki（LLM 拥有）** | 项目笔记 / 白板 PLAN / handoff 账本 / 每日日志 | ✅ 文件已存在 |
| **Schema** | 本规范 + `THREE-LAYER-CONTRACT.md` + 白板判据约定 | ✅ 文件化（比 Karpathy 更强：有编号与判据） |
| `index.md`（先读索引再深入） | **Tier-0 目录**（白板是它的可视化视图） | ⏳ C4 已出生成器，C5 待接线 |
| `log.md`（append-only 前缀可 grep） | 每日日志（`[YYYY-MM-DD] …` + `seq` 纪律） | ✅ |
| 操作：摄入 / 查询 / **整理（lint）** | 摄入 ✅ / 查询 ✅ / **lint ❌ 缺** | 见 S10.3 |

**S10 wiki 层契约（MUST）**

- **S10.1 页面即语料**：白板页面/卡片必须带锚点 `<!-- memory:mem_<32hex> -->`（与 L0 抽取同格式）→ **白板内容自动进检索语料**，无需新机制。这条同时解掉"双状态源"：白板是**现在时视图**，但它以锚点身份**注册进记忆索引**。
- **S10.2 索引自动生成，不许手抄**：`index`（Tier-0 目录）由页面**派生**（每条 = 链接 + 一句话 + `layer`/`status`），与白板不得各写一份。
- **S10.3 lint（整理）必须补上，且尽量零 token**：可零 token 判定的四类 —— ①**孤立条目**（无入站引用/无交叉引用）②**陈旧**（有 `superseded`/`retracted` 标记，或日期超阈）③**被提及却无独立页**的重要概念 ④**缺交叉引用**。**只有"矛盾检测"需要 LLM，必须按需触发（用户点一下），不得进自动路径**（S9.1：兼容档硬要求；最优档若要让它自动跑，按 S9.2 带成本 + 门控 + 降级路径）。
- **S10.4 不建状态机**（沿用既有拍板）：白板是视图层，状态归**记忆条目**（`layer`+`status` 三值）与 sidecar；lint 只做**只读检查 + 留痕**，不新增状态。
- **S10.5 答案归档回流**（补上"复利"回路）：一次检索/分析的结论要能**一键存成白板页面或记忆条目**（现有 `memory_note_pre` / handoff 即为通道）；否则知识只在对话里蒸发 —— 这是 Karpathy 方案最核心的收益点，也是我们目前**半闭环**的地方。
- **S10.6 人机分区**（B4 的工程解法）：沿用「模型维护区 / 用户备注区」；Karpathy 的原话是"你（几乎）从不亲自编写 wiki"，但他把**原始资料与提问**留给人 —— 与我们 B4 的分区方向一致，外部实践支持此判断。

---

## 附录 · 外部依据

### 附录 A · 视频实际覆盖内容（**已用接口核验**，2026-09-14）

用户指定：《7 分钟了解 10 种 RAG 策略》（B 站 [BV17J3B6PEGK](https://www.bilibili.com/video/BV17J3B6PEGK)，UP「Hucci写代码」，时长 451s，aid 116997224472457 / cid 40376927592）。
**注意口径**：标题里的 **7 是分钟数，策略是 10 种**（章节「RAG 十个策略总览」自证）。

`x/player/v2` 返回的 10 段章节要点（= 视频的权威骨架）：

| # | 章节 | 时间段 |
| --- | --- | --- |
| 1 | 引入 RAG 的必要性 | 0:00–0:30 |
| 2 | RAG 流程概览 | 0:30–1:06 |
| 3 | **RAG 十个策略总览** | 1:06–1:47 |
| 4 | **分块策略** | 1:47–2:42 |
| 5 | **嵌入与索引策略** | 2:42–3:48 |
| 6 | **查询处理策略** | 3:48–4:21 |
| 7 | **检索策略** | 4:21–5:03 |
| 8 | **迭代式 RAG** | 5:03–5:56 |
| 9 | **自适应路由** | 5:56–6:56 |
| 10 | 总结与建议 | 6:56–7:31 |

**核验边界（不许越界引用）**：字幕列表为空（`subtitle.allow_submit=false`），AI 摘要接口 `x/web-interface/view/conclusion/get` 返回 `-403/-101 账号未登录`，网页端 `-412`。
→ **能核验到"章/组"级，核验不到"条目"级。** 本规范 §2 与本文档其他处对"10 种"的逐条命名属**通用 advanced-RAG 谱系推断**，不得当作该视频的原话引用；需要条目级时，取用户提供的 AI 摘要文本或字幕。

### 附录 B · 我们的条款 ↔ 视频章节（组级对应，可核验）

| 视频章节 | 本规范条款 | 我方状态 |
| --- | --- | --- |
| 4 分块策略 | S1.1 块 ID 稳定 | 部分（已内容寻址，未进缓存键） |
| 5 嵌入与索引策略 | S1.2 缓存键含引擎身份 / S1.3 增量 | **缺**（草台根因） |
| 6 查询处理策略 | S2 改写 / 多查询 / HyDE | **全缺** |
| 7 检索策略 | S3 混合臂 + 融合、S4 精排 | S3 ✅ / S4 缺 |
| 8 迭代式 RAG | （**刻意不采纳**，见 §4 结论） | — |
| 9 自适应路由 | S6 决策 + S5 注入 | ✅ 本仓领先项 |

**结论一句话**：我们**最贴近"层次化索引 + 自适应路由"**（视频偏后的高级两章），草台处集中在**上游的分块/嵌入与索引两章**，而非策略选型错误。

### 附录 C · 通用 advanced-RAG 谱系（推断用，非视频原话）

- 技术谱系（advanced RAG 技术清单与调优方法）：[RAG_Techniques（含 notebook 教程）](https://github.com/hannancheng/RAG_Techniques)、[LlamaIndex 检索调优实战：分块、HyDE、压缩等提效方法](https://developer.aliyun.com/article/1685009)、[Advanced RAG techniques summary](https://github.com/maple3788/RAG_Lab/blob/7516654dbe0c16d110fad95e3de7000b3861e14e/docs/knowledge/advanced-rag-techniques-summary.md)、[关于 RAG 你不得不了解的 17 个技巧](https://cloud.tencent.cn/developer/article/2485763)。

### 附录 D · 本仓内部依据
- `THREE-LAYER-CONTRACT.md`（接口/预算/不变式 I1–I7、C1–C7）、`MEMORY-MUTATION-AND-INDEX-DESIGN.md`（supersede 与块级缓存）、`ROADMAP.md`（六步链路）、`TODO-GRAPH.html`（排期与阶段门，P1-⑨）。

### 附录 E · 核验用接口（可复现）

| 接口 | 结果 |
| --- | --- |
| `api.bilibili.com/x/web-interface/view?bvid=` | ✅ 标题/简介/时长/cid/up_mid |
| `api.bilibili.com/x/player/v2?bvid=&cid=` | ✅ `view_points` = 10 段章节要点（本文档附录 A 来源） |
| `api.bilibili.com/x/web-interface/view/conclusion/get`（需 wbi 签名 + 登录） | ❌ `-101 账号未登录`（签名算法已本地实现，卡在鉴权） |
| 网页 `bilibili.com/video/BV...` | ❌ 验证码页 / `-412 request was banned` |
| 字幕（`player/v2` 的 `subtitle.subtitles`） | ❌ 空列表（UP 未开 CC） |

**可复用结论**：无登录凭据时，B 站内容最高只能核验到**章节/结构级**；要条目级需用户提供 AI 摘要文本或字幕，或由用户授权登录态。

### 附录 F · 「RAG 已死」两条替代路线的核验事实（2026-09-14）

**① Karpathy · LLM Wiki**（[RAG已死！karpathy说：LLM Wiki永生](https://gitcode.csdn.net/69d4e95d0a2f6a37c59d9532.html)）
- 病灶诊断：传统 RAG（NotebookLM / ChatGPT 文件上传）**没有知识积累** —— "LLM 每次回答问题都要从零开始重新发现知识"。
- 方案：让 LLM **渐进式构建并维护持久 wiki**（markdown 集合），新资料到达时**整合进已有页面**（更新实体页、修订主题摘要、标注与旧说法矛盾处），"编译一次、保持最新"。
- 三层架构：**原始资料（不可变）／Wiki（LLM 拥有）／Schema**（如 `CLAUDE.md`、`AGENTS.md`，定义结构与工作流）。
- 操作：摄入 / 查询（**先读 `index.md` 定位，再深入**）/ 整理（lint：矛盾、陈旧、孤立页、缺失交叉引用）。
- **关键句（决定我们能否沿用）**：在适度规模（约 **100 份资料、数百个页面**）下，靠 `index.md` 导航"**出奇地有效，避免了基于嵌入的 RAG 基础设施的需求**"。
- → 对我们的意义：**我们的 Tier-0 目录 + 三层，本质就是它在设备端的轻量实现**；差别是我们把"维护"模板化（自动沉淀），而不是让 LLM 自由重写（省 token、可审计）。

**② Claude Code · Grep agentic**（[别再迷信RAG了！Grep回归](https://dbaplus.cn/news-141-7303-1.html)）
- Boris Cherny：「早期版本的 Claude Code 使用了 RAG + 本地向量数据库，但我们很快发现 **agentic search 通常效果更好**」；「**Plain glob and grep, driven by the model, beat everything**」。
- 实现：LLM 驱动的多轮 `grep/glob/read` 循环（无硬编码流程）；**GrepTool 默认只返回文件名**（故意控制信息量，避免一次 grep 淹没 context）、`head_limit` 默认 250；子 agent（Explore）做 **context 隔离**，只把结论回传主对话。
- 代价明账：**每轮的多次 LLM 工具调用**；Boris 也承认放弃 RAG 的决策**部分基于直觉**。
- → 对我们的意义：**语料性质不同** —— grep 吃"精确字面"（代码、路径、标识符），记忆是自然语言，用户往往说不出确切字面（例：他的"你上一轮并没有正常地纠错"）。我们取其"**要不要搜由智能判断**"的思路，但判断交给本地 LR，不引入多轮 token。
