让 AI 批量处理 300 份 PDF:把「跑一遍」变成「跑得住」的工程细节
一次性把几百份合同、财报、发票喂给 AI 抽取信息,坑几乎全在失败处理、成本控制和结果校验上。本文给出一套可复用的小型批处理流水线设计。
“帮我从这 300 份 PDF 里把甲乙方、合同金额、签订日期、违约金条款抽出来。”
这句话听起来是个简单的批量任务,实际做起来,大部分时间不会花在调用模型上,而会花在:有的 PDF 是扫描件、有的表格跨页、有的文件损坏、有的抽取结果明显不对、跑到第 180 份时 API 限流了、账单超出预期三倍。
这篇文章讲怎么把这类任务做稳。
先做 10 份的”代表性抽样”
在写代码之前,从 300 份里随机抽 10 份,逐份用人工或单次对话处理一遍。目的是搞清楚:
- 有多少是扫描件(需要 OCR)
- 版式有几种(决定是否需要不同的解析策略)
- 字段的位置是否固定(固定位置可以走模板,不固定才需要模型理解)
- 有哪些特殊情况(附件、补充协议、手写签名页)
这一步一小时能完成,但决定了后面所有设计。跳过它直接写批处理脚本,通常会在第 50 份左右发现”原来还有这种文件”。
解析层:PDF 不是文本
PDF 是排版格式,不是文本格式。同一句话在 PDF 里可能是一堆绝对定位的字符片段。直接用底层库抽文本,经常得到:跨栏错乱、表格散成一行、页码混进正文、页眉页脚插在段落中间。
推荐分两步:
第一步,判断文件类型。 用解析库检查是否包含文本层。没有文本层就是扫描件,走 OCR 路径。有文本层但抽取出来的字符数异常少(比如每页不到 50 字),大概率是”看起来有文本层实际上是图片外框”的伪文本 PDF,也要走 OCR。
第二步,优先用布局感知的解析。 输出 Markdown 而不是纯文本——标题层级、表格、列表会被保留,模型读 Markdown 的效果明显好于读一堆乱序文本。跨页表格是被低估的难点,需要在解析后做一次”相邻页表格合并”。
关键的降级策略:如果某份文件解析后文本长度低于阈值,标记为 needs_manual 而不是继续送去抽取。让模型从残缺文本里硬抽,产出的错误比不产出更麻烦。
抽取层:字段级输出,不要整段总结
输出格式直接决定可用性。错误做法是让模型”总结这份合同的关键信息”,得到一段流畅但结构不稳的文字。
正确做法是显式定义 schema:
{
"party_a": {"name": "string", "credit_code": "string|null"},
"party_b": {"name": "string", "credit_code": "string|null"},
"amount": {"value": "number|null", "currency": "string", "in_words": "string|null"},
"sign_date": "YYYY-MM-DD|null",
"penalty_clause": {"has": "boolean", "rate": "string|null", "source_text": "string"}
}
三个设计要点:
一、允许 null。 强制要求每个字段都有值,模型会编。明确告诉它”不存在时返回 null”,比事后补救有效得多。
二、要求原文引用。 关键字段(金额、日期、违约金)必须附带 source_text,即从文档中逐字抄录的原文片段。这给校验提供了锚点——如果 source_text 在原文里找不到,这个字段就不可信。这是对抗幻觉最实用的手段。
三、数字字段用字符串包裹再解析。 金额可能是”壹佰贰拾万元整”或”1,200,000.00”,让模型输出原始表述,由代码做规范化,比让模型直接输出数字更可靠。
执行层:把失败当成常态
批处理脚本的第一原则:单个失败不影响整体,且失败可重试。
具体设计:
- 状态落盘。 每份文件的处理状态写入本地数据库或 JSONL(pending / done / failed / needs_manual),支持断点续跑。跑到一半崩了,能从断点继续
- 幂等。 用文件哈希做 key,重复执行不会重复计费
- 有限重试 + 指数退避。 对限流(429)和超时重试,对解析失败(400)不重试——重试只会浪费钱
- 并发受控。 并发数不要拍脑袋定,从 3 开始压测往上加,观察限流率和整体耗时。很多任务在并发 5 时最划算,再高就开始频繁限流
- 超时与熔断。 单份文件超过 90 秒直接判失败,避免拖垮整批
成本预估要在跑之前做。 用抽样的 10 份算出平均 token 消耗,乘以文件数,加上 20% 的重试余量。如果这个数字超出预算,先优化方案(换便宜模型、缩短输入、只对关键字段走大模型),而不是跑完再心疼。
校验层:三类自动检查
跑完之后的质检决定了这批数据能不能用。至少做三类:
一、字段级格式校验。 日期能解析、金额是正数且在合理区间、信用代码位数正确。这类检查能抓出 30% 到 50% 的明显错误。
二、原文锚定校验。 检查 source_text 是否真的出现在解析后的原文中。找不到就标记为可疑。这一步对幻觉的拦截效果非常好。
三、交叉一致性校验。 合同金额的大写与小写是否一致、签订日期是否在合同有效期内、甲方名称与信用代码是否匹配。业务规则能检出格式检查发现不了的逻辑错误。
通过全部检查的进入 done,任一项失败进入 review。经验上,全自动通过率会在 80% 到 90% 之间——剩下那 10% 到 20% 才是人工该花时间的地方。这比全量人工省 80% 的工作量,又比全自动可靠得多。
交付:让人工审阅更省力
给审阅者的界面/表格要包含:文件链接、抽取结果、可疑字段高亮、原文定位。
最重要的是”带证据的展示”:每个字段旁边显示原文片段和页码。审阅者不用来回翻 PDF,直接看证据判断对错。这个小设计能把单份审阅时间从几分钟压到几十秒。
另外,审阅的修正结果要回流成评测样本。处理完 300 份,你就有了一批带人工标注的测试集,下次换模型或改提示词时可以快速验证。
一个时间预期
300 份 PDF 的项目,合理的时间分配大致是:
- 抽样与方案设计:半天
- 流水线开发:一到两天
- 试跑 30 份并调优:半天
- 全量跑 + 自动校验:一到两小时
- 人工审阅剩余 15%:半天到一天
也就是说,真正”跑”的时间不到总时间的 10%。批量 AI 任务的成本从来不在模型调用上,而在把不确定性管住的设计上。