通用票据数据提取器
触发词:票据提取 / 发票识别 / 票据数据提取 / 发票文件夹解析
一句话:扫描任意混放票据文件夹,PDF 文本直读、图像走 OCR 加 VLM 补录,输出三层 Schema 的 JSON 加 Excel,任何输入不空手而归。
前置依赖
| 依赖 | macOS/Linux 验证 | Windows 验证 |
|---|---|---|
| pdftotext(poppler) | which pdftotext |
where pdftotext |
| openpyxl | venv 内 python -c "import openpyxl" |
同左 |
| 百度密钥(可选) | 环境变量 BAIDU_API_KEY / BAIDU_SECRET_KEY | 同左(系统环境变量或 PowerShell $env:BAIDU_API_KEY) |
pdftotext 使用前必须先检测:有就直接用(无需任何部署),没有才按用户电脑系统本地部署。检测:macOS/Linux which pdftotext;Windows where pdftotext。有路径输出=已安装,跳过部署直接使用。未安装时按系统部署——macOS:brew install poppler;Windows:scoop install poppler 或 choco install poppler,或从 poppler-windows release 下载后把 bin 目录加入 PATH(重开终端生效);Linux:apt install poppler-utils。部署后复测确认;仍不可用时脚本会打印警告并把 PDF 全部按扫描件处理(自动降级 VLM 路线,不中断),并明确告知用户 pdftotext 未装好。
跨平台说明(macOS / Windows / Linux)
- 脚本纯 Python 标准库加 openpyxl,路径处理全部使用 os.path / os.walk,三平台通用
- Windows 控制台如遇中文乱码,先执行
chcp 65001切换 UTF-8 代码页 - 技能目录路径:macOS/Linux 为
~/.workbuddy/skills/ticket-extractor/,Windows 为%USERPROFILE%\.workbuddy\skills\ticket-extractor\ - 运行示例:
- macOS/Linux:
BAIDU_API_KEY=xxx BAIDU_SECRET_KEY=xxx python3 ticket_extractor.py <票据文件夹> --out-dir ./output - Windows PowerShell:
$env:BAIDU_API_KEY="xxx"; $env:BAIDU_SECRET_KEY="xxx"; python ticket_extractor.py <票据文件夹> --out-dir .\output - Windows CMD:
set BAIDU_API_KEY=xxx && set BAIDU_SECRET_KEY=xxx && python ticket_extractor.py <票据文件夹> --out-dir .\output - ZIP 包内文件名全为 ASCII,Windows 解压无中文乱码风险
五步工作流
-
扫描过滤:递归遍历用户给的文件夹,扩展名白名单 pdf/jpg/jpeg/png/ofd,过滤 DS_Store、Thumbs.db、desktop.ini。直接运行
scripts/ticket_extractor.py。 -
PDF 直读解析:脚本自动处理。pdftotext layout 模式探测文本层,有则按票种关键词分诊(成品油/通行费/增值税/定额/机打)后解析。核心规则见 references/spec.md 第 5 节:明细行从右往左取数;成品油折扣行仅 4 个 token,必须先判折扣行结构再做常规过滤(最高频 bug)。
-
图像分诊路由(与架构第②③层对齐):图像先经 VLM 分诊台确认票种清单、张数、区域与质量,再按分诊路由——票种有专用接口且已配密钥时走百度对应接口(结构化+置信度),无专用接口或分诊不确定时走 VLM 全字段提取;纯脚本环境无 VLM 时直接走百度接口,错误码 17 额度满、282103 模板不匹配(粉色广东定额票和机打发票必触发)、6 无权限均自动降级。
-
VLM 补录(助手亲自做,补录即分诊加提取合一):跑完后统计 extract_method 为 vlm_pending 的记录,逐张用你的视觉能力先分诊(票种、张数、质量)再提取,按三层 Schema 生成 vlm_session 记录合并回 JSON 并重新生成 Excel。纪律:看不清的字段填空并在 errors 注明,禁止编造;多票粘贴单输出逐票清单加页合计;倒贴票先摆正;印章不全标待核。
实操要点(2026-09 车杂费实战总结):
- 手机拍摄的粘贴单常整体旋转 90 度,先摆正(PIL rotate(90/‑90, expand=True))再识别。
- 不要整图硬认:按 2x2 象限裁切并放大 3-5 倍再读,票号/面额/金额误读率显著下降;可疑票号必须单独放大复核。
- 定额发票的面额标识(打孔/圈选)常被发票专用章压住,此时用「粘贴单手写页合计 ÷ 票据张数」交叉验证,同版式多页结果一致即可取值,并在 errors 写明推算来源。
- 粘贴单手写页合计是最强勾稽锚点:逐票金额必须能加总回它,加不回就是读错了。
- 用户给的文件夹可能不完整(编号跳号);若有配套台账(如车务档案 xls),用「票面合计 vs 档案合计/已通过合计」对总账,差额往往指向"被拒绝的申请但票据仍在文件夹里"。
- 输出建议双表:Sheet1 一文件一行(技能原生 Schema),Sheet2 一票一行(报销/入账可直接用)。
- 合并输出:写 extracted.json 与 extracted.xlsx,打印各提取方式分布统计。向用户汇报成功数、降级数、待核项。
运行时检查表(已知坑,出问题先对表)
- 恒等式批量失败:先查折扣行 4 token 结构是否被常规过滤吞掉
- 票号对不上:查是否多图提交乱序,改逐文件单独验证
- 同票号出现两次:可能是同页两拍,按票号集合判重
- 粘贴单手写合计对不上:以票面为准,差异标注待报销人核实
- 发票号显示科学计数:Excel 写入必须文本格式
- 粉色定额票 OCR 失败:正常现象(模板限制),走 VLM 补录
- 扫描件 PDF 提不出字:无文本层,走 VLM 路线
- 垃圾文件报图片格式错:扫描层黑名单是否生效
- Sheet2 明细恒为空、Sheet3 票面小写恒为空:parse_vat 是否漏挂载
rec["extension"] = ext(v1.1 曾踩,v1.1.1 已修) - 票面小写少两位小数(读成 931 而非 931.00):layout 把小数片段甩到相邻行,v1.1.1 会在匹配位前 200 字符窗口内自证式补全;补全过的记录 extension 带
票面小写已按金额+税额补全小数溯源戳 - 结论列出现「通过(附注)」:备注为可自证提示(小写未读出 / 小写被折断),不是错误,无需人工介入
工程纪律
- 原始发票只读,产出写独立输出目录
- 密钥只走环境变量,永不写入代码、文档、输出
- 每条记录必带 extract_method 溯源戳
- 单票失败不影响整批,错误进 errors 数组
参考文档
- references/spec.md:完整开发规格(六层架构、三层Schema、六道勾稽、验收标准)
- references/rules_v1.2.md:制作规则手册(部署方案、降级矩阵、坑清单详解)
适用与不适用
| 场景 | 支持 |
|---|---|
| 混放票据文件夹批量提取(PDF/JPG/PNG) | 支持 |
| 成品油/通行费/增值税/定额/机打发票 | 支持 |
| 一页多票粘贴单(逐票清单+页合计) | 支持 |
| 扫描件 PDF(无文本层) | 走图像路线,需 OCR 密钥或 VLM |
| 生成报销台账/费用对账/审批流程 | 不支持(本技能只做原始数据提取) |
| 发票真伪验证 | 不支持(需查验平台独立核验) |
已知限制
- pdftotext 未安装时 PDF 直读不可用,自动降级为图像路线(脚本会打印各平台安装指引)
- 百度定额接口对部分地方版式票据模板不匹配(实测粉色广东定额票失败),自动降 VLM 补录
- 百度 OCR 免费额度有限(混贴接口日限额),大批量需自备付费密钥
- VLM 补录依赖助手会话的视觉能力,离线脚本单独运行时该路线为占位状态
- 粘贴单手写合计可能与人写错,技能以票面明细为准,差异标注待人工核实
版本历史
| 版本 | 日期 | 变更 |
|---|---|---|
| 1.0.0 | 2026-09-19 | 首发:四路引擎(文本直读/百度OCR/PaddleOCR接口/VLM)、三层Schema、降级链、跨平台(macOS/Windows/Linux) |