JSON으로 추출한다고 해서 추출이 정확하다는 의미는 아닙니다. 금액, 단위, 출처에 대한 현장 계약을 수립합니다.

추출 결과를 JSON으로 변환할 수 있다고 해서 추출이 올바른 것은 아닙니다. 이 글에서는 소수 및 필드 계약을 사용하여 금액, 단위, 페이지 번호, 원문 및 좌표를 확인하고 정상 입력과 비정상 입력에 대한 결정론적 테스트를 첨부하여 오류가 다운스트림에 들어가기 전에 중지할 수 있습니다.

추출 결과를 JSON으로 파싱할 수 있다고 해서 데이터베이스에 바로 저장할 수 있는 것은 아니다. 이 문서는 청구서 및 견적 데이터를 다운스트림 시스템으로 보내야 하는 개발자를 위해 작성되었습니다. 우리는 더 엄격한 경계를 보여주기 위해 합성 인용문 조각을 사용합니다. 값 외에도 각 필드에는 단위, 소스 페이지, 소스 텍스트 및 좌표가 있습니다. 파서나 모델은 후보 제안만 담당하고, 결정론적 검증자는 이들이 데이터베이스에 포함될 수 있는지 여부를 결정합니다.

이 글을 읽고 나면 필드 계약 세트의 아이디어를 파악하고 누락된 필드와 불확실한 필드를 기본적으로 0으로 채우는 대신 명시적으로 유지하는 방법을 알 수 있습니다.

추출 결과를 데이터베이스에 직접 저장할 수 있나요? 한 문장 결론

데이터베이스에 입력될 수 있는지 여부는 JSON을 구문 분석할 수 있는지 여부가 아니라 현장 계약 및 증거 확인 통과 여부에 따라 다릅니다. 확인에 실패한 필드는 보류 상태로 유지되어야 하며 기본적으로 0으로 채울 수 없습니다.

당신의 상황권장 사례이유
파서가 유효한 JSON을 반환했습니다.현장 계약 검증을 계속 수행하고 이를 데이터베이스에 직접 저장하지 마십시오.JSON은 프로그램에서 읽을 수 있음을 의미합니다.
금액에 통화나 단위가 없습니다.화폐를 가정하지 말고 누락된 상태를 유지하십시오.비밀리에 위안화를 사용하지 마세요
금액은 1.200,50와 같은 지역 형식으로 표시됩니다.허용되는 각 형식에 대해 별도로 계약을 정의합니다.지역 규칙은 인과관계 없이 획일적으로 처리할 수 없습니다.
확인하려면 문서로 돌아가야 합니다.페이지 번호, 원본 텍스트 및 좌표를 저장하고 페이지 번호 범위를 확인하세요.리뷰어의 포지셔닝 기반 촉진
여러 통화 또는 환율 관련원래 통화 금액, 통화 유형 및 환율 소스는 유지되며 암시적 변환은 수행되지 않습니다.환율 없이 금액을 변환할 수 없습니다.

증거와 함께 값을 전달합니다.

송장이나 견적서를 파서에게 건네주면, 받은 JSON은 프로그램이 그것을 읽을 수 있다는 것만 알려줍니다. 1200 단가인가요, 총액인가요? 통화는 어디에 있나요? 1.200,50는 어떤 지역 규칙에 따라 이해되어야 합니까? 반환된 페이지 번호가 실제로 존재합니까? 이는 JSON 구문으로 답변할 수 있는 질문이 아닙니다.

필드강제왜 필요한가?
양10진수 문자열, 음수가 아닌 소수점 이하 두 자리로 제한된 예의도하지 않은 이진 부동 소수점 근사법 도입 방지
currency이 시나리오는 USD에 한정됩니다통화가 없을 때 비밀리에 위안화를 사용하지 마십시오
source.page1부터 시작하며, 페이지 수는 제한을 초과할 수 없습니다이로 인해 사용자가 문서의 원래 위치로 더 쉽게 돌아갈 수 있습니다
source.quote호출자가 제공하는 페이지 내 조각이어야 합니다근거가 있는 사람에게 검증이 제공될 수 있습니다
source.bbox페이지 범위 내에 위치한 제한 값빈 프레임이나 잘못된 좌표가 증거로 넘어가지 않도록

여기서 좌표 단위는 PDF 점이며, 원점은 현재 합의된 페이지의 왼쪽 상단에 위치합니다. 상류 회전, 크롭, 좌표계는 먼저 통합되어야 하며; 서로 다른 파서의 좌표를 직접 섞어서는 안 됩니다. 이 예시는 관례를 보여주는 것일 뿐, 상자에 텍스트가 포함되어 있음을 증명하지는 않습니다.

