AI Engineering Academy · Lektion

Validering og nye forsøg ved ugyldigt output

Implementér et valideringslag, der kontrollerer udtrukne data mod forretningsregler, automatisk forsøger igen med korrigerende feedback, når valideringen mislykkes, og logger fejlmønstre.

Lektion 4 af 413 trin

Validering og nye forsøg ved ugyldigt output er en gratis AI Engineering Academy-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i AI Engineering Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. AI Engineering Academy-kurset indeholder 4 lektioner i alt.

Hvorfor LLM-output skal valideres

Selv med strukturerede output og Pydantic-skemaer kan LLM-udtræk give output, der er syntaktisk gyldigt, men semantisk forkert. En sikkerhedsscore på 1,5 (uden for intervallet 0-1), en pris på -99,99, en datostreng, der ikke kan fortolkes, eller et telefonnummer med bogstaver — alt dette består JSON-fortolkningen, men overholder ikke dine forretningsregler.

Validering er en separat opgave fra udtræk. Udtræk spørger: "Fik vi strukturerede data?" Validering spørger: "Er de strukturerede data korrekte og anvendelige?" Begge lag er nødvendige i en pipeline til produktion. Betragt det som et filter i to trin: LLM'en udtrækker, og din validator accepterer eller afviser.

Valideringslag

Et robust system til validering af output fungerer på flere niveauer:

  1. Skemavalidering (Pydantic): Korrekte felttyper, obligatoriske felter er til stede, enum-værdier matcher de tilladte værdier — håndteres automatisk af strukturerede output
  2. Formatvalidering: Telefonnumre matcher et regex, e-mailadresser er gyldige, datoer kan fortolkes, og beløb ligger inden for realistiske intervaller
  3. Validering af forretningslogik: Fakturatotalen er lig med summen af fakturalinjerne, slutdatoen ligger efter startdatoen, og antallet er et positivt heltal
  4. Validering på tværs af felter: Værdien af et felt afhænger af værdien af et andet felt (f.eks. kan rabatprocenten ikke overstige 100)
  5. Semantisk validering: Det udtrukne virksomhedsnavn matcher en kendt virksomhed i din database

Pydantic-validatorer til formatkontrol

Pydantics field_validator-decorator giver dig mulighed for at tilføje brugerdefineret valideringslogik, der kører, når modellen oprettes. Brug den til kontroller på formatniveau, f.eks. regex-validering af telefonnumre og e-mailadresser, fortolkning af datoer og kontrol af intervaller for numeriske felter.

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)

Mønsteret med genforsøg og korrigerende feedback

Når valideringen mislykkes, er den mest effektive gendannelsesstrategi genforsøg med korrigerende feedback: Send valideringsfejlen tilbage til modellen som kontekst, forklar, hvad der gik galt, og bed den om kun at rette de felter, der fejlede. På den måde får modellen de oplysninger, den skal bruge for at rette sit output, i stedet for blot at prøve igen i blinde.

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...')

Validering af forretningslogik

Validering af forretningslogik kontrollerer egenskaber, der omfatter flere felter, eller som afhænger af eksterne datakilder. Pydantics model_validator kører efter alle validatorer på feltniveau og har adgang til den fuldt udfyldte model, hvilket gør den velegnet til kontroller på tværs af felter.

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

Logning af valideringsfejl

Alle valideringsfejl er et signal om, hvor din pipeline bryder sammen. Log hver fejl med: inputteksten (eller en hash af den af hensyn til privatlivets fred), det udtrukne output, den specifikke valideringsfejl og forsøgsnummeret. Saml disse logge for at identificere systematiske mønstre — tager modellen konsekvent fejl i ét bestemt felt? Er der en kategori af dokumenter, der forårsager fejl? Disse data danner grundlag for målrettede forbedringer af prompts.

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

Sikkerhedsscorer og tærskler

Tilføj et confidence-felt til dit udtræksskema, og instruer modellen i at angive sin sikkerhed for hvert udtræk på en skala fra 0 til 1. Anvend derefter forretningsregler baseret på sikkerheden: Udtræk med høj sikkerhed sendes direkte til din database, udtræk med middel sikkerhed markeres til stikprøvekontrol, og udtræk med lav sikkerhed sendes til en kø til manuel gennemgang.

Denne probabilistiske tilgang er langt mere praktisk end at kræve 100 % nøjagtighed fra LLM'en — du designer din pipeline til at håndtere usikkerhed på en robust måde i stedet for at lade som om, den ikke findes.

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}'

Fallback-strategier, når genforsøg mislykkes

