无书签 PDF 的大纲从哪里来:三级容错的解析链路
去年接的第一个活,做到现在终于稳定了。问题很具体:客户给的技术文档是 PDF,大多没有书签,几百页,要按章节结构切成条目给下游系统用。核心难点只有一个——大纲从哪来。
三个来源,没有一个够
书签。 有书签的 PDF 直接读书签树,准确、快。但客户的文档里有书签的不到三成,而且不少书签是错的(扫描件用工具补的)。
多模态模型。 把页面图片喂给视觉语言模型,让它标出标题和层级。效果比想象中好,但两个问题:一是贵、慢,几百页要跑很久;二是它给的坐标不准,后面单独写一篇。
启发式规则。 按字体大小、加粗、编号模式(1.、1.1、1.1.1)判断标题。快、免费,但遇到排版不规范的文档就乱。
三个来源单独用都不行。
链路
最后做成了三级容错:
PDF ──► 有可信书签? ──是──► 书签树 ────────────────┐
│否 │
▼ │
版面解析(MinerU)出文本块与坐标 │
│ ▼
▼ 大纲树
多模态模型标注标题 ──► 坐标回锚 ──► 层级推断 ─┘
│失败/超预算
▼
启发式规则(字体特征 + 编号)
每一级的判断标准:
- 书签”可信”的定义:书签数量和页数比例合理,且抽样几个书签跳转到的页面上确实有对应标题文字。
- 模型级失败的定义:调用超时、返回格式不对、或者标注出的标题数量明显离谱(比如一页十几个)。
- 启发式是最后兜底,永远能出结果,只是质量可能差。
层级推断为什么单独一步
模型标出来的标题带层级,但经常错——它会把”3.1”标成一级,把”3”标成二级。所以层级不信模型,单独推:
- 先看编号:点号数量决定层级(
3一级,3.1二级)。OCR 噪点要容错,3 .1、3.1都得认成3.1。 - 没编号的标题,用模型标注的层级,但要经过”单调性检查”:前一个标题是二级,下一个不能直接跳到四级。
- 两个来源冲突,编号优先。
这一步修掉了最烦人的一类错误:阶梯式嵌套——每个标题都比上一个深一级,整棵树歪成一条斜线。
切片
大纲树有了,切片就是按标题的坐标范围截页面。300 DPI 渲染,跨页的章节拼接。这部分没什么难的,难的是坐标要准,而坐标准不准取决于回锚那一步。
验收口径
客户验收时看的是”大纲对了多少、切片对了多少”,目前稳定在 94% 以上。剩下的 6% 集中在两类文档:扫描质量差的,和排版完全不规范的(标题不加粗不编号,纯靠空行分隔)。这两类靠链路解决不了,得靠人工复核,系统里留了复核入口。
一句话
模型不可靠,规则不聪明,书签不齐全。三个不完美的来源按可信度排队,每一级只接自己能确定的,剩下的往下传。这个结构后面在别的项目里又用了几次。