티스토리 뷰

수만 건의 계약서를 분석해 핵심 날짜, 이름, 의무 조항을 깔끔한 스프레드시트로 정리해 줄 팀을 고용했다고 상상해 보겠습니다.
그런데 그 결과물이 맞는지 확인할 방법이 전혀 없다면 어떨까요? 무작위로 샘플을 뽑아볼 수도 없고, 원본과 대조할 수도 없습니다. 그저 출력 결과를 믿고, 시스템에 입력하고, 다음 단계로 넘어갈 뿐입니다.
오늘날 대부분의 기업이 문서 추출에 AI를 도입하는 방식이 바로 이렇습니다. 이는 생각보다 심각한 문제가 되고 있죠.
이 모든 것을 바꿔줄 개념이 바로 그라운딩(Grounding) 입니다.
그라운딩이란?
그라운딩이란 출력 결과를 원본 근거에 연결하는 것입니다. 즉, 추출된 모든 정보가 원본 문서의 검증 가능한 위치로 추적될 수 있도록 보장하는 것입니다.
문서 데이터 추출에서 그라운딩은 모델이 말하는 내용을 문서가 실제로 포함하고 있는 내용에 연결하는 작업입니다. 문맥상 그럴듯해 보이는 내용이 아니라, 원본에 사실로서 검증 가능하게 존재하는 내용이어야 합니다.
머신러닝 분야에서도 마찬가지로, 그라운딩은 모델의 출력을 검증 가능한 증거와 연결하는 것을 의미합니다. 모델이 입력을 받아 예측을 생성하면, 그 예측은 원본 데이터로 추적 가능해야 합니다. 그라운딩이 없으면 출력을 감사하거나 검증할 방법이 없습니다.
이 개념은 측량학과 지도 제작에서 비롯된 'Ground Truth(실제 현장 측정값)' 라는 오래된 개념에서 유래했습니다.
그라운딩과 'Ground Truth'
Ground Truth란 특정 정보에 대한 최종적이고 인간이 검증한 정답을 뜻합니다. 문서 데이터 추출에서는 문서가 말하는 내용의 권위 있는 기록, 즉 문맥상 그럴듯한 내용이 아닌 사실로서 검증 가능하게 올바른 내용을 의미합니다. 원본을 그라운딩할 수 있다면 Ground Truth로 가는 길이 열립니다. 인용된 답변을 검증하는 확인 단계로 활용할 수 있는 것입니다.
왜 문서 추출은 유독 어려울까?
비정형 문서에서 정형 데이터를 추출하는 것은 엔터프라이즈 소프트웨어에서 오래된 난제 중 하나입니다.
이는 수십 년간 규칙 기반의 방식으로 시도되어 왔습니다. “20픽셀 이내에 날짜처럼 생긴 문자열이 있으면 추출하라"는 식이었죠. 하지만 이런 시스템은 매우 취약합니다. 레이아웃 하나가 바뀌거나, 특이한 폰트가 등장하거나, 페이지가 비스듬히 스캔되는 순간 규칙이 깨집니다.
AI는 이 상황을 바꿔 놓았습니다. 현대적인 추출 시스템은 사람처럼 문서를 읽을 수 있습니다. 맥락을 이해하고, 의미를 추론하고, 다양한 변형을 허용합니다. 정확도의 한계치가 극적으로 높아졌죠.
하지만, 한계치가 높아진다고 해서 정확한 추출이 보장이 되는 것은 아닙니다. 모델이 강력할수록, 틀렸음에도 더욱 자신 있는 태도를 보이죠. 문서는 정형 데이터와 달리 매우 복잡합니다. 모호한 표현, 모순, 수정 사항, 손으로 쓴 주석, 여러 페이지에 걸친 표, 다른 조항을 참조하는 조항들이 뒤섞여 있습니다. 아무리 신뢰도 높은 모델이더라도 잘못된 숫자를 추출할 수 있습니다. 합계가 아닌 소계, 현재 버전이 아닌 이전 버전, 잘못된 단위의 수치를 가져올 수 있는 것입니다.
그라운딩 없이는, 즉 데이터가 어디서 어떻게 가져온 것인지 볼 수 없다면, 무엇이 잘못됐는지 알 방법이 없습니다.
그라운딩과 PyMuPDF
앞서 설명한 것처럼, 그라운딩이란 모델의 출력을 원본 근거와 연결하는 것입니다. 모델이 주장하는 모든 내용이 원본 문서의 검증 가능한 위치에 고정되도록 보장하는 것이죠.
LLM 기반 문서 처리 방식의 문제는 언어 모델이 토큰 공간에서 작동한다는 점입니다. 텍스트를 읽고, 텍스트를 생성할 뿐, 공간적 구조에 대한 고유한 인식이 없습니다. 대부분의 문서 작업에서는 괜찮지만, 복잡한 응용 분야에서는 근본적인 모호성이 발생합니다. "이 내용이 페이지 어디에서 왔는가?"를 파악할 수 없다는 점입니다.
PyMuPDF는 ‘search_for’ 방식을 통해 이 질문에 답하고, 모델의 출력과 근거를 연결하는 다리가 됩니다.
Page.search_for(needle, quads=False, flags=..., clip=None)는 PDF 페이지 내에서 찾고자 하는 텍스트(needle)가 어디에 있는지 검색해 줍니다. 결과물로는 텍스트의 정확한 물리적 위치를 감싸는 사각형 영역(Bounding Rectangle, Bounding Box)들의 리스트를 돌려받게 됩니다. 만약 글자가 기울어져 있거나 특수한 형태라 사각형만으로 표현하기 어렵다면, quads=True 옵션을 주어 네 개의 꼭짓점 좌표(Quadrilateral) 형태로 더 정확하게 받아올 수도 있습니다.
좌표는 PDF의 기본 위치 기반 공간 내에 있으며, 페이지에 고정되어 있고, 정확합니다. OCR 추정이 아닌 PDF 내부의 문자 위치 정보를 직접 가져온 값입니다.
import pymupdf
doc = pymupdf.open("contract.pdf")
page = doc[0]
# Returns a list of Rect objects, one per match
rects = page.search_for("Governing Law")
왜 문서 그라운딩의 기반이 ‘공간 좌표’일까?
PDF의 좌표 시스템은 결정론적입니다. 글자 위치를 추정하는 OCR이나, 해상도에 따라 크기가 조절되고 줄바꿈이 바뀌는 웹 렌더링 방식과 달리, PDF에 인코딩된 텍스트 위치는 '원본 데이터 그 자체(Source of Record)'입니다. 즉, 문서를 만든 저작 도구(Authoring Tool)가 지정한 위치에 텍스트가 정확히 고정되어 있습니다.
PyMuPDF의 search_for가 Rect(72.0, 144.3, 210.5, 158.7)을 반환한다면, 이것은 근사치가 아닙니다. 해당 문자열의 정확한 위치값입니다.
이는 다음과 같은 몇 가지 이유로 LLM(대형 언어 모델) 워크플로우에서 굉장히 중요한 의미를 가집니다.
1. 인용(Citation)과 추적 가능성(Traceability)
LLM은 문서에서 특정 조항, 수치, 숫자를 잘 추출해 냅니다. 하지만 "4페이지, 3번째 문단" 같은 표현은 모호합니다. 문단을 어떻게 세느냐, 페이지가 어떻게 렌더링되느냐에 따라 달라지기 때문입니다. 반면 바운딩 박스(Bounding Box)는 명확합니다. 이 rect 좌표를 다시 PyMuPDF에 넘겨서 pixmap으로 해당 영역만 크롭(Crop)한 뒤, 사용자에게 원본 문서의 정확한 하이라이트 영역을 보여줄 수 있습니다.
이는 "모델이 4페이지에 위약금 조항이 있다고 답변했습니다"라고 텍스트로 보여주는 것과, 사용자에게 실제 하이라이트된 문구를 눈으로 보여주는 것의 차이입니다.
2. RAG 청크 검증 (RAG Chunk Validation)
RAG(검색 증강 생성) 파이프라인에서는 텍스트 청크를 임베딩하고 검색(Retrieve)합니다. 이때 자주 발생하는 실패 패턴(Failure Mode) 중 하나는, 검색된 텍스트 자체는 맞는데 모델이 이를 잘못 매칭하는 경우입니다. 예를 들어 5조에 있는 문구를 가져와놓고 2조의 맥락으로 해석해 버리는 식이죠. 만약 인덱싱(Index) 시점에 각 청크와 함께 바운딩 박스를 함께 저장해 둔다면, 모델의 아웃풋이 원래 있어야 할 페이지 영역과 일치하는지 프로그램적으로 검증할 수 있습니다. 즉, rect 좌표가 공간적 정확성을 보장하는 체크섬(Checksum) 역할을 해주는 것입니다.
3. 공간적 제약 조건을 활용한 구조화된 추출 (Structured Extraction)
재무제표나 서식, 테이블에서 값을 추출하는 상황을 가정해 봅시다. "Total(합계)"이라는 단어가 한 페이지에 수십 번 등장할 수 있습니다. 이때 search_for를 사용하면 일치하는 모든 단어를 나열한 뒤 공간 필터링 로직을 적용할 수 있습니다.
허술한 정규식이나 프롬프트 엔지니어링에 의존하는 대신, "이 특정 테이블의 맨 아래 행에 있는 'Total'은 어떤 것을 의미하는가"를 좌표 연산으로 정확히 찾아낼 수 있는 거죠. page.find_tables()로 알아낸 테이블의 바운딩 박스와 검색된 rect 좌표의 교집합(Intersect)을 구하면 끝납니다.
4. 피드백 루프 역할을 하는 주석 기능 (Annotation)
전형적인 피드백 패턴을 구성할 수 있습니다. 문자열을 검색하고, 반환된 quads 좌표를 page.add_highlight_annot()에 그대로 전달하는 방식입니다. 이는 인간 참여형(Human-in-the-loop) LLM 검토 워크플로우에서 강력한 힘을 발휘합니다.
LLM이 관심 조항을 식별하면, search_for가 그 위치를 기하학적으로 찾아내고, 시스템이 PDF에 하이라이트를 칩니다. 그러면 검토자는 정확한 출처(Provenance)를 눈으로 확인하게 되죠. 이 하이라이트는 실제 데이터(Ground-truth)에 단단히 고정되어 있기 때문에, 일반 텍스트 참조 방식처럼 위치가 어긋나거나 환각(Hallucination)이 발생할 여지가 없습니다.
# LLM identifies "indemnification" as a key clause
# We locate and highlight all occurrences with exact geometry
quads = page.search_for("indemnification", quads=True)
page.add_highlight_annot(quads)
5. 크로스 모달 그라운딩
페이지를 픽스맵으로 렌더링하고 비전 지원 LLM에 전달하면, 모델은 픽셀을 인식합니다. search_for는 픽셀 공간에서 시맨틱 텍스트로 다시 변환할 수 있으며, 반대로도 가능합니다.
비전 모델이 관심 영역을 식별하면, 잘라낸 사각형 내에서 (clip 파라미터 사용) 정확한 텍스트를 복구하기 위해 검색할 수 있습니다. 모델이 텍스트를 생성하면 물리적 위치를 확인할 수 있습니다. 이 양방향 그라운딩이 텍스트+비전 하이브리드 문서 파이프라의 신뢰성을 높여줍니다.
quads=True 옵션의 중요성
여기서 quads=True 옵션을 깊이 있게 짚고 넘어갈 필요가 있습니다.
Rect(사각형)는 네 개의 숫자 (x0, y0, x1, y1)로 구성됩니다. 반면 Quad(사각 패치)는 좌상단, 우상단, 우하단, 좌하단이라는 네 개의 독립된 꼭짓점을 가집니다. 가로로 곧게 뻗은 평범한 텍스트라면 두 옵션의 결과가 같습니다.
하지만 PDF에는 회전된 제목, 워터마크, 임의의 각도로 기울어진 테이블 헤더, 축 정렬이 맞지 않는 다단 레이아웃이 수시로 등장합니다. 이런 경우 바운딩 박스를 쓰면 텍스트를 과도하게 넓게 감싸게 되어, 일치하지 않는 주변의 공백까지 전부 포함해 버리는 문제가 생깁니다. 반면 쿼드(Quad)는 훨씬 타이트하고 기하학적으로 정확하게 텍스트 영역만 잡아냅니다.
LLM 콘텍스트를 구성할 때 이게 왜 중요할까요? 비전 모델에게 줄 페이지 이미지를 사각형(Rect) 기준으로 크롭하면, 영역이 과도하게 잡혀 주변 텍스트까지 크롭 영역에 침범(Leak)하게 되기 때문입니다. 쿼드를 사용한다면, 매칭된 텍스트 주변의 시각적 콘텍스트 창을 가장 타이트하게 유지할 수 있습니다.
실무 패턴: '좌표 기반 앵커'를 활용한 LLM 콘텍스트 구축
지금까지 살펴본 개념들을 종합해서, 실제로 서비스에 적용할 수 있는 파이프라인 코드를 구현해 보겠습니다. 전체적인 흐름은 간단합니다. 인덱싱(데이터 저장) 단계에서 텍스트의 좌표를 함께 데이터베이스에 구워두고, 쿼리(사용자 질문) 단계에서 LLM이 내뱉은 답변의 진위 여부를 이 좌표를 통해 검증하는 구조입니다.
import pymupdf
import json
doc = pymupdf.open("annual_report.pdf")
# Build a corpus with spatial metadata at index time
chunks = []
for page_num, page in enumerate(doc):
blocks = page.get_text("blocks") # (x0,y0,x1,y1, text, ...)
for block in blocks:
text = block[4].strip()
if not text:
continue
rect = pymupdf.Rect(block[:4])
chunks.append({
"page": page_num,
"text": text,
"bbox": list(rect), # ground truth spatial anchor
})
# At query time: LLM produces an answer referencing "net revenue declined"
# Verify by locating the claim spatially
page = doc[3]
hits = page.search_for("net revenue declined")
if hits:
# We can confirm the LLM's claim maps to a real location
# and highlight it for the user
page.add_highlight_annot(hits)
인덱싱 시점에 저장해 둔 바운딩 박스는 추후 강력한 '감사 기록(Auditable Record)'이 됩니다.
단순히 "LLM이 이러이러한 답변을 했습니다"라고 텍스트만 보여주는 수준을 넘어, "이 주장의 근거는 원본 문서의 정확히 '이 좌표'에 단단히 묶여 있습니다"라고 시각적으로 증명할 수 있게 되기 때문이죠. 말 그대로 엔지니어링 관점에서 구현할 수 있는 가장 확실하고 실무적인 의미의 '그라운딩(Grounding, 근거 확립)' 패턴이라고 볼 수 있습니다.
결론
Page.search_for는 겉보기엔 단순한 텍스트 검색 유틸리티처럼 보여서 과소평가되기 쉽습니다. 하지만 이 함수의 진짜 강력함은 의미론적 영역(Semantic Space)의 데이터를 기하학적 영역(Geometric Space)으로 신뢰할 수 있게 변환해 준다는 점에 있습니다.
환각(Hallucination)과 출처 오인(Misattribution) 리스크가 상존하는 LLM 문서 워크플로우에서, 임의의 텍스트 조각을 정확한 페이지 좌표에 모호함 없이 고정할 수 있는 방법론을 갖추는 것은 '검증 가능하고 감사 가능한 파이프라인'을 만드는 기본 토대입니다.
그라운딩은 LLM이 내뱉는 아웃풋 그 자체가 아닙니다. PDF의 물리적 기하 구조와의 강력한 연결 고리 그 자체이며, search_for는 바로 그 고리에 접근하는 열쇠가 됩니다.
[참고 자료]
-
PyMuPDF Pro: 전 세계 개발자가 선택한 Python 기반 데이터 추출 라이브러리
'PyMuPDF Pro' 카테고리의 다른 글
| 50% 더 빨라진 OCR과 AI 답변의 '근거'를 찾는 법 (0) | 2026.04.09 |
|---|---|
| PyMuPDF4LLM의 하이브리드 OCR (0) | 2026.04.06 |
| C 엔진(C engine)의 유산을 잇고, 파이썬(Python)다운 사용성까지 챙긴 PyMuPDF Pro (0) | 2026.03.26 |
| 왜 데이터 청킹(Data Chunking)이 LLM 처리에서 필수적일까 (0) | 2026.03.03 |
| PyMuPDF Layout 튜토리얼 — 시작하기 (0) | 2026.02.12 |
- Total
- Today
- Yesterday
- pdf추출
- 피터팬
- 고장예측
- pdf프로
- PDF변환
- ocr
- 스마트공장
- 파이썬라이브러리
- PDF-Pro
- pdf프로그램
- 모터센스
- paperless
- 페이퍼리스
- LLM
- 이벤트
- 전자문서
- PDF편집
- 이파피루스
- epapyrus
- djvu
- PDFpro
- PyMuPDFPro
- 전자서식
- 예지보전
- 문서ai
- 인공지능
- Ai
- 피터펜
- pdf뷰어
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | |||||
| 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 |
