대용량 PDF에서 표 데이터를 추출하고 수치를 검증하는 방법
대용량 PDF에서 표를 추출할 때는 Files API와 nullable 스키마를 적용한 Gemini API 워크플로를 구성하는 것이 적절합니다. 모델이 생성한 페이지 번호와 인용 스니펫은 원문 위치를 찾기 위한 미검증 단서일 뿐, 사실을 입증하거나 환각을 방지하는 수단이 아닙니다. 따라서 추출된 값은 시각적으로 렌더링된 원본 PDF와 직접 대조하여 일치 여부를 확인해야 합니다.
목차

수십 페이지에 달하는 설비 점검 보고서나 재무제표처럼 복잡한 PDF 문서를 다룰 때, 균일하고 깔끔한 텍스트만으로 이루어진 문서는 거의 없습니다. 실제 기업 실무 환경에서는 디지털 문서와 스캔 페이지가 뒤섞여 있고, 표에 명확한 격자선(테두리)이 없거나, 중요한 핵심 수치가 빽빽한 각주 속에 숨겨져 있는 경우가 비일비재합니다.
이러한 파일을 멀티모달 모델에 그대로 전달한 뒤, 출력 결과를 곧바로 운영 데이터베이스에 기록하는 것은 매우 위험합니다. 파운데이션 모델은 여전히 확률 기반 시스템으로 동작하므로, 신뢰할 수 있는 데이터 추출 파이프라인은 모델에 대한 맹목적인 믿음만으로 구축할 수 없습니다. 대신 정밀한 스키마 설계, 맥락을 파악할 수 있는 감사용 힌트 수집, 그리고 원본 시각 레이어와의 철저한 사후 대조 검증을 중심으로 파이프라인을 체계화해야 합니다.
1. 데이터 스키마: 필드 고정 및 null 허용
Gemini Structured Outputs 메커니즘을 활용하면 모델의 응답이 사전에 정의된 스키마를 엄격하게 따르도록 보장하여 구문적으로 유효한 JSON을 생성할 수 있습니다. 특정 속성을 숫자형으로 선언하면 값에 불필요한 대화형 텍스트가 섞여 들어가지 않습니다.
하지만 구문적 스키마 준수는 구조적 오류만을 방지할 뿐, 의미론적 진실성까지 보장하지는 못합니다.
- 테두리가 없는 복잡하고 조밀한 표에서는 모델이 인접한 행을 혼동하거나 열을 뒤바꿔 인식할 수 있습니다.
- 화질이 저하된 스캔 문서에서는 숫자
8을3으로 잘못 판독하기 쉽고, 소수점이 누락되는 일도 빈번합니다. - 수치가 누락되었거나 얼룩 등으로 가려져 있을 때, 값 생략을 명시적으로 허용하지 않으면 모델이 그럴듯한 숫자를 지어내려고 시도할 수 있습니다.
이러한 환각(hallucination) 위험을 완화하려면 스키마에서 필드를 null 허용(Optional 또는 null)으로 선언해야 합니다. 이와 함께 높은 신뢰도로 숫자를 판독할 수 없을 때는 반드시 null을 반환하도록 프롬프트에 명시적으로 지시해야 합니다. null 반환을 허용하면 모델이 임의로 추측해야 한다는 압박을 크게 줄일 수 있지만, 이것만으로 환각을 완전히 차단할 수 있는 절대적인 보증이 되는 것은 아닙니다.
2. 파일 수집: Files API를 선택해야 하는 시점
공식 Gemini 문서 처리 가이드에 따르면, PDF 파일당 운영 제한은 최대 50 MB 또는 최대 1,000페이지로 설정되어 있습니다(파일 크기와 페이지 수 제한이 동시에 적용되며, 두 최댓값을 모두 동시에 달성할 수 있다는 보장은 없습니다. 둘 중 어느 한도에든 먼저 도달하면 처리가 중단됩니다).
문서 크기와 작업 패턴에 따라 최적의 전송 방식을 선택해야 합니다.
- **인라인 데이터 전달(Inline data passing)**은 용량이 작은 문서나 일회성 추출 호출에 가장 적합합니다.
- **Files API(
client.files.upload)**는 대용량 파일이나 동일한 문서를 대상으로 연속적인 작업을 수행하는 다중 턴 워크플로(예: 초기 섹션 분류 후 특정 표에 대한 집중 추출 진행)에 맞춰 설계되었습니다. Files API를 사용하면 매 호출마다 전체 문서 페이로드를 반복해서 다시 업로드할 필요가 없습니다.
3. 데이터 질의: 스키마, 인용 힌트 및 가상 응답
추출된 데이터의 감사 가능성을 확보하려면, 목표 수치와 함께 보조 메타데이터인 대략적인 페이지 번호(page_number)와 짧은 원문 인용 발췌문(evidence_quote)을 반환하도록 모델에 프롬프트로 요청해야 합니다.
핵심적인 구분점:
page_number와evidence_quote는 사실을 입증하는 확정 증거가 아닙니다. 이는 어디까지나 휴리스틱한 검색 힌트일 뿐입니다. 모델이 이 필드들을 직접 생성하기 때문에, 인용 발췌문에 OCR 오류가 섞이거나 인접한 줄이 병합될 수 있으며, 시각적 레이어에 표시된 페이지 번호가 PDF 컨테이너의 실제 물리적 용지 인덱스와 다를 수 있습니다.
가상의 문제 설정
가상의 문제 설정을 살펴보겠습니다(실제 PDF 파일이 제공되거나 업로드 및 분석된 적이 없으며, 실제 API 질의가 실행되지도 않았습니다). 가상의 펌프 설비 점검 보고서에서 요약 수치를 추출하는 상황을 모델링합니다. 이 예시에서는 2개 행으로 구성된 가상의 표를 검토합니다.
| 식별자 | 압력 (MPa) | 진동 (mm/s) | 상태 | 비고 |
|---|---|---|---|---|
| Н-101-А | 1.45 | 2.1 | В норме | 정기 점검 |
| Н-102-В | (판독 불가) | 7.8 | Attention (Внимание) | 유격 증가 |
다음은 공식 SDK 호출 문법과 함께 Pydantic으로 정의한 스키마 예시입니다.
from google import genai
from pydantic import BaseModel, Field
from typing import List, Optional
class PumpRecord(BaseModel):
unit_id: str = Field(
description="Идентификатор агрегата точно как в таблице"
)
inlet_pressure_mpa: Optional[float] = Field(
default=None,
description="Давление в МПа. Если значение неразборчиво или отсутствует — null"
)
vibration_mms: Optional[float] = Field(
default=None,
description="Уровень вибрации в мм/с. При отсутствии данных — null"
)
status: str = Field(
description="Статус узла (например, 'В норме', 'Внимание')"
)
page_number: Optional[int] = Field(
default=None,
description="Оценочный номер страницы документа (подсказка для аудитора, не подтверждена)"
)
evidence_quote: Optional[str] = Field(
default=None,
description="Короткий фрагмент строки (до 10 слов), откуда взяты числа (подсказка, не подтверждена)"
)
class InspectionPayload(BaseModel):
records: List[PumpRecord]
client = genai.Client()
uploaded_file = client.files.upload(file="hypothetical_inspection.pdf")
response = client.interactions.create(
model="gemini-3.8-flash",
input=[
{
"type": "document",
"uri": uploaded_file.uri,
"mime_type": uploaded_file.mime_type,
},
{
"type": "text",
"text": (
"Извлеки показатели агрегатов в соответствии со схемой. "
"Если число неразборчиво или отсутствует, возвращай null. "
"Для каждой записи заполни номер страницы и короткую цитату-подтверждение."
),
},
],
response_format={
"type": "text",
"mime_type": "application/json",
"schema": InspectionPayload.model_json_schema(),
},
)
payload = InspectionPayload.model_validate_json(response.output_text)
모델의 가상 JSON 응답 예시
다음 JSON은 이 요청에 대한 모델의 가상 응답을 보여줍니다. 거듭 강조하지만, 이 출력은 가상의 구조적 예시일 뿐이며 실제 API 실행 결과나 실제 물리적 측정값이 아닙니다.
{
"records": [
{
"unit_id": "Н-101-А",
"inlet_pressure_mpa": 1.45,
"vibration_mms": 2.1,
"status": "В норме",
"page_number": 12,
"evidence_quote": "Н-101-А 1.45 2.1 В норме"
},
{
"unit_id": "Н-102-В",
"inlet_pressure_mpa": null,
"vibration_mms": 7.8,
"status": "Внимание",
"page_number": 12,
"evidence_quote": "Н-102-В [пятно] 7.8 Внимание"
}
]
}
이 가상 응답에서 두 개의 page_number: 12 항목과 두 개의 evidence_quote 값은 모두 검증되지 않은 힌트(unverified hints) 상태를 갖습니다. 모델이 두 번째 설비의 판독 불가능한 압력 수치에 대해 올바르게 null을 반환했더라도, 추출된 어떠한 속성도 사전 검증된 사실로 간주되지 않습니다.
4. 원본 시각 레이어 대조 및 내보내기 규칙
추출된 레코드는 검증 과정 없이 다운스트림 데이터베이스에 바로 저장할 수 없습니다. 각 필드를 원본 PDF 페이지의 시각적 렌더링과 대조하는 엔드투엔드 감사가 반드시 필요합니다.
예시 관련 중요 안내 사항: 2개 행으로 구성된 표, 12페이지, 물리적 14번째 용지 오프셋, 압축기 부서, 시각적 검증 워크플로는 순수하게 프로세스를 설명하기 위한 가상의 예시입니다. 실제 PDF 문서가 제공되거나 검토된 적이 없으며, 아래에 설명된 단계는 시각적 렌더링을 통해 명시된 수치가 확인될 경우 검토자가 실무에서 무엇을 확인하고 어떤 조건부 결정을 내리는지를 나타냅니다.
레코드별 단계적 검증: 검토자의 확인 사항
-
설비
Н-101-А:- 페이지 및 위치 확인: 모델이
page_number: 12힌트를 반환했습니다. 검토자는 문서 12페이지(앞표지나 목차 등으로 인해 물리적 오프셋이 발생한 경우 14번째 용지)의 시각적 렌더링을 열고 대상 압축기 부서 표를 찾습니다. - 식별자: 첫 번째 열에서 식별자
Н-101-А가 정확히 일치하는지 확인합니다. - 압력 및 단위: 인입 압력 열에서 수치
1.45가 명확히 판독 가능한지, 공학 단위가 스키마 요구사항(МПа/MPa)과 일치하는지 확인합니다. - 진동 및 단위: 진동 열에서
2.1값을 확인하고 단위 표기(мм/с/mm/s)가 맞는지 검증합니다. - 상태: 가동 상태란에서
В норме(Normal) 상태가 표시되어 있는지 확인합니다. - 조건부 결정: 렌더링된 페이지에서 모든 필드, 수치, 물리적 단위가 검증되면 해당 행은 내보내기 승인(Export / Accepted) 상태가 됩니다.
- 페이지 및 위치 확인: 모델이
-
설비
Н-102-В:- 페이지 및 위치 확인: 동일한 가상 표에서 검토자는 두 번째 행으로 이동합니다.
- 식별자: 식별자
Н-102-В의 존재를 확인합니다. - 진동 및 상태: 진동 수치
7.8과 상태Внимание(Warning / Attention)를 시각적 레이어와 대조합니다. - 압력: 모델이
null을 반환했습니다. 검토자는 페이지 렌더링의 해당 셀을 조사합니다. 수치가 있어야 할 위치에 어둡고 번진 얼룩(스캔 결함)이 확인된다면, 이는null을 반환한 모델의 판단이 타당함을 입증하지만 핵심적인 물리적 측정값은 여전히 누락된 상태입니다. - 조건부 결정: 시각적 확인을 통해 필수적인 압력 수치가 누락된 것으로 확인되었으므로, 해당 행은 자동 내보내기가 차단되고 수동 검토(Hold for manual review) 상태로 분류되어 재스캔을 요청하거나 백업 유지보수 로그와 교차 대조해야 합니다.
검증 후 확인 결과 (Checked Output)
요약 표는 검토자가 구체적으로 무엇을 검사하며, 시각적 확인 시 유효성 검증 파이프라인이 어떤 조건부 라우팅 결정을 내리는지 상세히 보여줍니다.
| 설비 | 압력 (MPa) | 진동 (mm/s) | 상태 | 검토자 확인 사항 (가상 검증) | 파이프라인 조건부 결정 (렌더링 확인 시) |
|---|---|---|---|---|---|
| Н-101-А | 1.45 | 2.1 | В норме (Normal) | 렌더링과 대조하여 식별자 일치 여부, 수치 및 단위(MPa, mm/s) 확인 | 내보내기 승인 (수집 준비 완료)—모든 필드의 완전한 시각적 확인을 전제로 함 |
| Н-102-В | null (생략됨) | 7.8 | Внимание (Warning) | 압력 셀의 스캔 결함(번진 얼룩) 확인 및 진동 수치 교차 대조 | 수동 검토 대기 (운영자 분류)—핵심 측정값 누락 확인에 따름 |
아키텍처 유효성 검증 패턴
견고한 문서 수집 파이프라인은 처리된 레코드를 두 개의 분리된 스트림으로 라우팅합니다.
- 그린 통로 (Green Corridor / Verified Export): 모든 필수 필드가 원본 페이지 렌더링과 시각적으로 일치하고 모든 물리적 단위가 스키마 표준에 맞게 정규화된 행에만 독점적으로 허용됩니다.
- 검토 대기열 (Review Queue / Quarantine): 중요 필드에
null이 포함되어 있거나 측정 단위가 충돌하는 경우, 또는 모호한 인용문이 있는 모든 레코드는 운영자의 수동 검토를 위해 격리됩니다.