0Pricing
AI Engineering Academy · Lekcja

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:

  1. 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
  2. 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
  3. Walidacja logiki biznesowej: suma faktury jest równa sumie pozycji, data końcowa przypada po dacie początkowej, ilość jest dodatnią liczbą całkowitą
  4. Walidacja między polami: wartość jednego pola zależy od wartości innego pola (np. procent rabatu nie może przekraczać 100)
  5. 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 self

Logowanie 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 logic

Wyniki 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:

  1. Częściowy wynik: zwrócenie pól, które przeszły walidację, i oznaczenie nieprawidłowych pól jako null
  2. Kolejka weryfikacji przez człowieka: dodanie dokumentu do kolejki ręcznej weryfikacji, szczególnie w przypadku dokumentów o wysokiej wartości
  3. 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
  4. 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

  1. Tryb JSON i response_format
  2. Ustrukturyzowane dane wyjściowe z Pydantic
  3. Ekstrakcja danych z nieustrukturyzowanego tekstu
  4. Walidowanie i ponawianie dla niepoprawnych danych wyjściowych
← Powrót do AI Engineering Academy