117 lines
6.3 KiB
Markdown
117 lines
6.3 KiB
Markdown
# 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
|
||
&lt; → <
|
||
&gt; → >
|
||
&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 语义错误、图片资产、修订词选择和一般
|
||
段落重排仍不在自动清洗范围内。
|