Når alle genforsøg er brugt op, og valideringen stadig mislykkes, har du brug for en fallback-strategi. Mulighederne er angivet i foretrukken rækkefølge:

  1. Delvist resultat: Returnér de felter, der blev valideret, og markér de felter, der fejlede, som null
  2. Kø til manuel gennemgang: Føj dokumentet til en kø til manuel gennemgang, især for dokumenter af høj værdi
  3. Udtræk med lavere detaljeringsgrad: Fald tilbage til et enklere skema, der beder om færre felter, og acceptér mindre struktur til gengæld for større robusthed
  4. Lagring af rå tekst: Gem den oprindelige tekst med metadata, så den kan behandles igen senere, når du har forbedret din pipeline til udtræk

Kassér aldrig dokumentet uden at give besked. Log altid fejlen, og sørg for, at du kan vende tilbage til dokumentet.

Semantisk validering mod eksterne data

Nogle valideringsregler kræver opslag i eksterne data, som ikke kan udføres i Pydantic-validatorer. Det kan for eksempel være at kontrollere, om et udtrukket virksomhedsnavn findes i dit CRM-system, eller om en udtrukket produkt-SKU findes i dit lager. Disse kontroller hører hjemme i et valideringstrin efter udtrækningen, som kører, når Pydantic-valideringen er gennemført.

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')

Test af din valideringspipeline

Din valideringslogik har brug for sin egen testsuite, separat fra testene af udtrækningen. Skriv enhedstests, der sender kendt ugyldige resultater til dine validatorer, og kontrollér, at de korrekte fejl opstår. Test kanttilfælde: beløb ved grænseværdier, datoer i usædvanlige formater, felter med uventede mellemrum og felter, hvor tal er angivet som tekststrenge i stedet for tal.

Denne valideringstestsuite kører uden API-kald, så den er hurtig og billig at køre ved hver kodeændring. Den er også den bedste dokumentation af dine valideringsregler — testtilfældene gør alle de format- og forretningsmæssige begrænsninger, som din pipeline håndhæver, tydelige.

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()

Analyse af mønstre i fejl og justering af prompts

Når du har kørt din udtrækningspipeline på et udsnit af virkelige dokumenter og gennemgået valideringsfejlene, vil du sandsynligvis opdage, at 80 % af fejlene har et lille antal grundårsager til fælles. Typiske syndere er, at modellen konsekvent formaterer datoer forkert fra en bestemt lokalitet, forveksler skattebeløbet med totalbeløbet eller genererer telefonnumre uden bindestreger, selvom din validator forventer bindestreger.

For hvert tilbagevendende fejlmønster skal du opdatere din udtrækningsprompt med et specifikt eksempel og en begrænsning, der forhindrer fejlen. Kør derefter hele dit testsæt igen for at bekræfte, at rettelsen forbedrede nøjagtigheden uden at forringe andre tilfælde. Denne gentagne prompt-og-evaluér-cyklus er sådan, produktionspipelines til udtrækning når op på over 95 % nøjagtighed på virkelige data.

Hurtigt tjek

Test din forståelse af AI Engineering-begreberne fra denne lektion.

Opsummering af lektionen

I denne lektion lærte du, at validering foregår på flere lag — skema, format, forretningslogik og semantik — og at hvert lag kræver forskellige implementeringstilgange, at nye forsøg med korrigerende feedback giver modellen specifik fejlkontekst, så den effektivt kan rette sit output, og at konfidensscorer muliggør probabilistisk routing til køer for automatisk godkendelse, stikprøvekontrol eller manuel gennemgang baseret på sikkerheden i udtrækningen. Du har nu gennemført kurset Structured Output and JSON Mode. Næste emne er vektorembeddings — grundlaget for alle RAG-systemer.

Gratis at komme i gang

Lær Python med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
30
Lektioner
120

Ofte stillede spørgsmål

Er lektionen “Validering og nye forsøg ved ugyldigt output” gratis?

Ja — hele teksten til “Validering og nye forsøg ved ugyldigt output” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af AI Engineering Academy-kurset, skal du opgradere til CoddyKit PRO. AI Engineering Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Validering og nye forsøg ved ugyldigt output”?

Implementér et valideringslag, der kontrollerer udtrukne data mod forretningsregler, automatisk forsøger igen med korrigerende feedback, når valideringen mislykkes, og logger fejlmønstre. Du øver dig i AI Engineering Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på AI Engineering Academy?

Der kræves ingen tidligere erfaring. AI Engineering Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.

Hvor lang tid tager lektionen “Validering og nye forsøg ved ugyldigt output”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne AI Engineering Academy-lektion?

Ja. Alle AI Engineering Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. JSON-tilstand og response_format
  2. Struktureret output med Pydantic
  3. Udtræk af data fra ustruktureret tekst
  4. Validering og nye forsøg ved ugyldigt output
← Tilbage til AI Engineering Academy