日期:2026-10-02 性质:本机实测报告(可直接给外部评审) 环境:DSH 桌面端 0.2.0-rc.2 + dsh-knowledge 插件 0.4.1,仅监听 127.0.0.1:19387 嵌入/重排:Qwen/Qwen3-Embedding-4B(2560 维) / Qwen/Qwen3-Reranker-8B(硅基流动 API) 总原则:原文完整、出处可追溯 > 分块与检索效果;未确认前不动原始 EPUB、不动现有库、不改插件
《经络腧穴学》的"碎块"问题已定位并解决:不改插件,用"结构优先文本"通道重建,676 块(原 6535 块),入库逐字无损,36 道测试题全部第 1 位精确命中(原库 14/36)。新库已建成(经络腧穴学·结构优先),但检索开关仍指向原库,等人工确认后切换。
「中医新」库(《经络腧穴学》沈雪勇 4 版 EPUB)共 6535 块 / 283093 字符,平均 41 字符/块,1341 块不足 50 字符。问"中府 定位",Top1 返回的是一句 36 字符、没有穴名的 【定位】横平第1肋间隙…。
<p>;插件 parseEpub(lib/knowledge/index.js:4433)按 ZIP 字母序取 HTML → Turndown 转 Markdown → join("\n\n")。splitBlocks(:1992)只以空行和 # 标题行为边界,因此"段落数 = 块数"。<p>/<blockquote>)逐个转换,得 6535 块、283096 字符,与库内 rawText 逐字相同。chunkSize 800 × charsPerToken 2.28 = 1824 字符(此前误算为 1280,已更正)。 占位 ↔ 217 个 <img>,一一对应;另有 2 张为封面(manifest cover-image)与封底类页图。但 217 个 alt 全为空,图内文字没有任何文字化入口。parseEpub 从不读取 OPF spine(全文无 spine/container.xml/content.opf 字样),只按 ZIP 字母序。本书恰好"字母序 = spine 序",属偶然正确;凡 spine 顺序 ≠ 字母序的 EPUB,正文顺序会错。这是通用缺陷(尚未向上游提 issue)。测试集:第一、二、三、九、十六章 + 附录,共 1812 个原始段落;同一套嵌入/重排/检索参数。
| 指标 | A 现状 | C 结构优先(采用) | D 语义合并 | R 原库(整本) |
|---|---|---|---|---|
| 块数 | 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 | — |
| 入库逐字无损 | 通过 | 通过 | 通过 | — |
| 检索:单块精确命中(21 题 TopK=4) | 11 | 21 | 14 | 11 |
| 检索:仅靠 Top4 拼装才命中 | 7 | 0 | 1 | 5 |
| 检索:完全落空 | 3 | 0 | 6 | 5 |
结论:C 路(结构优先文本)全面胜出;D 路(插件配置项 semanticChunk=true,按嵌入相似度自动合并碎块)只能把 1812 块降到 1552 块,穴位条目同块率仍只有 2%,解决不了问题,予以排除。
早期结论认为"在 Markdown 层面预先聚合无效"。实测表明该结论只在"单元内部保留空行"时成立:
normalizeText 只压缩 3 个以上连续换行,单换行保留)→ splitBlocks 不再切。#/## 开头的行会被 splitBlocks 吃掉并转成该块的 heading 元数据,入库时拼成 context = 书名 > 章 > 节(:9401)。此前一次失败的 A/B 测试,是因为前缀行与正文之间留了空行,才被切成独立小片。即:仅靠文本导入通道就能实现结构优先分块,无需修改插件。
charsPerToken,各篇预算不同,不收口会被 windowBlock 带重叠切开)。〔源:textNNNNN.html#锚点〕 或 〔源:textNNNNN.html 元素N〕;被拆开的块另补「章 · 节」上下文。| 核验项 | 结果 |
|---|---|
| 片数 / 最长片 / 超预算片 | 676 / 1817 / 0 |
| 无损性 | 去标记与注入上下文后,去空白逐字 == 库内 rawText |
| 标题 | 253 个,一个不少 |
| 图片占位 / 脚注锚点 / 【定位】 | 217 / 1922 / 401,与现状完全一致 |
| 出处标记可回解 | 676 / 676 全部可定位到原 XHTML 页 + 元素序号 / 锚点 id |
| 穴位条目纯度 | 每片最多 1 个【定位】,0 粘连 |
| 标记与上下文开销 | 19810 字符,占原文 7.1% |
| 目录页处理 | EPUB 目录页 541 小片 → 18 片;NCX 导航页 3.3 万字符单块 → 20 片 |
| 项 | 值 |
|---|---|
| 库名 | 经络腧穴学·结构优先 |
| 库 id | 8bf27782-5f61-49c8-8d9a-af2b89158239 |
| 结构 | 29 篇文档(按原 XHTML 页切分,标题取原书章名/前言/附录等) |
| 块数 | 676 |
| 字符 / token | 297000 / 128197 |
| 逐篇核验 | 块数与送入片数一致 29/29;入库逐字无损 29/29 |
| 检索开关 | 仍指向原「中医新」库(enabledBaseIds 未改) |
建设过程中修正了两个我方脚本的缺陷(与插件无关):① 未按每篇文档自身预算收口,导致 5 篇中各有片超限;② 按篇补切时片首标记行后多出空行,入库被切成"标记 + 正文"两块(首版库 686 块 ≠ 676 片)。修正后删库重建,逐篇完全对齐。
36 题覆盖《经络腧穴学》的第一、二、三、五、六、七、八、九、十、十一、十二、十三、十四、十五、十六、十七章及附录——其中 12 章未参与任何规则调参,用于防止过拟合。全部问题限定到"本书"这一个文档,TopK=4。
| 版本 | 块数 | 一块多大 | 单块精确命中 | 仅拼装命中 | 完全落空 |
|---|---|---|---|---|---|
| 中医新(EPUB 直导) | 6535 | 41 字符 | 14 / 36 | 11 | 11 |
| 中医 旧库(md 版) | 175 | ~1200 字符 | 36 / 36 | 0 | 0 |
| 新库 结构优先 | 676 | 一条一块 | 36 / 36 | 0 | 0 |
必须更正的一点:不能声称"前两个库都远不如新库"。实测表明旧「中医」库里那一版(md,1200 字一块)同样是 36/36,与新库打平。真正的差异不在"能否检索到",而在下面四项:
| 维度 | 中医 旧库 | 新库 结构优先 |
|---|---|---|
| 一块里含几个穴位 | 多个(1200 字一大段,需自行在其中定位) | 一个(穴名+定位+主治+操作,可整块引用) |
| 正文结构标记 | 401【定位】/401【主治】/401【解剖】(与 EPUB 完全一致) | 同左 |
| 图片占位 | 0(全丢) | 217(全在) |
| 脚注锚点 | 0(全丢,无法跳转) | 1922(全在) |
| 每块出处 | 无 | 有(原 XHTML 页 + 锚点) |
即:新库相对旧库的增量是「一条一块、可整块引用、图片与脚注未丢、出处可回解」,而不是"能查到 vs 查不到"。若只要求"能检索到",其余那批 1200 字一块的资料不需要改造。
47 份资料的原始格式全部为 EPUB,原始文件位置: DSH云端备份\4-原始底本\epubtest\00-原始EPUB\(10 个 EPUB,含 2 个合集) 已转换的 md:…\epubtest\03-入库\(10 个)、…\epubtest\04-合集拆分\(38 个,由两个合集拆出)
按"块是否碎"分类(以库内块数 ≈ md 段落数、块长 ≈ 段落长为等价关系):
| 类别 | 本数 | 特征 | 建议 |
|---|---|---|---|
| A 已较粗 | 34 本 | 一段约 1200 字,不碎(等同旧库那版) | 不用重切 |
| B 偏碎 | 1 本 | 《脈經校注》,一段约 590 字 | 顺手做 |
| C 真碎 | 11 本 | 一段 200–370 字,近一半段落不足 50 字 | 优先做,受益最大 |
C 类 11 本(合计约 400 万字符):证治准绳(五)幼科、千金翼方校释、重订医学衷中参西录(上/下)、中药学、证治准绳(四)疡医、黄帝内经素问语译、针灸大成、刺法灸法学、单玉堂针灸配穴通俗讲话、黄帝内经素问(典藏版)。
| 步骤 | 时间 |
|---|---|
| 4 套"单元规则"(医经校注类 / 方书类 / 教材讲稿类 / 针灸专著类) | 4–8 小时 |
| C 类 11 本 + B 类 1 本:跑通+逐本无损与出处核验+导入+抽测 | 5–7 小时 |
| 全库检索验证 | 2–3 小时 |
| 合计 | 约 11–18 小时 ≈ 2–3 个工作日 |
| A 类 34 本若也要按章细化 | 追加 8–12 小时(收益小,建议暂不做) |
复用性:EPUB 元素级抽取、结构优先切分、无损自检、出处回解、入库与检索对比脚本均已具备,对全部书目通用;每本真正需要人工介入的是"什么算一个单元"的规则确认。
注意:《中药学》那份文件名标注为 中药学 .pdf_by_PaddleOCR-VL-1.6.md,是扫描 PDF 经 OCR 得到的,文字质量需单独核对,不宜与其他书批量处理。
关键机制:库级配置优先于全局配置(曾因此踩坑:重排模型短名写在库级,全局正常但库级报 HTTP 400)。
| 项 | 全局 | 「中医新」库级(实际生效) |
|---|---|---|
| 嵌入模型 | Qwen3-Embedding-4B | 继承(一致) |
| 重排模型 | Qwen3-Reranker-8B | 同(已修好) |
| topK | 4 | 6 |
| similarityThreshold | 0 | 0.15 |
| siblingChunks | 1 | 继承 1 |
| semanticChunk | false | false(显式) |
| 文档处理器 | builtin | mineru(带 key) |
| 图片识别 | off | openai / Qwen2.5-VL-72B(带 key) |
| chunkSize / overlap / smartChunk | 800 / 100 / true | 继承 |
不要改动:
smartChunk=true 与空行分隔(本方案生效的前提)。建议调整(均在「中医新」库级,属可选):
siblingChunks 1 → 0:现在会把命中块的前后邻居一并塞进结果。对碎块库是补救(邻居里可能有穴名),对"一条一块"的新库则是噪声(邻居是别的穴位)。旧「中医」库本身即设为 0。topK 6 → 4(可选):一条一块时 4 条足够。MinerU:只对 PDF 生效。现有三库内容全部是 EPUB/md(唯一 PDF 衍生的《中药学》已在外部用 PaddleOCR 转好),MinerU 当前完全用不上、也不会被触发,无需改动。注意它设在「中医新」库级,新库继承全局 builtin;将来若向新库导入扫描 PDF,才需要另行配置。
图片识别:「中医新」库级其实已开启(openai / Qwen2.5-VL-72B-Instruct;全局是 off,被库级覆盖),但这本书一张图注都没生成——库内 217 个图片占位全部为空。说明内置 EPUB 解析路径不会把图片交给视觉模型,该设置对本库是空转。图片提字应作为独立工作线,且机器输出必须标注「机器识别/待核」。
关于嵌入模型 4B vs 8B:同家族 MTEB 多语言成绩 0.6B ≈64.3 / 4B ≈69.5 / 8B = 70.58,即 4B → 8B 仅约 +1.1 分(相对约 1.6%),真正的大台阶是 0.6B → 4B。而本次仅改变分块方式(同一 4B 嵌入)就把命中率从 14/36 提到 36/36。因此暂不更换嵌入模型;若将来要试 8B,最省的做法是只在新库上试(676 块,分钟级、成本极低),不动旧库与全局。
| # | 早期建议 | 核对结果 |
|---|---|---|
| 1 | 碎块源于源 XHTML 短段落 + 插件按空行切,重建索引无效 | 成立,已复现 |
| 2 | 本 EPUB 顺序/图片未丢;插件从不读 spine 是潜在缺陷 | 成立,源码复核确认 |
| 3 | Markdown 预聚合不可行,须改插件或用"单元内无空行"的文本 | 需限定:前半句只对"保留空行的 Markdown"成立;后半句已验证可行,因此不必改插件 |
| 4 | 按章→节→穴位单元组织,超 1824 再拆,保留章节/XHTML/元素序号/锚点 | 成立,已实现并量化 |
| 5 | 暂不改全书,先扩到 3–5 章测试再定 | 已执行,并已按此结论建成全量新库(未切换) |
| 6 | 图片单独处理、模型输出只作待核 | 同意,尚未开始 |
| 7 | 接口明文返回 key,须轮换 | 同意,尚未执行(见 §10) |
| — | 该清单遗漏:插件配置项 semanticChunk(自动合并碎块) | 已补测,解决不了问题,排除 |
| — | 需更正:不宜声称"旧库远不如新库" | 旧库 md 版 36/36 与新库打平,真正差异见 §6 |
knowledge-toggle 的 enabledBaseIds,目前仅 中医新)。一步可回退。alt 全空;字形图/正文图/注释图需分别提取并人工核对,机器输出标注「机器识别/待核」。经络示意图的模型描述建议另存。GET /knowledge/config 与 GET /knowledge/bases 会明文返回嵌入/重排/图注/MinerU 的 key,而本机接口无鉴权;历史上会话记录中亦曾出现明文 key。建议轮换密钥并给这两个 GET 加鉴权(后者属改动插件本体,未执行)。本报告与全部脚本均不含任何密钥。_tmp- 临时库,尚未删除(见 §12)。全部脚本位于 工作区\_tmp\,可用随附 Python 运行;关键产物:
| 文件 | 用途 |
|---|---|
gen_struct.py | 结构优先文本生成器(内置无损自检 + 按每篇预算收口) |
extract_v2.js | EPUB 元素级抽取器(按 OPF spine + body 顶层块元素,输出 page/elemIndex/id/images) |
import_full.py / reset_full.py | 全量建库 / 删库重建 |
verify_c_full.py / check_pieces.py | 出处回解、单元纯度、片内空行与预算复核 |
retrieval_full.py / retrieval_old.py / deep_compare.py | 36 题检索对比 / 旧库补测 / 三维度深挖 |
five\C-pieces-all.json | 全书 676 片(含 pre/core 与元素 ids),全量建库之源 |
five\full_base.json | 新库 id 与 29 篇文档 id 台账 |
five\retrieval_full.json / retrieval_3way.json | 检索结果原始数据 |
five\md_inventory.json / worklist.json | 47 份资料的结构体检与分类 |
主要 API 端点:POST /knowledge/bases、POST /knowledge/bases/{id}/documents、GET /knowledge/documents/{id}?includeChunks=true、POST /knowledge/search。
未改动:
DSH云端备份\4-原始底本\epubtest\00-原始EPUB\)lib\knowledge\index.js(未修改;历史改动仅存在于 lib\tool-knowledge\index.js:1929,有备份)新增:
_tmp- 对比临时库:_tmp-第三章策略A(183 块)、_tmp-第三章策略B(184 块)、_tmp-五章-现状A(1812 块)、_tmp-五章-结构C(224 块)、_tmp-五章-语义D(1552 块)本报告全部数字均可在本机用 §11 脚本复现。
安全声明:本报告未包含任何 API key、token 或其他凭据;配置审计中密钥一律以掩码形式呈现。