완전한 검증 및 거부 테스트

Python 표준 라이브러리만으로도 실행할 수 있습니다. Decimal는 문자열로 구성되어야 하며, 먼저 float로 변환한 후 전달하지 마세요. 정확한 소수점 처리는 Python 십진법 문서를 참조하세요.

아래 코드는 실제 테스트 출력이며, 인식 정확도 보고서가 아닙니다. 샘플 입력은 저희가 직접 작성했으며, 실제 영수증에 대해 OCR이나 모델 평가는 수행하지 않았습니다.

from copy import deepcopy
from decimal import Decimal
import math
import re

pages = [{"width": 595, "height": 842,
          "text": "Total: USD 1,200.50\nDelivery within 10 days"}]

def validate(candidate, pages):
    if set(candidate) != {"amount", "currency", "source"}:
        raise ValueError("unexpected or missing field")
    raw = candidate["amount"]
    if not isinstance(raw, str) or not re.fullmatch(r"(?:0|[1-9][0-9]*)\.[0-9]{2}", raw):
        raise ValueError("amount must be a two-decimal string")
    if candidate["currency"] != "USD":
        raise ValueError("unsupported currency for this contract")
    src = candidate["source"]
    if not isinstance(src, dict) or set(src) != {"page", "quote", "bbox"}:
        raise ValueError("source contract mismatch")
    number = src["page"]
    if type(number) is not int or not 1 <= number <= len(pages):
        raise ValueError("page outside document")
    page = pages[number - 1]
    quote = src["quote"]
    if not isinstance(quote, str) or not quote or quote not in page["text"]:
        raise ValueError("source quote not found")
    match = re.fullmatch(r"Total: USD ([0-9]{1,3}(?:,[0-9]{3})*\.[0-9]{2})", quote)
    if not match or Decimal(match[1].replace(",", "")) != Decimal(raw):
        raise ValueError("amount does not agree with source")
    box = src["bbox"]
    if (not isinstance(box, list) or len(box) != 4
            or any(type(v) not in (int, float) or not math.isfinite(v) for v in box)):
        raise ValueError("invalid box values")
    x0, y0, x1, y1 = box
    if not (0 <= x0 < x1 <= page["width"] and 0 <= y0 < y1 <= page["height"]):
        raise ValueError("box outside page or empty")
    return {"amount": str(Decimal(raw)), "currency": "USD", "source": deepcopy(src)}

good = {"amount": "1200.50", "currency": "USD", "source": {
    "page": 1, "quote": "Total: USD 1,200.50", "bbox": [40, 70, 260, 90]}}
assert validate(good, pages)["amount"] == "1200.50"
print("PASS valid source-linked amount: USD 1200.50")
bad_cases = []
wrong = deepcopy(good); wrong["amount"] = "12000.50"; bad_cases.append(wrong)
wrong = deepcopy(good); wrong["source"]["page"] = 2; bad_cases.append(wrong)
wrong = deepcopy(good); wrong["source"]["bbox"][2] = float("nan"); bad_cases.append(wrong)
wrong = deepcopy(good); wrong["source"]["bbox"][0] = 300; bad_cases.append(wrong)
for index, wrong in enumerate(bad_cases, 1):
    try:
        validate(wrong, pages)
        raise AssertionError("bad candidate accepted")
    except ValueError as error:
        print(f"PASS rejection {index}: {error}")

엄격한 정칙성은 시나리오가 엄격하기 때문입니다

이 일반 형태는 예시에서 영어 양 형식만 허용합니다. 다음 상황을 거부합니다:

  • 유클리드 십진법
  • 음수
  • 괄호 안에 환불 표기됨
  • 기타 통화
  • 천 자리에 네 자리 숫자가 없다

실제 시스템에서는 문서 유형과 지역에 따라 독립 계약을 선택해야 하며, 모든 쉼표, 점, 공백을 마음대로 삭제해서는 안 됩니다.

입력 범위를 더 확장하기 전에, 허용된 각 형식에 대해 양성 샘플과 리젝트 샘플을 준비하세요. 만약 지폐가 다중 통화라면, 필드에 원래 통화 금액, 통화, 환율 출처를 별도로 기록해야 합니다; 환율이 없으면 비밀리에 금액을 환전할 수 없습니다.

📊 핵심 비교

