Validare e riprovare gli output errati
Implementerà un livello di validazione che controlli i dati estratti rispetto alle regole aziendali, esegua automaticamente un nuovo tentativo con feedback correttivo in caso di errore e registri i pattern dei fallimenti.
Validare e riprovare gli output errati è una lezione AI Engineering Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Engineering Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Engineering Academy include 4 lezioni in totale.
Perché gli output degli LLM richiedono la convalida
Anche con output strutturati e schemi Pydantic, l'estrazione con gli LLM può produrre output sintatticamente validi ma semanticamente errati. Un punteggio di confidenza pari a 1.5 (al di fuori dell'intervallo 0-1), un prezzo di -99.99, una stringa di data non analizzabile o un numero di telefono contenente lettere: tutti questi valori superano il parsing JSON, ma non rispettano le regole aziendali.
La convalida è un aspetto distinto dall'estrazione. L'estrazione chiede: «Abbiamo ottenuto dati strutturati?». La convalida chiede: «I dati strutturati sono corretti e utilizzabili?». Entrambi i livelli sono necessari per una pipeline di livello produttivo. La si può considerare come un filtro in due fasi: l'LLM estrae, il sistema di convalida accetta o rifiuta.
Livelli di convalida
Un sistema affidabile di convalida degli output opera a più livelli:
- Convalida dello schema (Pydantic): tipi di campo corretti, presenza dei campi obbligatori, corrispondenza degli enum ai valori consentiti; viene gestita automaticamente dagli output strutturati
- Convalida del formato: i numeri di telefono corrispondono a una regex, le email sono valide, le date sono analizzabili e gli importi rientrano in intervalli realistici
- Convalida della logica aziendale: il totale della fattura corrisponde alla somma delle voci, la data di fine è successiva alla data di inizio e la quantità è un intero positivo
- Convalida tra campi: il valore di un campo dipende da quello di un altro (ad esempio, la percentuale di sconto non può superare 100)
- Convalida semantica: il nome dell'azienda estratto corrisponde a un'azienda conosciuta nel database
Validatori Pydantic per i controlli di formato
Il decoratore field_validator di Pydantic consente di aggiungere una logica di convalida personalizzata che viene eseguita quando il modello viene istanziato. Lo utilizzi per controlli a livello di formato, come la convalida tramite regex di numeri di telefono ed email, il parsing delle date e i controlli degli intervalli dei campi numerici.
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)Il pattern dei nuovi tentativi con feedback correttivo
Quando la convalida non riesce, la strategia di recupero più efficace consiste nell'effettuare un nuovo tentativo con feedback correttivo: invii al modello il messaggio di errore della convalida come contesto, spieghi cosa non ha funzionato e chieda di correggere solo i campi problematici. In questo modo il modello dispone delle informazioni necessarie per correggere il proprio output, invece di riprovare alla cieca.
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...')Convalida della logica aziendale
La convalida della logica aziendale controlla proprietà che coinvolgono più campi o che dipendono da fonti di dati esterne. model_validator di Pydantic viene eseguito dopo tutti i validatori a livello di campo e può accedere al modello completamente compilato, perciò è il punto adatto per i controlli tra campi.
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 selfRegistrazione degli errori di convalida
Ogni errore di convalida segnala un punto in cui la pipeline si sta bloccando. Registri ogni errore insieme a: testo di input (o un suo hash per motivi di privacy), output estratto, errore di convalida specifico e numero del tentativo. Aggregi questi log per identificare schemi sistematici: il modello sbaglia costantemente un determinato campo? Esiste una categoria di documenti che causa errori? Questi dati guidano miglioramenti mirati dei prompt.
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 logicPunteggi di confidenza e soglie
Aggiunga un campo confidence allo schema di estrazione e chieda al modello di valutare la propria confidenza in ogni estrazione su una scala da 0 a 1. Applichi quindi regole aziendali basate sulla confidenza: le estrazioni con alta confidenza passano direttamente al database, quelle con confidenza media vengono segnalate per controlli a campione e quelle con bassa confidenza vengono inserite in una coda per la revisione umana.
Questo approccio probabilistico è molto più pratico che richiedere un'accuratezza del 100% all'LLM: si progetta la pipeline in modo che gestisca l'incertezza con equilibrio, invece di fingere che non esista.
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 di fallback quando i nuovi tentativi non riescono
Quando tutti i nuovi tentativi sono esauriti e la convalida continua a non riuscire, è necessaria una strategia di fallback. Opzioni, in ordine di preferenza:
- Risultato parziale: restituisca i campi convalidati e imposti a null quelli che non superano la convalida
- Coda per la revisione umana: aggiunga il documento a una coda per la revisione manuale, soprattutto nel caso di documenti di alto valore
- Estrazione meno dettagliata: ricorra a uno schema più semplice che richieda meno campi, accettando una struttura meno ricca in cambio di maggiore robustezza
- Memorizzazione del testo grezzo: salvi il testo originale con i metadati per rielaborarlo in seguito, quando la pipeline di estrazione sarà migliorata
Non scarti mai il documento senza registrare l'operazione. Registri sempre l'errore e si assicuri di poterlo recuperare.
Convalida semantica rispetto a dati esterni
Alcune regole di validazione richiedono ricerche su dati esterni che non possono essere eseguite all’interno dei validator di Pydantic. Ad esempio, verificare che il nome di un’azienda estratto compaia nel CRM o che un SKU di prodotto estratto esista nel vostro inventario. Questi controlli appartengono a un passaggio di validazione post-estrazione, eseguito dopo il superamento della validazione di 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')Testare la pipeline di validazione
La logica di validazione richiede una propria suite di test, separata dai test di estrazione. Scrivete unit test che forniscano ai vostri validator output notoriamente errati e verifichino che vengano generati gli errori corretti. Testate i casi limite: importi ai valori soglia, date in formati insoliti, campi con spazi bianchi imprevisti e campi contenenti stringhe numeriche anziché numeri.
Questa suite di test della validazione viene eseguita senza effettuare chiamate API, quindi è rapida ed economica da eseguire a ogni modifica del codice. È anche la documentazione migliore delle vostre regole di validazione: i casi di test rendono espliciti tutti i vincoli di formato e di business applicati dalla pipeline.
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()Analisi dei pattern di errore e ottimizzazione dei prompt
Dopo aver eseguito la pipeline di estrazione su un campione di documenti reali e aver esaminato gli errori di validazione, probabilmente scoprirete che l’80% degli errori condivide un numero ridotto di cause principali. Tra le cause più comuni: il modello formatta sistematicamente in modo errato le date di una specifica area geografica, confonde l’importo dell’imposta con il totale oppure produce numeri di telefono senza trattini quando il validator si aspetta i trattini.
Per ogni pattern di errore ricorrente, aggiornate il prompt di estrazione con un esempio specifico e un vincolo che impedisca il verificarsi dell’errore. Dopo l’aggiornamento, eseguite nuovamente l’intero set di test per verificare che la correzione abbia migliorato la precisione senza introdurre regressioni negli altri casi. Questo ciclo iterativo di modifica del prompt e valutazione è il modo in cui le pipeline di estrazione in produzione raggiungono una precisione superiore al 95% sui dati del mondo reale.
Verifica rapida
Verificate la vostra comprensione dei concetti di AI Engineering trattati in questa lezione.
Riepilogo della lezione
In questa lezione avete appreso che la validazione opera su più livelli — schema, formato, logica di business e semantica — e che ciascuno richiede approcci di implementazione diversi, che il retry con feedback correttivo fornisce al modello un contesto specifico sugli errori per correggere efficientemente il proprio output e che i punteggi di confidenza consentono un instradamento probabilistico verso code di approvazione automatica, controllo a campione o revisione umana in base alla certezza dell’estrazione. Avete completato il corso Structured Output and JSON Mode. Ora esploreremo i vector embedding, la base di ogni sistema RAG.
Domande Frequenti
La lezione «Validare e riprovare gli output errati» è gratuita?
Sì — il testo completo di «Validare e riprovare gli output errati» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Engineering Academy, passa a CoddyKit PRO. Il corso AI Engineering Academy include 4 lezioni in totale.
Cosa imparerò in «Validare e riprovare gli output errati»?
Implementerà un livello di validazione che controlli i dati estratti rispetto alle regole aziendali, esegua automaticamente un nuovo tentativo con feedback correttivo in caso di errore e registri i p… Eserciti AI Engineering Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare AI Engineering Academy?
Non è richiesta alcuna esperienza precedente. AI Engineering Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Validare e riprovare gli output errati»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione AI Engineering Academy?
Sì. Ogni lezione AI Engineering Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Modalità JSON e response_format
- Output strutturati con Pydantic
- Estrarre dati da testo non strutturato
- Validare e riprovare gli output errati