合同签名批量核验
把「一整批签署文件」变成「一张可复核的核验表」:每份文件的签名写了什么、和文件名对不对得上、哪几份需要人工再看一眼。
本 Skill 由一条五阶段流水线构成。原项目里的五个专家角色,在这里收敛为同一条流水线的五个阶段——不再需要多次转交,一个执行体依次跑完即可:
| 阶段 | 原角色 | 干什么 | 绝不干什么 |
|---|---|---|---|
| S0 编排 | 阎王爷 | 定路径、拆任务、定判定口径、维护进度 | 不替下游做专业判断 |
| S1 清点 | 八部天龙 | 扫目录、登记文件名与元数据 | 不读文件内容 |
| S2 切分定位 | 庖丁 | 分出主体 / 边角 / 签署单元,给签名框坐标 | 不改内容、不认字 |
| S3 签名识别 | 火眼金睛 | 读签名位图,输出签名文字与置信度 | 不猜、不硬认 |
| S4 对应判定 | 移花接木 | 签名 ↔ 文件名比对,出结论 | 不臆造、不擅自择一 |
何时用 / 何时不用
用:
- 「把这个文件夹里的合同签名都识别出来」
- 「核对这些扫描件的签名和文件名是不是同一个人」
- 输入是一批签署文件(PDF / 扫描件 / 照片),文件名即人名(或需与正文人名核对)。
不用:
- 单份文件的普通 OCR(用通用文字识别流程即可)。
- 合同条款抽取、金额核对(见 references/ 之外的其他技能;本 Skill 只管签名)。
- 需要对签名真伪做司法鉴定 —— 本 Skill 只做文字识别与对应核验,不出具真伪结论。
S0 · 编排:先把口径钉死
开工前必须向用户确认四件事,缺一件就停下来问:
- 源目录:固定路径是什么?含不含子目录?
- 参照人名从哪来:文件名即人名,还是另有一份名册?
- 判定口径:默认按下面的默认口径;用户另有规则时以用户的为准并写进最终报告。
- 输出形态:只要结论表,还是同时要每份文件的逐项明细 + 需复核件清单。
确认后输出一张任务流总表(哪怕只有一个阶段也要给,便于用户跟踪),并在每阶段结束时通报一句"做了什么、拿到什么、下一步谁上":
| 阶段 | 任务 | 类型 | 依赖 | 产出 | 验收标准 |
|---|---|---|---|---|---|
| S1 | 批量清点 | 同步 | — | manifest.json / index.csv |
文件数、异常项齐全 |
| S2 | 切分定位 | 同步 | S1 | probe.json + 签名裁切图 |
每份文件的签署栏都有交代 |
| S3 | 签名识别 | 异步(批量耗时) | S2 | signatures.json |
每枚签名都有文字或显式标注 |
| S4 | 对应判定 | 同步 | S3 | verdict.md / verdict.csv |
每份文件都有结论 |
异步纪律:S3 是长耗时环节。批量超过约 20 份时,分批推进并逐个文件通报进度,不要闷头跑完才出声。派出去的批次必须有回收点——每批结束即结算,不留悬空任务。
编排细则与阶段间数据契约见 references/pipeline-contract.md。
S1 · 清点:只认名字,不认内容
python scripts/batch_scan.py --source "D:/签署件" --out ./cs-out --ext pdf,png,jpg,jpeg
脚本做四件事,全部只涉及文件名与元数据:
- 类型筛选:按扩展名白名单过滤,非白名单直接跳过(不报错)。
- 自然序排列:
IMG2排在IMG10之前(不是字典序);中文名按 Unicode 序稳定排列。 - 格式诚实性校验:读文件头魔数比对扩展名,
伪装成PDF的图.pdf(实为 PNG)会被标formatOk: false——只标注,不改文件。 - 重复检测:算 SHA-256,检出内容完全相同的重复文件组。
产出 <out>/<批次号>/:manifest.json(程序消费)、index.csv(UTF-8-BOM,Excel 直接开不乱码)、index.md(人读)。
把 formatOk: false 与重复组整理成表摆给用户看,再进入 S2。文件名字节原样保留,绝不改写、绝不规范化——中文、空格、括号、多后缀都在所不惜,因为文件名就是本任务的比对基准。
S2 · 切分定位:找到那道缝
对每一份文件,先判断形态,再定位签署区域。
python scripts/probe_pdf.py --input "D:/签署件/张三.pdf" --out ./cs-out/probe/张三
侦察输出(probe.json)包括:
- PDF 类型:有文字层(正文走文字层抽取,零错字)还是扫描件(整页走图像识别);
- 签署栏标签坐标:get_text("blocks") 取「签字:」「承诺人:」「甲方:」等标签位置;
- 签名候选:位图 get_image_info(xrefs=True)、矢量笔迹 get_drawings()、批注 page.annots() 三路穷尽;
- 排除项:公章、骑缝章、勾选标记 —— 单列备查,不计入个人签名。
裁切签名必须走页面渲染裁剪,不要用 extract_image:
python scripts/crop_region.py --input "D:/签署件/张三.pdf" --page 1 --bbox 320,540,470,590 --out ./cs-out/sig/张三-s1.png --dpi 600
⚠️ doc.extract_image(xref) 取出的签名常是遮罩存储,直接保存会得到纯黑一片。必须从页面渲染中按 bbox 裁切,get_pixmap(clip=..., dpi=600)。
切分与定位的完整规则(保护性留白、完整性铁律、矢量笔迹查法)见 references/signature-recognition.md。
S3 · 签名识别:宁可标注,不可硬猜
用 Read 工具直接查看裁切出的 PNG(600 dpi 足够)。识别时同时采集旁证:
- 与页内文字层出现的打印体姓名逐字比对;
- 与身份证号、准考证号、工号等其他字段互证;
- 哈希交叉验证:多份文件若出现同一枚签名位图(SHA-256 一致),说明是同一枚签名被反复贴入,可互为交叉样本,识别置信度大幅提升。
置信度分级(必须标注):
| 级别 | 判据 | 处置 |
|---|---|---|
| 高 | 字形清晰、笔画完整、旁证一致 | 直接采用 |
| 中 | 连笔但主笔可辨,旁证基本吻合 | 采用并标注「建议复核」 |
| 低 | 字形在两个字之间摇摆、手机拍照件分辨率不足 | 降置信,给复核件路径,不硬猜 |
铁律:
- 看不清写 [?],不臆造、不脑补。
- 空白签署栏写「未检出签名」,不要写成「文件异常」。
- 签名连笔潦草、有限增强后仍不可辨 → 判「字迹不合规」,不再做反复锐化处理(这是项目的明确要求)。
- 「文件异常」只用于完全不是签署文件的输入(软件界面截图、风景照、空白页等)。
S4 · 对应判定:签名 ↔ 文件名
一份文件可能有多枚签名。全部输出,再整体与文件名比。
默认判定口径(用户另有规则时以用户为准):
| 情形 | 结论 |
|---|---|
| 签名文字与文件名完全一致 | 正确 |
| 一份文件含多枚签名,任一与文件名能对应 | 正确 |
| 签名文字与文件名不一致 | 错误 |
| 完全不是签署文件(软件界面截图、风景照、空白页…) | 文件异常 |
| 文件读取/解析失败、非签署类材料 | 文件异常 |
| 签名连笔潦草、有限增强后仍不可辨 | 字迹不合规 |
python scripts/verdict.py --signatures ./cs-out/signatures.json --out ./cs-out
批量必查的两个坑(命中率很高):
- 文件名与内容指向不同的人:PDF 文字层里的打印体姓名、身份证号是硬证据,先抽出来和文件名对一遍。曾见
谢新豪.pdf通篇是「谢新蒙」的承诺书+签名 → 错配。 - 文件名是姓名简写/缺字:如
新蒙.pdf内签名与正文均为「谢新蒙」。严格按「完全一样」判即「错误」,但必须在备注里点明「疑为同一人简写,请人工确认」,不要直接等同于换人错签。
判定细则、多签名与简写处理、结论表述见 references/judgement-rules.md。
输出规范
逐份一行:文件 → 签署栏位置 → 签名文字 → 旁证 → 结论。多枚签名就输出多行。
| 文件名 | 序号 | 签署栏位置 | 签名文字 | 旁证 | 置信度 | 结论 |
|---|---|---|---|---|---|---|
| 张三.pdf | 1 | 第2页 承诺人签字栏 | 张三 | 正文打印体「张三」、身份证号一致 | 高 | 正确 |
| 李四.pdf | 1 | 第1页 甲方栏 | 李五 | 正文打印体「李五」 | 高 | 错误 |
另外三件东西不能少:
1. 总览:共几份、正确几份、错误几份、文件异常几份、字迹不合规几份。
2. 需人工确认项单列:对方签章是否缺失、空白栏是否为漏签、简写疑同人、文件是否属"合同"口径 —— 识别环节不做法律判断。
3. 可追溯:每份文件的签名裁切图路径一并给出,便于复核。
模板见 references/output-templates.md。
铁律(任何阶段不得违反)
- 不臆造:看不清、查不到就说看不清,用标注(
[?]/ 「未检出」/「待确认」)代替猜测。这与本 Skill 的名字相称。 - 不改原文:不润色、不改写、不翻译、不静默删减。手写补充、涂改一律原文保留并单独标注。
- 文件名即主键:S1–S3 全程保留原始文件名,不截断、不替换、不去重改名。
- 不越界:只做识别与对应核验。不做条款效力判断、不出具法律意见、不对签名真伪做司法结论。
- 异常要曝光:失败、跳过、降置信、复核对不上,都要在最终报告里写明,不静默消化。
- 不做反复锐化:字迹潦草就是潦草,有限增强后仍不可辨即判「字迹不合规」并停下。
- 隐私与合规:签署件含个人身份信息,结果只写入用户指定的输出文件,不主动外传、不写入与任务无关的文件。需要脱敏时按用户规则做掩码。
- 成本要告知:批量识别会消耗额度,开跑前先说明规模(多少份、多少页),得到确认再执行。
常见坑速查
| 现象 | 原因 | 处理 |
|---|---|---|
| 抽出的签名是纯黑块 | 遮罩存储 | 改从 get_pixmap(clip=...) 裁切 |
| 文字层里签署栏为空 | 签名是位图不是文字 | 走图像路径,别以为文件没签名 |
| 一页多个签署栏只找到一枚签名 | 其余栏确实空白,或签名是矢量的 | 先查 get_drawings();再渲染切片目视证实每一栏 |
| 把公章 / 勾选(✓)当成签名 | 未区分签署主体 | 单列备查,不计入个人签名 |
| 浅灰印刷字被当成手写签名 | 底纹/水印 | 提高阈值或调 dpi 重裁 |
| 手写连笔在两个字之间摇摆 | 拍照件分辨率不足 | 二值化+锐化+逐字放大;仍不确定则降置信并给复核件 |
完整坑位表见 references/pitfalls.md。
依赖
- Python:
pymupdf(必备);Pillow(图像裁切/增强)。 - 安装到隔离虚拟环境,不要污染全局环境。
numpy与opencv-python为可选增强项,缺失时脚本自动降级。
文件索引
references/pipeline-contract.md— 五阶段输入输出契约、编排与同步/异步规则references/signature-recognition.md— 签名定位与识别专章(三路径排查、裁切、哈希交叉验证)references/judgement-rules.md— 判定口径与边界情形references/output-templates.md— 总表、逐项明细、汇总报告模板references/pitfalls.md— 常见坑位全表scripts/batch_scan.py— S1 批量清点(登记文件名与元数据)scripts/probe_pdf.py— S2 形态判定与签名候选侦察scripts/crop_region.py— S2 高清裁切(规避遮罩坑)scripts/verdict.py— S4 对应判定与汇总报告