# 知识库分块 · 3–5 章实测与「结构优先」结论

**2026-10-02（第三阶段）** · 全部数据为本机实测

> **一句话结论**：把「章 → 节 → 穴位条目 → 字段」做成块边界，**不需要改插件**——走文本导入通道，只要单元内部不留空行即可。已用 5 章 + 附录验证：单块精确命中从 **11/21 提升到 21/21**（全部第 1 位），穴位条目「穴名 +【定位】同块」从 **0% → 100%**，入库**逐字无损**。

---

## 一、这轮做了什么

1. 通读插件源码，核实三条可能路线（改解析、语义合并、文本通道），确认 `parseEpub` 从未读取 OPF `spine`。
2. 写结构优先文本生成器，产出 5 章 + 附录的测试文本，以及全书 670 片版本（含出处映射）。
3. 建 3 个临时库实测对比：**A 现状** / **C 结构优先** / **D 语义合并**，同一套嵌入与重排参数。
4. 21 道题检索对比 + 块级量测 + 全量离线核验（无损性、出处回解、条目纯度）。
5. 原 EPUB、原「中医新」库、旧「中医」库、插件文件**均未改动**。

## 二、三条路实测对比

测试集：第一章、第二章、第三章、第九章、第十六章 + 附录，共 **1812 个原始段落**。
参数：`Qwen/Qwen3-Embedding-4B`、`Qwen/Qwen3-Reranker-8B`、`chunkSize 800`、`smartChunk`。

| 指标 | A 现状 | **C 结构优先** | D 语义合并 |
|---|---|---|---|
| 块数 | 1812 | **224** | 1552 |
| 块长 中位 / 均值 / 最长 | 24 / 42.2 / 509 | **266 / 377 / 1821** | 23 / 49.4 / 881 |
| 碎块（不足 50 字符） | 1341 | **9** | 1090 |
| 孤立标题块 | 98 | **0** | 48 |
| 穴名与【定位】同块 | 0 / 116 | **116 / 116** | 2 / 116 |
| 【定位】/【主治】/图片/脚注锚点 | 117/117/71/392 | 117/117/71/392 | 117/117/71/392 |
| 入库逐字无损 | 通过 | **通过** | 通过 |
| 检索：单块精确命中（21 题 TopK=4） | 11 | **21** | 14 |
| 检索：仅靠 Top4 拼装才命中 | 7 | **0** | 1 |
| 检索：完全落空 | 3 | **0** | 6 |

参考：现有「中医新」库（整本 1 个文档）同 21 题为 **11 命中 / 5 拼装 / 5 落空**，与 A 同级。

**D 路（`semanticChunk=true`）结论**：1812 → 1552 块，条目同块率仍只有 2%，解决不了碎块问题，可以排除。

## 三、关键样例（问「中府 定位」）

- **现状 / 原库 Top1**（0.9935，36 字符）：`【定位】横平第1肋间隙，锁骨下窝外侧，前正中线旁开6寸（图3-2-2）。` —— 没有穴名，读者不知道这是哪个穴。
- **结构优先 Top1**（0.984，339 字符）：`〔源：text00011.html#filepos210801〕 1.中府* Zhōngfǔ（LU1）肺募穴，手太阴经、足太阴经交会穴 【定位】… 取法：… 【解剖】…` —— 条目不拆。

另外两例：

- 「列缺 定位」：现状与原库的 **Top4 里连一个含【定位】的块都没有**（穴名块与定位块互相挤占名次）。
- 「定喘 定位」：现状与原库 **Top1 只有** `1.定喘 Dìngchuǎn（EX-B1）`，定位在别的块里。

## 四、「Markdown 聚合不可行」这个说法要修正

上一轮 A/B 的失败点**不是**「Markdown 层面做不到」，而是**单元内部留了空行**。本轮把三件事做实：

1. 单元内部段落用**单换行**连接（`normalizeText` 只压 3 个以上换行，单换行保留）→ `splitBlocks` 不再切。
2. 单元之间用**空行**分隔 → 每个单元正好一个块。
3. `#` / `##` 开头的行会被 `splitBlocks` **吃掉并转成该块的 `heading` 元数据**，入库时拼成 `context = 书名 > 章 > 节`。上一轮 B 的前缀变成独立小片，是因为前缀行与正文之间留了空行。本轮改用**片首内联出处行**，不依赖该机制，因此逐字无损。

## 五、结构优先规则与全量核验

**规则**

- 层级：章 → 节 → 一、 →（一）为边界；401 个穴位条目为原子单元；论述按小节聚合、不跨节；目录页按行聚合。
- 超过 **1824** 字符（= `chunkSize 800` × `charsPerToken 2.28`）才拆：穴位按【字段】边界、其余按句 → 行 → 硬切。
- 每片片首带出处标记：`〔源：textNNNNN.html#锚点〕` 或 `〔源：textNNNNN.html 元素N〕`；超预算拆出的片另补「章 · 节」上下文。

