Walidowanie i ponawianie dla niepoprawnych danych wyjściowych
Uczestnicy zaimplementują warstwę walidacji sprawdzającą wyodrębnione dane względem reguł biznesowych, automatycznie ponowią próbę z informacją korygującą po nieudanej walidacji oraz zapiszą wzorce błędów w logach.
Walidowanie i ponawianie dla niepoprawnych danych wyjściowych to bezpłatna lekcja AI Engineering Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Engineering Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Dlaczego wyniki LLM-ów wymagają walidacji
Nawet w przypadku strukturalnych wyników i schematów Pydantic ekstrakcja za pomocą LLM-a może generować wyniki poprawne składniowo, ale błędne semantycznie. Wynik pewności równy 1.5 (poza zakresem od 0 do 1), cena -99.99, ciąg znaków daty, którego nie można sparsować, lub numer telefonu zawierający litery — wszystkie te wartości przechodzą parsowanie JSON, ale nie spełniają reguł biznesowych.
Walidacja jest odrębnym zagadnieniem od ekstrakcji. Ekstrakcja odpowiada na pytanie: „Czy uzyskaliśmy dane strukturalne?”. Walidacja pyta: „Czy dane strukturalne są poprawne i użyteczne?”. Obie warstwy są niezbędne w potoku jakości produkcyjnej. Można traktować je jak filtr dwuetapowy: LLM przeprowadza ekstrakcję, a walidator akceptuje wynik lub go odrzuca.
Warstwy walidacji
Solidny system walidacji wyników działa na wielu poziomach:
- Walidacja schematu (Pydantic): poprawne typy pól, obecność wymaganych pól, zgodność wartości wyliczeniowych z dozwolonymi wartościami — obsługiwane automatycznie przez strukturalne wyniki
- Walidacja formatu: numery telefonów pasują do wyrażenia regularnego, adresy e-mail są poprawne, daty można sparsować, a kwoty mieszczą się w realistycznych zakresach
- Walidacja logiki biznesowej: suma faktury jest równa sumie pozycji, data końcowa przypada po dacie początkowej, ilość jest dodatnią liczbą całkowitą
- Walidacja między polami: wartość jednego pola zależy od wartości innego pola (np. procent rabatu nie może przekraczać 100)
- Walidacja semantyczna: wydobyta nazwa firmy odpowiada znanej firmie w Państwa bazie danych
Walidatory Pydantic do sprawdzania formatu
Dekorator field_validator w Pydantic umożliwia dodanie niestandardowej logiki walidacji uruchamianej podczas tworzenia instancji modelu. Należy używać go do sprawdzania formatu, na przykład walidacji numerów telefonów i adresów e-mail za pomocą wyrażeń regularnych, parsowania dat oraz sprawdzania zakresów pól liczbowych.
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)Wzorzec ponownej próby z informacją zwrotną korygującą
Gdy walidacja się nie powiedzie, najskuteczniejszą strategią odzyskiwania jest ponowna próba z informacją zwrotną korygującą: należy przekazać komunikat o błędzie walidacji z powrotem do modelu jako kontekst, wyjaśnić, co poszło nie tak, i poprosić o poprawienie wyłącznie nieprawidłowych pól. Dzięki temu model otrzymuje informacje potrzebne do skorygowania wyniku, zamiast po prostu ponawiać próbę na ślepo.
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...')Walidacja logiki biznesowej
Walidacja logiki biznesowej sprawdza właściwości obejmujące wiele pól lub zależne od zewnętrznych źródeł danych. model_validator w Pydantic jest uruchamiany po wszystkich walidatorach poziomu pól i ma dostęp do w pełni wypełnionego modelu, dzięki czemu jest właściwym miejscem do sprawdzania zależności między polami.
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 selfLogowanie niepowodzeń walidacji
Każde niepowodzenie walidacji jest sygnałem wskazującym, gdzie potok nie działa prawidłowo. Należy rejestrować każde niepowodzenie wraz z: tekstem wejściowym (lub jego skrótem w celu ochrony prywatności), wydobytym wynikiem, konkretnym błędem walidacji i numerem próby. Agregowanie tych dzienników pomaga identyfikować powtarzające się wzorce — czy model konsekwentnie błędnie odczytuje jedno pole? Czy istnieje klasa dokumentów powodująca niepowodzenia? Dane te umożliwiają ukierunkowane ulepszanie promptów.
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 logicWyniki pewności i progi
Należy dodać pole confidence do schematu ekstrakcji i polecić modelowi ocenianie pewności każdej ekstrakcji w zakresie od 0 do 1. Następnie należy stosować reguły biznesowe zależne od poziomu pewności: ekstrakcje o wysokiej pewności trafiają bezpośrednio do bazy danych, ekstrakcje o średniej pewności są oznaczane do wyrywkowej kontroli, a ekstrakcje o niskiej pewności trafiają do kolejki weryfikacji przez człowieka.
Takie podejście probabilistyczne jest znacznie bardziej praktyczne niż wymaganie od LLM-a stuprocentowej dokładności — potok należy zaprojektować tak, aby sprawnie obsługiwał niepewność, zamiast udawać, że ona nie istnieje.
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}'Strategie awaryjne po nieudanych ponownych próbach
Gdy wszystkie ponowne próby zostaną wyczerpane, a walidacja nadal się nie powiedzie, potrzebna jest strategia awaryjna. Dostępne opcje, uporządkowane według preferencji:
- Częściowy wynik: zwrócenie pól, które przeszły walidację, i oznaczenie nieprawidłowych pól jako null
- Kolejka weryfikacji przez człowieka: dodanie dokumentu do kolejki ręcznej weryfikacji, szczególnie w przypadku dokumentów o wysokiej wartości
- Ekstrakcja o mniejszej szczegółowości: przejście na prostszy schemat wymagający mniejszej liczby pól, kosztem mniejszej struktury na rzecz większej odporności
- Przechowywanie surowego tekstu: zapisanie oryginalnego tekstu wraz z metadanymi do późniejszego ponownego przetworzenia po ulepszeniu potoku ekstrakcji
Nigdy nie należy po cichu odrzucać dokumentu. Zawsze trzeba zarejestrować niepowodzenie i zapewnić możliwość powrotu do dokumentu.
Walidacja semantyczna względem danych zewnętrznych
Niektóre reguły walidacji wymagają wyszukania danych zewnętrznych, czego nie można wykonać wewnątrz walidatorów Pydantic. Przykładowo może chodzić o sprawdzenie, czy wyodrębniona nazwa firmy występuje w systemie CRM albo czy wyodrębniony identyfikator SKU produktu istnieje w systemie magazynowym. Takie kontrole powinny należeć do etapu walidacji po ekstrakcji, uruchamianego po pomyślnym przejściu walidacji 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')Testowanie potoku walidacji
Logika walidacji powinna mieć własny zestaw testów, niezależny od testów ekstrakcji. Należy pisać testy jednostkowe, które przekazują do walidatorów znane niepoprawne wyniki i sprawdzają, czy zgłaszane są właściwe błędy. Należy testować przypadki brzegowe: kwoty na wartościach granicznych, daty w nietypowych formatach, pola z nieoczekiwanymi białymi znakami oraz pola zawierające ciągi znaków reprezentujące liczby zamiast liczb.
Ten zestaw testów walidacji działa bez wywołań API, dzięki czemu jego uruchamianie przy każdej zmianie kodu jest szybkie i tanie. Jest to również najlepsza dokumentacja reguł walidacji — przypadki testowe jasno określają każdy format i każde ograniczenie biznesowe egzekwowane przez potok.
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()Analiza wzorców błędów i dostrajanie promptów
Po uruchomieniu potoku ekstrakcji na próbce rzeczywistych dokumentów i przeanalizowaniu błędów walidacji prawdopodobnie okaże się, że 80% błędów ma zaledwie kilka podstawowych przyczyn. Typowe problemy to: model konsekwentnie formatuje daty niepoprawnie dla określonej lokalizacji, myli kwotę podatku z kwotą całkowitą albo generuje numery telefonów bez łączników, podczas gdy walidator ich wymaga.
Dla każdego powtarzającego się wzorca błędów należy zaktualizować prompt ekstrakcji, dodając konkretny przykład i ograniczenie zapobiegające danemu błędowi. Po aktualizacji należy ponownie uruchomić pełny zestaw testów, aby potwierdzić, że poprawka zwiększyła dokładność i nie pogorszyła wyników w innych przypadkach. Ten iteracyjny cykl: prompt, a następnie ewaluacja, pozwala produkcyjnym potokom ekstrakcji osiągać dokładność na poziomie ponad 95% dla rzeczywistych danych.
Szybkie sprawdzenie
Sprawdź swoją wiedzę na temat koncepcji inżynierii AI z tej lekcji.
Podsumowanie lekcji
W tej lekcji poznali Państwo: walidacja działa na wielu warstwach — schematu, formatu, logiki biznesowej i semantyki — a każda z nich wymaga innego sposobu implementacji, ponowienie próby z informacją zwrotną zawierającą poprawki dostarcza modelowi konkretnego kontekstu błędu, dzięki czemu może on sprawnie poprawić swój wynik oraz oceny pewności umożliwiają probabilistyczne kierowanie wyników do automatycznego zatwierdzenia, wyrywkowej kontroli lub kolejki do weryfikacji przez człowieka, zależnie od pewności ekstrakcji. Ukończyli Państwo kurs Structured Output and JSON Mode. Następnie omówimy embeddingi wektorowe — podstawę każdego systemu RAG.
Często zadawane pytania
Czy lekcja „Walidowanie i ponawianie dla niepoprawnych danych wyjściowych” jest bezpłatna?
Tak — pełny tekst „Walidowanie i ponawianie dla niepoprawnych danych wyjściowych” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Engineering Academy, przejdź na CoddyKit PRO. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Walidowanie i ponawianie dla niepoprawnych danych wyjściowych”?
Uczestnicy zaimplementują warstwę walidacji sprawdzającą wyodrębnione dane względem reguł biznesowych, automatycznie ponowią próbę z informacją korygującą po nieudanej walidacji oraz zapiszą wzorce b… Ćwiczysz AI Engineering Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć AI Engineering Academy?
Nie wymagamy żadnego doświadczenia. AI Engineering Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Walidowanie i ponawianie dla niepoprawnych danych wyjściowych”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji AI Engineering Academy?
Tak. Każda lekcja AI Engineering Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Tryb JSON i response_format
- Ustrukturyzowane dane wyjściowe z Pydantic
- Ekstrakcja danych z nieustrukturyzowanego tekstu
- Walidowanie i ponawianie dla niepoprawnych danych wyjściowych