Files
hermes-skills/skills/legal/contract-portfolio-analysis/scripts/ocr-garble-detect.py
T

117 lines
5.0 KiB
Python

#!/usr/bin/env python3
"""
OCR乱码检测器 — Step1 OCR完成后必跑。
扫描OCR文本中所有疑似乱码行,输出vision核实清单。
每一处乱码都必须vision对应PDF页面核实后才能进Step2。
用法: python3 ocr-garble-detect.py <ocr_file.md>
判定规则:
1. 多个无意义英文片段(连续3+字母非常见词)
2. 中文占比过低(正常合同行>50%,低于30%标红)
3. 中英混杂碎片(典型OCR乱码模式)
4. 异常符号密集(>20%非正常字符)
误报处理:
- 纯数字表格行(日期+金额)→ 正常,核对数字即可
- 邮箱/网址/银行账号 → 正常
- 英文缩写(USD/RMB/PDF)→ 正常
真正需要vision的是:含中文碎片+英文乱码的混合行(如"oe方,同Se"
教训(凤凰文化0701):
Line 204 "Se【2024】年【10】月【15】日" 被跳过未核实,
导致整段违约金条款丢失(实际是"初年年租金20%作为违约金")。
代价=审查结论反转("无违约金""有20%违约金")。
"""
import re, sys
def is_garbled(line):
"""判断一行是否疑似乱码,返回原因或None"""
stripped = line.strip()
if not stripped or len(stripped) < 5:
return None
# 1. 连续3+个无意义英文字母组合(非常见英文词)
nonsense_en = re.findall(r'[a-zA-Z]{3,}', stripped)
common_words = {'pdf','ocr','usd','rmb','the','and','for','with','from',
'www','com','xdf','jpg','png','doc','docx','xlsx','occ',
'vision','step','check','null','true','false'}
real_nonsense = [w for w in nonsense_en
if w.lower() not in common_words
and not re.match(r'^[A-Z]{1,4}$', w)]
if len(real_nonsense) >= 2:
return f"多个无意义英文片段: {real_nonsense[:3]}"
# 2. 单行中文字符占比过低(正常合同行中文应>50%)
chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', stripped))
total_chars = len(re.findall(r'\S', stripped))
if total_chars > 10 and chinese_chars / total_chars < 0.3:
# 排除纯数字表格行(日期+金额)和邮箱/账号行
if re.match(r'^[\d\.\-\s\|/,]+$', stripped):
return None # 纯数字表格行
if '@' in stripped or re.match(r'^[A-Z]{2,4}:', stripped):
return None # 邮箱或字段标签
return f"中文占比过低({chinese_chars}/{total_chars}={chinese_chars/total_chars:.0%})"
# 3. 常见OCR乱码模式:小写英文碎片+中文混杂
if re.search(r'[a-z]{2,}\s+[a-z]{2,}\s+[a-z]{2,}', stripped) and chinese_chars > 0:
return "中英混杂碎片(典型OCR乱码)"
# 3b. 日期区域英文字母污染(凤凰文化10.2教训:Se【2024】年【10】月→整段丢失)
if re.search(r'[A-Za-z]{2,}\s*【\d{4}', stripped) and chinese_chars > 0:
return "日期区域字母污染(高风险:可能整段条款被OCR吃掉)"
# 4. 特殊符号异常密集
special = len(re.findall(r'[^\w\u4e00-\u9fff\s,。、;:""''()【】《》\-\+\.\%\/\|]', stripped))
if special > 5 and total_chars > 0 and special / total_chars > 0.2:
return f"异常符号密集({special}个)"
return None
def main():
if len(sys.argv) < 2:
print("用法: python3 ocr-garble-detect.py <ocr_file.md>")
sys.exit(1)
filepath = sys.argv[1]
with open(filepath, 'r') as f:
lines = f.readlines()
garbled = []
for i, line in enumerate(lines, 1):
reason = is_garbled(line)
if reason:
garbled.append((i, line.strip()[:80], reason))
if not garbled:
print("✅ 未检测到明显乱码行。可以进入Step 2。")
else:
# 区分真乱码 vs 可能误报(纯数字/邮箱等)
real_garble = [g for g in garbled if '无意义英文' in g[2] or '中英混杂' in g[2]]
maybe_garble = [g for g in garbled if g not in real_garble]
print(f"⚠️ 检测到 {len(garbled)} 处疑似乱码(其中 {len(real_garble)} 处高危)\n")
if real_garble:
print("🔴 高危乱码(必须vision核实,不核实不进Step2):")
for lineno, text, reason in real_garble:
print(f" Line {lineno:3d} | {reason}")
print(f" | {text}")
print()
if maybe_garble:
print("🟡 疑似乱码(核对数字/格式是否正确):")
for lineno, text, reason in maybe_garble:
print(f" Line {lineno:3d} | {reason}")
print(f" | {text}")
print()
print(f"--- 高危 {len(real_garble)} 处必须vision核实 + 疑似 {len(maybe_garble)} 处核对数值 ---")
print("操作:对每处高危乱码,定位PDF页码 → vision精读 → 记录修正内容")
print("全部消灭后才能进入 Step 2。")
if __name__ == '__main__':
main()