Files
mdpolish/research-wiki/explanation/clindb-first-batch-components.md
T

117 lines
6.3 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.
# ClinDB 第一批组件如何在不猜正文的前提下完成清洗
## 1. 它解决的实际问题
5 份 ClinDB 论文 Markdown 同时包含编辑痕迹、转换噪声和排版噪声。它们看起来都像“删掉几行或整理一下格式”,
实际误删边界不同:行首数字可能是手稿行号,也可能是作者单位;`arXiv:` 可能是边栏戳,也可能是合法参考文献;
编号列表可能属于 References,也可能是正文方法步骤。
当前实现没有建立一个能随意改全文的“大清洗器”,而是把第一批确定问题拆成 8 个组件。每个组件只识别一种证据,
返回快照绑定的精确 `TextEdit`,由公共流水线统一验证、应用和记录。
清洗语义来自已批准的
[`0006-clindb-first-batch-cleaning-components.md`](../design/0006-clindb-first-batch-cleaning-components.md)
输入范围和稳定计数见
[`CLINDB_REVIEWBENCH_CLEANING_SCOPE.md`](../reference/CLINDB_REVIEWBENCH_CLEANING_SCOPE.md)。
## 2. 当前组件和顺序
```text
Word 批注 ─┐
手稿行号 ──┼──► arXiv 戳 ──► 重复页眉 ──► 映射断词
│ │
│ └────────────► 参考文献空行
└────► HTML 双重实体 ──► HTML 表格布局
```
实验脚本固定按以下顺序组装:
| 顺序 | 组件 | 当前作用 |
| ---: | --- | --- |
| 1 | `paper.word_review_comment` | 删除完整单行 Word 批注及其后一个空行 |
| 2 | `paper.manuscript_line_number` | 删除由长递增序列确认的手稿行号 |
| 3 | `paper.arxiv_submission_stamp` | 删除严格整行提交戳 |
| 4 | `paper.repeated_running_header` | 删除重复页眉,并接回有明确续句证据的段落 |
| 5 | `paper.page_break_word_join` | 只应用本次运行参数中记录的词片段映射 |
| 6 | `markdown.html_table_double_escape` | 只在严格表格单元格文本中解除一层实体转义 |
| 7 | `markdown.html_table_layout` | 保留 HTML 内容,把单行表格展开成一行一个 `<tr>` |
| 8 | `paper.reference_spacing` | 只在完整连续的 References 章节中统一条目空行 |
顺序不是为了让结果“看起来整齐”。dmp 的一个重复页眉正好位于第 18、19 条参考文献之间;如果不先删除页眉,
参考文献组件就不能确认这是完整连续序列。HTML 实体先修改小范围 token,表格布局再基于新快照替换整张表,
两类修改仍能在审计中分别追踪。
## 3. 为什么行号不能使用全局正则
JAMA 文档中一共有 134 个看似 `数字 + 空格` 的行首前缀。前 59 个位于 Abstract 之前,是作者单位编号;真正的手稿
行号只有 Abstract 之后的 75 个。
当前组件要求:
- 文档中恰好有一个严格 `## Abstract`
- 只看它之后的候选;
- 候选数字全部严格递增;
- 至少有 20 个候选,并至少有 2 个数字标题。
任一条件失败就整篇不改。这样会漏掉较短的带行号手稿,但不会为了提高命中率删除作者单位或零散数字段落。
## 4. 为什么断词使用显式映射
“行尾连字符加下一行小写字母”无法决定连字符应删还是保留:`possi-` + `bly` 应成为 `possibly`,而
`SOFA-` + `based` 应保留为 `SOFA-based`。dmp 还存在 `threshold.` + `olds`,它同时包含多余句点和重复片段,
普通词典也无法解释。
因此组件的实际参数记录三项:左片段、右片段和结果词。只有相邻物理行或中间恰好一个空行、词边界完整且映射唯一时
才修改。增加新词不是自动学习行为,需要先批准新的项目映射;组件算法版本不变时,运行清单仍能通过参数区分实际语义。
## 5. 两个表格组件怎样共享范围
`_html_table.py` 只识别当前转换器输出的严格子集:`<tr>` 直接位于 `<table>` 下,`<td>` / `<th>` 直接位于
`<tr>` 下,标签完整闭合,单元格中没有嵌套标签。它返回原字符串下标,不生成 DOM,也不重新渲染全文。
实体组件只处理单元格文本中的三个精确 token:
```text
&amp;lt; → &lt;
&amp;gt; → &gt;
&amp;amp; → &amp;
```
结果仍是合法 HTML 源码中的单层实体。标签、属性、表格外文本和其他实体不受影响。
布局组件只处理整个片段没有换行的严格表格。它原样复用 `<table>` 起始标签、每个完整 `<tr>...</tr>` 和结束标签,
只增加外层换行与两个空格缩进。当前 9 张真实表格都有 `colspan`,所以全部保留 HTML;实现没有猜测表头,也没有转 GFM。
遇到未闭合、嵌套、额外结构标签或混合换行时,扫描失败关闭,保持原文。
## 6. 共享代码为什么仍然很小
- `_text_ranges.py` 只提供 Python 字符下标下的物理行、行尾和空行关系;
- `_html_table.py` 只提供严格 HTML 表格、行和单元格范围;
- 业务组件依赖这两个私有模块,但辅助模块不依赖组件、流水线或文件层;
- 文件实验层只接收已经组装的 `Pipeline`,不知道任何识别规则。
因此以后放宽某个业务规则通常只改一个组件及其测试;替换 HTML 识别方式不会改变 `DocumentSnapshot`
`ProposedChange``Change` 或 JSON 产物;未来引入 Profile 时,也只接管当前脚本里的组件组装。
## 7. 当前验证结果和边界
2026-08-22 在 Python 3.13.11 环境完成:
- Ruff 通过;
- mypy 检查 37 个文件无问题;
- pytest 204 项通过;
- 5 份本地论文全部 `success`,合计 155 条 `Change`
- 对 5 份成功输出再次运行,全部 `success` 且零修改;
- 输入运行前后哈希不变;
- JAMA Abstract 前内容不变,Springer 两条合法 arXiv 参考文献保留;
- 图片引用文字不变,但实验产物没有复制图片资产。
保存型实验位于本机 Git 忽略的
`artifacts/2026-08-22/runs/clindb-first-batch-v1/`。运行和复核方法见
[`run-local-clindb-first-batch-experiment.md`](../guides/run-local-clindb-first-batch-experiment.md)。
这次成功只证明批准的 8 类规则在当前 5 份输入上闭环。截断、缺表、乱码、OCR 语义错误、图片资产、修订词选择和一般
段落重排仍不在自动清洗范围内。