**全书离线核验（29 页 / 6535 段 → 670 片）**

| 核验项 | 结果 |
|---|---|
| 片数 / 最长片 / 超预算片 | 670 / 1817 / **0** |
| 无损性 | 去标记与注入上下文后，**去空白逐字 == 库内 rawText** |
| 标题 | 253 个，**一个不少** |
| 图片占位 / 脚注锚点 /【定位】 | 217 / 1922 / 401，**与现状完全一致** |
| 出处标记可回解 | **670 / 670 全部**可定位到原 XHTML 页 + 元素序号 / 锚点 id |
| 穴位条目纯度 | 每片最多 1 个【定位】，**0 粘连** |
| 标记与上下文开销 | 19641 字符，占原文 **7.1%** |
| 目录页处理 | EPUB 目录页 541 小片 → 18 片；NCX 导航页 3.3 万字符单块 → 20 片 |

## 六、对上一轮 7 条意见的逐条回应

| # | 原意见 | 我的判断 |
|---|---|---|
| 1 | 碎块源于源 XHTML 短段落 + 插件按空行切，重建索引无效 | **同意**，已复现 |
| 2 | 本 EPUB 顺序/图片未丢；插件从不读 spine 是潜在缺陷 | **同意**，源码复核：`parseEpub` 全文无 spine / container / opf 字样 |
| 3 | Markdown 预聚合不可行，须改插件或用「单元内无空行」的文本 | **半同意**：前半句只对「保留空行的 Markdown」成立；后半句那条路已验证可行，**所以不必改插件** |
| 4 | 按章→节→穴位单元组织，超 1824 再拆，保留章节 / XHTML / 元素序号 / 锚点 | **同意**，已实现并量化 |
| 5 | 暂不改全书，先扩到 3–5 章测试再定 | **同意**，已执行；结果支持进入下一步 |
| 6 | 图片单独处理、模型输出只作待核 | **同意**，尚未开始；`imageCaptionProvider` 仍为 off |
| 7 | 接口明文返回 key，须轮换 | **同意**；本轮读配置时密钥只以掩码形式出现，未落盘。轮换与加鉴权待批 |
| — | **清单遗漏**：`semanticChunk` 配置项 | 本轮补测（D 路），**解决不了问题**，可排除 |

## 七、当前状态

**未改动**：原 EPUB 文件、「中医新」库（6535 块）、旧「中医」库、插件 `lib\knowledge\index.js`。

**本轮新建（临时库，可随时删）**：

| 库名 | id | 内容 |
|---|---|---|
| `_tmp-五章-现状A` | `ab1e7e17-38f5-4de5-a4ff-684e818c44c3` | 6 文档 / 1812 块 |
| `_tmp-五章-结构C` | `1a478d0e-78a2-43f7-9978-a72049000bc8` | 6 文档 / 224 块 |
| `_tmp-五章-语义D` | `57d082ed-4f2e-45ae-aad8-aa95ee0978cc` | 6 文档 / 1552 块 |

## 八、下一步选项

**选项 1 · 用全量 670 片建新库，检索仍指向旧库（推荐）**
只新建一个库，原库与插件都不碰。代价约 670 次嵌入调用，2 分钟内完成，可逆（删库即可）。适合先亲眼验证效果。

**选项 2 · 建新库，并把代理检索切到新库**
在选项 1 基础上把启用范围从「中医新」改成新库，一步回退即可恢复。旧库保留、只是不参与检索。

**选项 3 · 同时改插件 `parseEpub` / `splitBlocks`**
让以后任何 EPUB 导入都自动结构优先，并顺带修掉「不读 spine」的缺陷。风险：插件升级会覆盖改动（会先备份）；不会自动修好现有库。

**选项 4 · 先停在这里**
保留本报告、临时库与脚本，供自行复核。

> 选项 1 与 3 不冲突：1 是「把这本书弄好」，3 是「以后新导入的书自动弄好」。

## 附 · 复现用脚本

均在 `工作区\_tmp\`：`gen_struct.py`（结构优先文本生成器，内置无损自检）、`import_five.py`（建库导入）、`measure3c.py`（块级量测）、`retrieval.py`（检索对比）、`verify_c_full.py`（全量出处回解与纯度核验）；结果见 `_tmp\five\measure_final.json`、`_tmp\five\retrieval.json`、`_tmp\five\C-pieces-all.json`。

---

**安全提醒**：`GET /knowledge/config` 会明文返回嵌入/重排等 API key，而本机接口无鉴权。建议轮换密钥并给相关 GET 加鉴权（后者属改动本体，待批）。本报告与生成脚本均未包含任何密钥。
