잘못된 출력 검증 및 재시도
추출된 데이터를 업무 규칙과 대조하는 검증 계층을 구현하고, 검증에 실패하면 수정 피드백과 함께 자동으로 재시도하며, 실패 패턴을 기록합니다.
잘못된 출력 검증 및 재시도은(는) CoddyKit의 무료 AI Engineering Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Engineering Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
LLM 출력에 검증이 필요한 이유
구조화된 출력과 Pydantic 스키마를 사용하더라도 LLM 추출 결과는 문법적으로는 유효하지만 의미상 잘못될 수 있습니다. 1.5라는 확신도 점수(0~1 범위를 벗어남), -99.99라는 가격, 분석할 수 없는 날짜 문자열, 문자가 포함된 전화번호가 그 예입니다. 이러한 값은 모두 JSON 분석은 통과하지만 비즈니스 규칙에서는 실패합니다.
검증은 추출과 별개의 관심사입니다. 추출은 '정형 데이터를 얻었는가?'를 묻고, 검증은 '그 정형 데이터가 정확하고 사용할 수 있는가?'를 묻습니다. 운영 수준의 파이프라인에는 두 계층이 모두 필요합니다. 이를 2단계 필터라고 생각하십시오. LLM이 추출하고 검증기가 승인하거나 거부합니다.
검증 계층
견고한 출력 검증 시스템은 여러 수준에서 작동합니다.
- 스키마 검증(Pydantic): 올바른 필드 유형, 필수 필드의 존재 여부, 열거형이 허용된 값과 일치하는지 여부를 확인하며 구조화된 출력이 자동으로 처리합니다
- 형식 검증: 전화번호가 정규 표현식과 일치하는지, 이메일이 유효한지, 날짜를 분석할 수 있는지, 금액이 현실적인 범위 내에 있는지 확인합니다
- 비즈니스 로직 검증: 청구서 총액이 항목 합계와 같은지, 종료 날짜가 시작 날짜 이후인지, 수량이 양의 정수인지 확인합니다
- 필드 간 검증: 한 필드의 값이 다른 필드의 값에 의존하는지 확인합니다(예: 할인율이 100을 초과할 수 없음)
- 의미 검증: 추출된 회사명이 데이터베이스에 등록된 회사와 일치하는지 확인합니다
형식 검사를 위한 Pydantic 검증기
Pydantic의 field_validator 데코레이터를 사용하면 모델이 인스턴스화될 때 실행되는 사용자 지정 검증 로직을 추가할 수 있습니다. 전화번호와 이메일의 정규 표현식 검증, 날짜 분석, 숫자 필드의 범위 검사와 같은 형식 수준의 검사에 사용하십시오.
from pydantic import BaseModel, Field, field_validator
from typing import Optional
import re
from datetime import datetime
class ExtractedInvoice(BaseModel):
vendor: str
invoice_number: Optional[str]
amount: float = Field(gt=0, description='Must be positive')
currency: str = Field(min_length=3, max_length=3)
invoice_date: str
@field_validator('currency')
@classmethod
def currency_must_be_uppercase(cls, v):
return v.upper()
@field_validator('invoice_date')
@classmethod
def parse_date(cls, v):
# Try to parse common date formats
for fmt in ('%Y-%m-%d', '%d/%m/%Y', '%m/%d/%Y', '%B %d, %Y'):
try:
datetime.strptime(v, fmt)
return v
except ValueError:
continue
raise ValueError(f'Cannot parse date: {v}')
@field_validator('amount')
@classmethod
def reasonable_amount(cls, v):
if v > 10_000_000:
raise ValueError(f'Amount {v} seems unreasonably large. Flag for review.')
return round(v, 2)수정 피드백을 활용한 재시도 패턴
검증에 실패했을 때 가장 효과적인 복구 전략은 수정 피드백을 활용한 재시도입니다. 검증 오류 메시지를 문맥으로 모델에 다시 보내 무엇이 잘못되었는지 설명하고 실패한 필드만 수정하도록 요청하십시오. 이렇게 하면 무작정 다시 시도하는 대신 모델이 출력 결과를 수정하는 데 필요한 정보를 얻을 수 있습니다.
import openai
from pydantic import BaseModel, ValidationError, Field
client = openai.OpenAI()
class PriceExtraction(BaseModel):
product: str
price_usd: float = Field(gt=0, lt=100000)
quantity: int = Field(ge=1)
def extract_with_retry(text: str, max_retries: int = 3) -> PriceExtraction:
messages = [
{'role': 'system', 'content': 'Extract product pricing information.'},
{'role': 'user', 'content': text}
]
for attempt in range(max_retries):
result = client.beta.chat.completions.parse(
model='gpt-4o-mini',
messages=messages,
response_format=PriceExtraction
)
msg = result.choices[0].message
if msg.refusal:
raise ValueError(f'Model refused: {msg.refusal}')
try:
return msg.parsed # Pydantic validates on parse
except ValidationError as e:
if attempt == max_retries - 1:
raise
# Add corrective feedback for the next attempt
messages.append({'role': 'assistant', 'content': msg.content})
messages.append({'role': 'user', 'content': f'The previous extraction failed validation: {e}\nPlease correct and try again.'})
print(f'Attempt {attempt+1} failed. Retrying with feedback...')비즈니스 로직 검증
비즈니스 로직 검증은 여러 필드에 걸쳐 있거나 외부 데이터 원본에 의존하는 속성을 확인합니다. Pydantic의 model_validator는 모든 필드 수준 검증기가 실행된 후 작동하며 완전히 채워진 모델에 접근할 수 있으므로 필드 간 검사를 수행하기에 적합합니다.
from pydantic import BaseModel, Field, model_validator
from typing import List
class LineItem(BaseModel):
description: str
quantity: int = Field(ge=1)
unit_price: float = Field(ge=0)
line_total: float
@model_validator(mode='after')
def check_line_total(self):
expected = round(self.quantity * self.unit_price, 2)
actual = round(self.line_total, 2)
if abs(expected - actual) > 0.02: # Allow 2-cent rounding tolerance
raise ValueError(
f'Line total {actual} does not match quantity*price={expected}'
)
return self
class Invoice(BaseModel):
line_items: List[LineItem]
subtotal: float
tax: float
total: float
@model_validator(mode='after')
def check_invoice_total(self):
expected_total = round(self.subtotal + self.tax, 2)
if abs(expected_total - round(self.total, 2)) > 0.02:
raise ValueError(
f'Invoice total {self.total} != subtotal+tax ({expected_total})'
)
return self검증 실패 기록
모든 검증 실패는 파이프라인이 어디에서 제대로 작동하지 않는지 알려 주는 신호입니다. 각 실패에 대해 입력 텍스트(개인 정보 보호를 위해 텍스트의 해시를 사용해도 됨), 추출된 출력, 구체적인 검증 오류, 시도 횟수를 기록하십시오. 이러한 기록을 집계하여 체계적인 패턴을 파악하십시오. 모델이 특정 필드 하나를 계속 잘못 추출하고 있습니까? 실패를 일으키는 문서 유형이 있습니까? 이 데이터가 대상이 분명한 프롬프트 개선을 이끕니다.
import logging
from pydantic import ValidationError
logger = logging.getLogger(__name__)
def extract_with_logging(text: str, doc_id: str) -> dict:
result = None
for attempt in range(3):
try:
result = run_extraction(text) # Your extraction function
logger.info('Extraction success', extra={
'doc_id': doc_id,
'attempt': attempt + 1
})
return result
except ValidationError as e:
logger.warning('Validation failure', extra={
'doc_id': doc_id,
'attempt': attempt + 1,
'errors': e.errors(),
'error_count': len(e.errors())
})
# All retries failed
logger.error('Extraction failed after max retries', extra={'doc_id': doc_id})
return {'error': 'extraction_failed', 'doc_id': doc_id}
def run_extraction(text):
pass # Placeholder for actual extraction logic확신도 점수와 임계값
추출 스키마에 confidence 필드를 추가하고 모델이 각 추출 결과에 대한 확신도를 0에서 1 사이로 평가하도록 지시하십시오. 그런 다음 확신도에 따라 비즈니스 규칙을 적용하십시오. 확신도가 높은 추출 결과는 바로 데이터베이스로 보내고, 중간 수준의 결과는 표본 검사를 위해 표시하며, 확신도가 낮은 결과는 사람 검토 대기열로 보냅니다.
이러한 확률적 접근 방식은 LLM에 100% 정확도를 요구하는 것보다 훨씬 실용적입니다. 불확실성이 존재하지 않는 척하는 대신, 불확실성을 자연스럽게 처리하도록 파이프라인을 설계할 수 있기 때문입니다.
from pydantic import BaseModel, Field
from typing import Optional
class ExtractedWithConfidence(BaseModel):
value: Optional[str]
confidence: float = Field(ge=0.0, le=1.0)
reason: Optional[str] = None # Why confidence is low, if below threshold
class DocumentExtraction(BaseModel):
vendor_name: ExtractedWithConfidence
invoice_amount: ExtractedWithConfidence
due_date: ExtractedWithConfidence
def route_by_confidence(extraction: DocumentExtraction, threshold=0.85):
low_confidence_fields = []
for field_name, field_val in extraction.model_dump().items():
if isinstance(field_val, dict) and field_val.get('confidence', 1.0) < threshold:
low_confidence_fields.append(field_name)
if not low_confidence_fields:
return 'auto_approve'
elif len(low_confidence_fields) > 2:
return 'human_review'
else:
return f'spot_check: {low_confidence_fields}'재시도가 실패했을 때의 대체 전략
모든 재시도를 소진했는데도 검증에 계속 실패한다면 대체 전략이 필요합니다. 선호하는 순서에 따른 선택지는 다음과 같습니다.
- 부분 결과: 검증에 성공한 필드를 반환하고 실패한 필드는 비어 있는 값으로 표시합니다
- 사람 검토 대기열: 특히 가치가 높은 문서의 경우 수동 검토를 위해 문서를 대기열에 추가합니다
- 낮은 정밀도의 추출: 더 적은 필드를 요청하는 단순한 스키마로 전환하여 구조를 일부 포기하는 대신 견고성을 확보합니다
- 원문 저장: 추출 파이프라인을 개선한 후 다시 처리할 수 있도록 메타데이터와 함께 원문을 저장합니다
문서를 절대 조용히 삭제하지 마십시오. 항상 실패를 기록하고 해당 문서로 돌아갈 수 있도록 하십시오.
외부 데이터와 대조하는 의미 검증
일부 검증 규칙은 Pydantic 검증기 내부에서 수행할 수 없는 외부 데이터 조회를 필요로 합니다. 예를 들어 추출된 회사 이름이 CRM에 등록되어 있는지, 또는 추출된 상품 SKU가 재고에 존재하는지 확인하는 경우입니다. 이러한 검사는 Pydantic 검증이 통과한 후 실행되는 추출 후 검증 단계에서 수행해야 합니다.
from typing import Optional
# Simulated external data source
KNOWN_VENDORS = {'acme corp', 'techsupplies inc', 'globex corporation'}
def validate_against_crm(extraction: dict) -> dict:
vendor = extraction.get('vendor', '').lower()
warnings = []
if vendor and vendor not in KNOWN_VENDORS:
warnings.append({
'field': 'vendor',
'issue': f'Vendor "{vendor}" not found in CRM',
'severity': 'warning'
})
# Optionally suggest closest match
# from difflib import get_close_matches
# matches = get_close_matches(vendor, KNOWN_VENDORS, n=1, cutoff=0.8)
# if matches: warnings[-1]['suggestion'] = matches[0]
return {
'extraction': extraction,
'warnings': warnings,
'requires_review': len(warnings) > 0
}
print('Semantic validation pattern defined')검증 파이프라인 테스트
검증 로직에는 추출 테스트와 분리된 자체 테스트 모음이 필요합니다. 의도적으로 잘못된 출력값을 검증기에 전달하고 올바른 오류가 발생하는지 확인하는 단위 테스트를 작성하십시오. 경계값의 금액, 일반적이지 않은 형식의 날짜, 예상치 못한 공백이 포함된 필드, 숫자가 아닌 숫자 문자열인 필드와 같은 예외적인 경우도 테스트하십시오.
이 검증 테스트 모음은 API 호출 없이 실행되므로 코드가 변경될 때마다 빠르고 저렴하게 실행할 수 있습니다. 또한 검증 규칙을 설명하는 가장 좋은 문서이기도 합니다. 테스트 사례를 통해 파이프라인이 적용하는 모든 형식 및 업무 제약 조건을 명확하게 확인할 수 있습니다.
from pydantic import ValidationError
def test_extraction_validation():
# Test cases: (input_data, should_pass)
test_cases = [
({'vendor': 'ACME', 'amount': 150.00, 'currency': 'USD', 'invoice_date': '2025-01-15'}, True),
({'vendor': 'ACME', 'amount': -50.00, 'currency': 'USD', 'invoice_date': '2025-01-15'}, False), # negative
({'vendor': 'ACME', 'amount': 150.00, 'currency': 'EURO', 'invoice_date': '2025-01-15'}, False), # 4-char
({'vendor': 'ACME', 'amount': 150.00, 'currency': 'USD', 'invoice_date': 'yesterday'}, False), # bad date
]
passed = failed = 0
for data, should_pass in test_cases:
try:
# ExtractedInvoice(**data) # Your Pydantic model
if should_pass:
passed += 1
else:
print(f'MISSED: Should have failed for {data}')
failed += 1
except (ValidationError, ValueError):
if not should_pass:
passed += 1
else:
print(f'UNEXPECTED FAIL for {data}')
failed += 1
print(f'Tests: {passed} passed, {failed} failed')
test_extraction_validation()패턴 실패 분석 및 프롬프트 조정
실제 문서의 일부 표본에 추출 파이프라인을 실행하고 검증 실패를 검토한 후에는, 실패의 80%가 소수의 근본 원인을 공유한다는 사실을 발견하게 될 가능성이 높습니다. 흔한 원인으로는 특정 지역의 날짜 형식을 모델이 지속적으로 잘못 지정하는 경우, 세액과 총액을 혼동하는 경우, 또는 검증기가 하이픈을 요구하는데 하이픈이 없는 전화번호를 생성하는 경우가 있습니다.
반복되는 각 실패 패턴에 대해 해당 오류를 방지하는 구체적인 예시와 제약 조건을 추출 프롬프트에 추가하십시오. 수정한 후에는 전체 테스트 세트를 다시 실행하여 다른 사례의 성능을 저하시키지 않으면서 정확도가 향상되었는지 확인하십시오. 이처럼 프롬프트를 수정한 후 평가하는 반복 과정이 실제 데이터에서 운영 추출 파이프라인의 정확도를 95% 이상으로 끌어올리는 방법입니다.
빠른 확인
이 단원에서 배운 AI 엔지니어링 개념을 제대로 이해했는지 확인하십시오.
단원 요약
이 단원에서는 검증이 스키마, 형식, 업무 로직, 의미론 등 여러 계층에서 이루어지며 각 계층에는 서로 다른 구현 방식이 필요하다는 점, 수정을 위한 피드백을 포함한 재시도를 사용하면 모델에 구체적인 오류 상황을 전달하여 출력을 효율적으로 수정할 수 있다는 점, 그리고 신뢰도 점수를 사용하면 추출 확실성에 따라 자동 승인, 표본 검사 또는 사람 검토 대기열로 확률적으로 분기할 수 있다는 점을 배웠습니다. 이제 구조화된 출력 및 JSON 모드 과정을 완료했습니다. 다음으로 모든 RAG 시스템의 기반이 되는 벡터 임베딩을 살펴보겠습니다.
AI 튜터와 함께 Python을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 30
- 레슨
- 120
자주 묻는 질문
“잘못된 출력 검증 및 재시도” 강의는 무료인가요?
네 — “잘못된 출력 검증 및 재시도” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Engineering Academy 강의 전체를 잠금 해제할 수 있습니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“잘못된 출력 검증 및 재시도”에서 뭘 배우나요?
추출된 데이터를 업무 규칙과 대조하는 검증 계층을 구현하고, 검증에 실패하면 수정 피드백과 함께 자동으로 재시도하며, 실패 패턴을 기록합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Engineering Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
AI Engineering Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 AI Engineering Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“잘못된 출력 검증 및 재시도” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 AI Engineering Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 AI Engineering Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.