검증 수준답할 수 있는 질문들아직도 답이 없는 질문들
JSON 분석프로그램이 내용을 읽을 수 있나요?금액이 단가인가요, 아니면 총 금액인가요? 통화는 어디에 있나요?
현장 계약금액 형식, 통화 금액, 페이지 번호 범위, 좌표가 합법인지 확인하세요좌표 박스가 텍스트를 정확히 포함하는지 확인하세요
출처 검증현재 페이지의 텍스트에 인용이 존재하는지, 그리고 그 금액이 제한된 형식과 일치하는지 여부은행이 사업의 실제 최종 총액을 대표하는지 여부

"출처가 있다고 해서 "증명된" 것은 아닙니다

프로그램은 현재 페이지 텍스트에 인용이 존재하는지, 그리고 그 금액이 이 제한된 형식과 같은지 확인합니다. 좌표 박스가 원본 텍스트를 정확히 포함하는지 확인하지도 않으며, 해당 라인이 사업의 실제 총 금액인지 판단하지도 않습니다. 상류 텍스트 계층이 잘못된 OCR에서 비롯될 수 있으며, 여러 개의 합계가 같은 페이지에 나타날 수 있습니다.

따라서 현장 계약은 단지 첫 번째 게이트일 뿐이다. 따라서 위치 확인 일관성 점검, 동일 페이지 후보자 명확 해소, 소합 세금 잔액 관계 점검, 수동 현장 점검이 요구됩니다. 중요한 비즈니스 데이터는 이 게이트를 통과하기 전에 "확인 중" 상태로 저장되어야 하며, "확인됨" 상태가 아닌,

문서 워크플로우에 구현하는 방법

원본 파일 요약을 유지하고, 추출된 각 레코드에 대한 버전을 처리하며, 거부 사유를 저장합니다. 번역 과정도 동일한 현장 감사를 사용해야 합니다: 본문은 다른 언어로 대체할 수 있지만, 중요한 금액과 단위 간의 대응 관계는 분실되어서는 안 됩니다. 관련 문서 서비스에 대해서는 옴니닥 제품 항목를 통해 배울 수 있습니다; 이 글의 계약과 코드는 제품 API와 독립적이며 공개되지 않은 인터페이스에 의존하지 않습니다.

🎯 어떤 방법이 당신에게 맞나요?

다통화 지폐를 다뤄야 한다면

원래의 통화 금액과 통화 유형을 유지하고, 환율의 출처를 기록하세요. 환율이 없을 때는 환전이 이루어지지 않습니다.

입력 형식의 범위를 확장하고 싶다면

먼저, 각 형식에 대해 긍정적 예시와 부정 예시를 작성한 후, 규칙을 완화하되 규칙을 완화하세요.

데이터가 재무 또는 비즈니스 결정에 사용될 경우

필드를 대기 중인 검증으로 표시하고, 위치 일치, 동일 페이지 중의성 해소, 수동 임시 점검 후에 확인하세요.

문서 번역을 하고 싶다면

본문은 언어를 변경할 수 있지만, 양과 단위 간의 대응은 감사되어야 하며, 동일한 필드를 사용해 유지해야 합니다.

❓ 자주 묻는 질문 (FAQ)

1. JSON이 파싱을 할 수 있다면, 직접 저장할 수 있다는 뜻인가요?

아니. 구문 분석은 프로그램이 내용을 읽을 수 있다는 것만을 의미하며; 현장 계약과 증거 검증도 필요합니다.

2. 프로그램이 견적을 확인한다면, 그 금액이 정확해야 하나요?

아니. 현재 페이지 텍스트에 견적이 존재하는지, 그리고 금액이 제한된 형식과 같은지만 확인하며, 좌표 상자나 사업체 내 총 금액의 의미는 확인하지 않습니다.

3. 왜 누락된 금액을 0으로 처리할 수 없는 걸까요?

누락 금액, 검증 실패, 진짜 0은 서로 다른 상태이며, 이 두 가지를 섞으면 하류를 구분하기 어렵습니다.

4. 다중 통화 지폐는 어떻게 처리해야 할까요?

각각 원래 통화 금액과 통화를 유지하세요. 환율이 없을 때는 환전하지 말고, 환율의 원천을 유지하세요.

📝 요약

추출 결과를 사용 가능하게 하려면 JSON 형식 자체가 아니라 계약서, 증거, 수락에 의존합니다. 각 필드는 문서 내 특정 페이지로 추적 가능해야 하며, 확인되지 않은 데이터는 검증 대기 상태로 남아 있어야 합니다.

지금 경험해보세요: 원본 형식을 유지하면서 번역을 시작하려면 문서를 업로드하세요

업데이트: 2026-11-14