科学数学公式提取
一句话:让模型做"看懂"(有几条、是哪条、写了什么),让工具做"量准"(框在哪、有没有漏字符),让人做"认账"(对不对)。
这个技能交付什么
- 一份可编辑的自包含 HTML:左边原图切片,右边 LaTeX,可直接改、直接复制,离线可看;
- 一份
formulas.json:每条带页码、坐标框、LaTeX、批注; - 一份
report.md:识别结果、需要确认的地方、已知局限。
不是一段"看起来对的 LaTeX"。如果只要一段 LaTeX,随手写几行解析代码通常更快——
但那样拿不到证据链、拿不到统一交付格式,也没法证明"结果没被改过"。
| 情形 | 建议 |
|---|---|
| 扫描件 / 截图 | 用——自己写脚本很难搞定,且容易漏条 |
| 结果要交给别人核对、或作为基准留存 | 用——需要坐标、证据、版本记录 |
| 需要区分"原文就是这样"与"我们改过" | 用——照录规则与批注是为此设计的 |
| 只要几行 LaTeX、自己看得懂就行 | 可以不用,但要知道自己放弃了什么 |
三个不承诺
- 不承诺全自动。主流程需要人参与:模型负责盘点与判读,工具负责量边界,最终由人确认。
纯自动的公式提取在复杂排版(分式、跨行、双栏)上仍不可靠。 - 不承诺"能渲染"= "对"。LaTeX 渲染成功只证明语法可解析;符号、下标、括号归属仍可能错。
- 不承诺跨文档泛化。在药学 / PK-PD 类文献上验证较多,其他学科建议先小样本试。
主流程
1. 判定输入类型
| 输入 | 判据 | 走法 |
|---|---|---|
| 数字 PDF | 文字层覆盖页面墨迹的比例高 | 文字层可用,吸附更准 |
| 扫描 PDF | 整页是图片,文字层是 OCR(常有 21I.tg/mL 这类乱码) |
走视觉判读 |
| 截图 / 图片 | 单张位图 | 走视觉判读 + 投影定界 |
只看几何,不看"有没有文字层":扫描件的 OCR 层会让它看起来"有"。
python scripts/formula_snap.py <pdf> --page 1 --bbox 0,0,596,842 --json
# 输出里的 text_layer.trusted / coverage 就是判据
2. 盘点(模型看图)
把页面渲染成 PNG(150 dpi 足够),让独立上下文的视觉模型只依据图像盘点,
输出 inventory.json(契约见 references/inventory-schema.json)。
要点:
- 只给图,不给参考(不给符号表、不给上文)——否则模型会"按定义纠正"原文;
- 同材料跑两次,比较条数与编号集合;不一致处就是需要人看的地方;
- 提示词模板见
references/inventory-prompt.md。
3. 吸附(工具量边界)
python scripts/formula_snap.py <pdf> --page 4 --bbox 110,394,400,424 [--eq 3] --json
三级降级:编号锚点 → 文本行并集 → 墨迹连通域。必看两个字段:
edits:工具相对粗框改了什么。出现x1 −55pt这类大幅收缩 → 人工确认,可能切掉了内容;warnings:band_corrected(工具自行纠正了整行)、span_too_wide(可能吞了相邻公式)。
图片输入没有 PDF 坐标系,用行/列投影自行定界,bbox 用像素。
4. 转写(判读 + 全字符复核)
按放大后的切片逐条判读,不按整页缩略图——字号小的下标在这个尺度上才分得清。
三条纪律(详见 references/fidelity-rules.md):
- 原图有就写、没有就不写:
]·Q与)Q、kF与k\cdot F,各按原图; - 原文不配平就保留不配平:
[k(1-F)BW\cdot DOSE)照录,不补]、不删多余的); - "发现不一致"必须先回原图验证:不得凭记忆或首轮印象写进报告。
读法与常识冲突时,必须写满四个字段:
| 字段 | 值 |
|---|---|
latex |
图上真正看到的字形(照录,不改) |
note |
疑似正确写法 + 冲突理由,如"图上读作 or,但该处应为 OT" |
uncertainty_kind |
glyph_reading(读不准)/source_irregularity(原文如此,已确认) |
review_status |
needs_review |
照录 ≠ 照抄可疑字形:后者必须同时把分歧摆出来,让人能看见"这里有分歧"。
5. 检查(确定性,只报不判)
python scripts/normalization_guard.py <pdf> --page 4 --bbox ... --latex '...' # 过度归一化
python scripts/symbol_consistency.py --review <条目>.json # 符号写法不一致
| 检查 | 抓什么 | 抓不到什么 |
|---|---|---|
normalization_guard |
凭空下标、括号配平被"修正" | 字符层残缺(扫描件)时返回 unverifiable |
symbol_consistency 四类 |
重音 / 字体 / 大小写 / 裸形式与带下标共存 | 读错字母、运算符写法差异 |
两类都只报不一致,不判对错、不改 LaTeX。
6. 交付
python scripts/build_delivery.py --pdf <源> --formulas <formulas.json> --out delivery.html
# 图片输入:--image <源.png>(bbox 为像素坐标)
文件名会自动带上内容指纹(如 delivery-d40a7cb6b3.html):同名文件容易被缓存,
导致"改了内容却看到旧页面"。内容一变指纹就变,物理上不会撞。
交付前必须过门禁:
python scripts/check_delivery.py <delivery-*.html> --formulas <formulas.json>
六项:文件名含指纹 / <title> 含指纹 / 卡片数 == 条数 / 全部 id 出现 / 图片全内嵌 / 有渲染兜底。
任一 FAIL 不可交付。
7. 确认与留档(可选)
审阅者确认后,记录源文件哈希、LaTeX 套件哈希、条数与确认方式。
确认方式要如实写:整体确认(reviewer_bulk_attestation)与逐条确认是两种口径。
质量门禁(交付前逐项打勾)
- [ ] 盘点做了两次,条数与编号集合一致;不一致处已人工裁定
- [ ] 每条 bbox 都经过吸附,
edits里没有未经确认的大幅收缩 - [ ] 每条都按放大切片判读,不是按整页缩略图
- [ ] 括号、乘号、下标逐个对照过原图(不是"看着像")
- [ ] 报告里写的每一处"原文不一致",都回原图验证过
- [ ]
check_delivery.py六项全 PASS - [ ] 没有把"渲染成功"写成"提取正确"
硬性规矩
- 禁止编造:找不到的定义、看不清的符号,标
unresolved/needs_review,不猜。 - 禁止按语义纠正原文:原文的笔误、不配平括号、记号不统一,一律照录 + 批注。
- 不重复犯错:同一个错误出现第二次,说明上一次只修了症状。要补一条可执行检查
(脚本 / 门禁 / 必填字段),只写"下次注意"视为未修复。 - 证据必须保存:原图切片、吸附前后坐标、模型原始输出,都要随结果留存。
- 口径纪律:区域覆盖 ≠ 独立对象 ≠ 整式忠实;无人工确认时只能称"草案"。
文件说明
| 文件 | 作用 |
|---|---|
scripts/formula_snap.py |
粗框 → 精确框(三级吸附,附 edits 记账) |
scripts/normalization_guard.py |
过度归一化拦截(凭空下标、括号被配平) |
scripts/symbol_consistency.py |
文档级符号写法一致性(四类,只报不判) |
scripts/build_delivery.py |
交付 HTML(PDF / 图片两种源) |
scripts/check_delivery.py |
交付门禁(六项) |
scripts/validate_record.py |
记录契约校验(--selftest 自检) |
scripts/check_env.py |
环境自检(缺件即停) |
assets/formula-schema-v0.1.json |
公式记录的数据契约 |
references/workflow.md |
每步的命令、判据与常见弯路 |
references/fidelity-rules.md |
照录规则的边界与自检清单 |
references/delivery-contract.md |
交付物清单、验收项、口径红线 |
references/inventory-prompt.md |
盘点提示词模板 |
references/inventory-schema.json |
盘点输出契约 |
环境
- Python 3.9+
- 必需:
pymupdf、pillow、numpy - 可选:
scipy(连通域更快,缺失时自动降级)、latex2mathml(离线预览,缺失则只显示源码)、
jsonschema(记录校验) - 自检:
python scripts/check_env.py - 缺件要停下报错,不要自己造桩或临时安装绕过——那会让交付物在别人机器上直接崩。
常见弯路
| 现象 | 原因 | 对策 |
|---|---|---|
| 盘点两次条数不同 | 无编号公式的口径差异 | 宽口径 + 事后淘汰,不追求计数完全一致 |
| 吸附后框变小很多 | 墨迹被切成多个簇,只取了主体 | 看 edits;大幅收缩必须人工确认 |
| 双栏页面框横跨两栏 | 编号锚点把另一栏同高度内容也算进来 | 已在脚本内按栏间空白隔离 |
| HTML 看着是旧的 | 同名文件被缓存 | 文件名自动带内容指纹,核对 <title> 里的版本 |
| 扫描件行带粘连 | 相邻行间隙小于公式内部间隙 | 纯几何无解,靠人工裁 |