OCR 리서치/05 — 07
읽기를 완벽하게 만드는 게 아니라, 틀린 것을 잡는 그물을 여러 겹 친다
결론 다섯 줄
- 스캔본이 아니면 OCR을 안 쓴다. 글자층이 있는 PDF는 그대로 읽으니 오독이 없다. (KPF 샘플은 전부 팩스 스캔이라 OCR 필수)
- 스스로 검산한다. 수량×단가=금액, 합계, 인보이스와 패킹리스트 수량·무게 일치. 계산이 안 맞으면 신뢰도가 높아도 사람 확인.
- 아는 값과 대조한다. 부품 기준표, 거래처 목록, HS 코드 표.
- 자신 없는 칸만 사람이 본다. 원본을 확대해 옆에 보여준다. 업계 목표: 60~75%는 사람 손 안 타고 통과, 최종 오류 0.5% 미만.
- 위험한 칸은 기준을 높인다. 금액·수량은 아주 확실할 때만 자동, 비고는 느슨하게.
추천 파이프라인
| 단계 | 내용 | 근거 |
|---|---|---|
| 0 문서 판정 | 페이지마다 글자층이 있는지 본다(PyMuPDF get_text). 있으면 OCR 없이 파싱, 없으면 OCR. 한 PDF 안에 섞일 수 있으니 페이지 단위 | BSWEN |
| 1 페이지 분류·분할 | 페이지마다 종류와 "새 문서 시작" 여부를 태깅해 세트(건)를 만든다. 세트가 있어야 두 서류 대조가 된다 | DocSplit, LandingAI |
| 2 구조화 추출 | JSON 스키마를 강제하는 tool-use로 AI 비전 모델 호출. 칸마다 신뢰도를 같이 받는다. 과한 스키마 제약은 추론력을 약간 낮출 수 있다 | OpenAI docs, SLOT |
| 3 방어적 검증 | 산술 검증, 세트 간 대조, 신뢰도 임계값 → 칸마다 자동확정 / 확인 / 보류 | 아래 표 |
| 4 확인 화면 | 낮은 신뢰도·검산 실패 칸만 강조, 원본 해당 부분 확대. 전 칸을 다 보여주면 도장 찍기식 승인이 된다 | Parseur |
| 5 엑셀 반영 | 확정된 세트를 시트로 추가 | — |
방어책 10겹
| # | 방어책 | 무엇을 잡나 | 비용 |
|---|---|---|---|
| 1 | 신뢰도 점수 + 칸별 임계값 (금액 0.95~0.98, 비고 0.85) | 화질·애매한 칸의 저확신 오독 | 낮음 |
| 2 | 산술 교차 검증 (수량×단가=금액, 줄 합=소계, 소계+운임=총계) | 신뢰도는 높은데 틀린 값(자릿수·소수점) | 거의 0 |
| 3 | 세트 간 대조 (인보이스 수량 = 패킹 수량, 무게·포장수) | 세트 자체의 앞뒤 불일치 | 낮음 |
| 4 | 이중 엔진 합의 (실패 패턴이 다른 두 엔진이 불일치하면 표시) | 한 엔진의 체계적 오독 | 중~높음 |
| 5 | 글자층 우선 | OCR 오류 원천 차단 | 없음 |
| 6 | 세트 분할 검증 (예상 건 수 ≠ 쪼갠 건 수면 표시) | 경계 오류 → 이후 대조 무의미 | 낮음 |
| 7 | 사람 확인 + 예외만 라우팅 | 검산으로 못 잡는 의미 오류(엉뚱한 회사명) | 인건비(예외만) |
| 8 | 저확신 영역 확대 재판독 | 저해상도·작은 글씨 | 낮음 |
| 9 | 마스터 데이터 대조 (부품 기준표, 거래처, HS 코드) | 오타·없는 코드 | 낮음 |
| 10 | 스키마 강제 구조화 출력 | 칸 누락·타입·enum 오류 | 없음 |
실무자가 보고한 실패 유형 (KPF 서류에 해당하는 것에 ★)
- 회전·기울어진 스캔, 저해상도 팩스본 ★ (팩스 스캔)
- 손글씨 60~85% ★ (인보이스 위 "#1 BNLY 188")
- 도장이 글자를 덮음
- 병합 셀·구분선 없는 표: 수량이 단가 칸에 섞임 ★ (패킹 상세는 한 줄이 두 행에 걸침)
- 표가 다음 장으로 넘어가며 구조 소실 ★ (패킹 상세 1~3장)
- 소계 행을 표 끝으로 착각해 그 아래 줄 누락 ★ (부품별 소계 뒤에 다음 부품 줄이 이어짐)
- 1.000(유럽 천단위) vs 1,000, 소수점 혼동 ★ (단가 @1,551.0000 은 천 개당)
- 날짜 순서 DD/MM vs MM/DD
- 원가 통화와 결제 통화 혼재
엑셀 시트 추가·동시성·배포
- openpyxl:
load_workbook+copy_worksheet(서식 갖춘 견본 시트를 복제해 값만 채움)이 서식 보존에 유리. 차트 등 고급 객체는 유실될 수 있으니 저장 후 확인. - 동시성: 큐 + 전용 writer 하나가 파일 락보다 안전.
- Cloudflare: Workers는 Python을 못 돌린다. Worker(접수) → Queue → 외부 처리(Container 또는 작은 VM) → R2(파일) → D1(상태)이 정석.
출처
- Businessware Tech, AWS Textract vs Google, Azure, GPT-4o Invoice Extraction Benchmark — https://www.businesswaretech.com/blog/research-best-ai-services-for-automatic-invoice-processing
- Newtuple, OCR Benchmark PaddleOCR vs Docling vs LlamaParse vs Surya — https://www.newtuple.com/post/ocr-benchmark-paddleocr-docling-llamaparse-surya
- invoicedataextraction.com, Invoice OCR Error Handling — https://invoicedataextraction.com/blog/invoice-ocr-error-handling
- Parseur, Human-in-the-Loop Best Practices — https://parseur.com/blog/hitl-best-practices
- BSWEN, How to Detect if a PDF Needs OCR — https://docs.bswen.com/blog/2026-03-16-ocr-vs-text-pdf-python/
- dev.to, Document Parsing System with Cloudflare Queues and Workflows — https://dev.to/pablog6/building-a-robust-document-parsing-system-with-cloudflare-queues-and-workflows-1g7f
- openpyxl 문서 — https://openpyxl.readthedocs.io/en/3.1/usage.html
- SuperFastPython, Thread-Safe Write to File — https://superfastpython.com/thread-safe-write-to-file-in-python/
- arXiv, Beyond Logprobs: Multi-Signal Confidence Engine — https://arxiv.org/pdf/2606.24420