0Pricing
AI Engineering Academy · Lección

Validación y reintento de salidas incorrectas

Implementará una capa de validación que compruebe los datos extraídos según reglas de negocio, realice reintentos automáticos con comentarios correctivos cuando falle la validación y registre patrones de fallo.

Validación y reintento de salidas incorrectas es una lección gratuita de AI Engineering Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Engineering Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Engineering Academy incluye 4 lecciones en total.

Por qué las salidas de los LLM necesitan validación

Incluso con salidas estructuradas y esquemas de Pydantic, la extracción mediante LLM puede producir salidas sintácticamente válidas, pero semánticamente incorrectas. Una puntuación de confianza de 1.5 (fuera del intervalo de 0 a 1), un precio de -99.99, una cadena de fecha que no se puede analizar o un número de teléfono con letras: todo esto supera el análisis de JSON, pero incumple sus reglas de negocio.

La validación es una responsabilidad independiente de la extracción. La extracción pregunta: «¿Hemos obtenido datos estructurados?». La validación pregunta: «¿Son correctos y utilizables esos datos estructurados?». Ambas capas son necesarias para un pipeline listo para producción. Piense en ello como un filtro de dos etapas: el LLM extrae y su validador acepta o rechaza.

Capas de validación

Un sistema sólido de validación de salidas funciona en varios niveles:

  1. Validación del esquema (Pydantic): tipos de campo correctos, presencia de los campos obligatorios y correspondencia de los enums con los valores permitidos; las salidas estructuradas lo gestionan automáticamente
  2. Validación del formato: los números de teléfono coinciden con una regex, los correos electrónicos son válidos, las fechas se pueden analizar y los importes están dentro de intervalos realistas
  3. Validación de la lógica de negocio: el total de la factura equivale a la suma de las partidas, la fecha de finalización es posterior a la fecha de inicio y la cantidad es un entero positivo
  4. Validación entre campos: el valor de un campo depende del valor de otro (por ejemplo, el porcentaje de descuento no puede superar el 100)
  5. Validación semántica: el nombre de empresa extraído coincide con una empresa conocida en su base de datos

Validadores de Pydantic para comprobaciones de formato

El decorador field_validator de Pydantic permite añadir lógica de validación personalizada que se ejecuta al instanciar el modelo. Utilícelo para comprobaciones de nivel de formato, como la validación mediante regex de números de teléfono y correos electrónicos, el análisis de fechas y las comprobaciones de rango de campos numéricos.

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)

Patrón de reintento con feedback correctivo

Cuando falla la validación, la estrategia de recuperación más eficaz es el reintento con feedback correctivo: envíe al modelo el mensaje de error de validación como contexto, explique qué ha fallado y pídale que corrija únicamente los campos problemáticos. De este modo, el modelo obtiene la información que necesita para corregir su salida en lugar de limitarse a reintentarlo a ciegas.

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

Validación de la lógica de negocio

La validación de la lógica de negocio comprueba propiedades que abarcan varios campos o que dependen de fuentes de datos externas. El model_validator de Pydantic se ejecuta después de todos los validadores a nivel de campo y puede acceder al modelo completamente poblado, por lo que es el lugar adecuado para las comprobaciones entre campos.

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

Registro de fallos de validación

Cada fallo de validación es una señal sobre el punto en el que su pipeline está fallando. Registre cada fallo junto con: el texto de entrada (o un hash del mismo por motivos de privacidad), la salida extraída, el error de validación específico y el número de intento. Agregue estos registros para identificar patrones sistemáticos: ¿el modelo se equivoca constantemente en un campo? ¿Hay una clase de documentos que provoca fallos? Estos datos impulsan mejoras específicas de los 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

Puntuaciones de confianza y umbrales

Añada un campo confidence al esquema de extracción e indique al modelo que valore su confianza en cada extracción entre 0 y 1. Después, aplique reglas de negocio basadas en la confianza: las extracciones con alta confianza pasan directamente a su base de datos, las de confianza media se marcan para una comprobación aleatoria y las de baja confianza se envían a una cola de revisión humana.

Este enfoque probabilístico es mucho más práctico que exigir una precisión del 100 % al LLM: diseñe su pipeline para gestionar la incertidumbre con elegancia en lugar de fingir que no existe.

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

Estrategias alternativas cuando fallan los reintentos

Cuando se agotan todos los reintentos y la validación sigue fallando, necesita una estrategia alternativa. Opciones, por orden de preferencia:

  1. Resultado parcial: devuelva los campos que sí se validaron y marque como null los campos que fallaron
  2. Cola de revisión humana: añada el documento a una cola para su revisión manual, especialmente si se trata de documentos de gran valor
  3. Extracción de menor fidelidad: utilice como alternativa un esquema más sencillo que solicite menos campos, aceptando una estructura menor a cambio de una mayor robustez
  4. Almacenamiento del texto sin procesar: guarde el texto original con metadatos para volver a procesarlo más adelante, cuando haya mejorado su pipeline de extracción

No descarte nunca el documento silenciosamente. Registre siempre el fallo y asegúrese de poder volver a procesarlo.

Validación semántica con datos externos

Algunas reglas de validación requieren consultas a datos externos que no pueden realizarse dentro de los validadores de Pydantic. Por ejemplo, comprobar que el nombre de empresa extraído aparece en su CRM o que un SKU de producto extraído existe en su inventario. Estas comprobaciones deben realizarse en un paso de validación posterior a la extracción, que se ejecuta después de que la validación de Pydantic se complete correctamente.

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

Pruebas de su pipeline de validación

Su lógica de validación necesita su propia suite de pruebas, separada de las pruebas de extracción. Escriba pruebas unitarias que proporcionen a sus validadores resultados incorrectos conocidos y compruebe que se generen los errores correctos. Pruebe casos límite: importes en los valores extremos, fechas en formatos inusuales, campos con espacios en blanco inesperados y campos que sean cadenas numéricas en lugar de números.

Esta suite de pruebas de validación se ejecuta sin realizar llamadas a ninguna API, por lo que es rápida y económica de ejecutar con cada cambio de código. También es la mejor documentación de sus reglas de validación: los casos de prueba explicitan cada formato y restricción empresarial que aplica su 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()

Análisis de fallos recurrentes y ajuste de prompts

Después de ejecutar su pipeline de extracción sobre una muestra de documentos reales y revisar los fallos de validación, probablemente descubrirá que el 80 % de los fallos comparte un pequeño número de causas raíz. Entre las causas habituales se encuentran que el modelo formatea sistemáticamente mal las fechas de una región específica, confunde el importe del impuesto con el total o genera números de teléfono sin guiones cuando su validador espera que los tengan.

Para cada patrón de fallo recurrente, actualice su prompt de extracción con un ejemplo y una restricción específicos que eviten ese error. Después de actualizarlo, vuelva a ejecutar todo el conjunto de pruebas para confirmar que la corrección mejoró la precisión sin introducir regresiones en otros casos. Este ciclo iterativo de ajustar el prompt y evaluar es lo que permite que los pipelines de extracción en producción alcancen una precisión superior al 95 % con datos del mundo real.

Comprobación rápida

Compruebe su comprensión de los conceptos de ingeniería de IA de esta lección.

Resumen de la lección

En esta lección ha aprendido que la validación funciona en varias capas —esquema, formato, lógica empresarial y semántica— y que cada una requiere distintos enfoques de implementación, que los reintentos con comentarios correctivos proporcionan al modelo un contexto de error específico para corregir su salida de forma eficiente y que las puntuaciones de confianza permiten dirigir probabilísticamente los resultados a colas de aprobación automática, revisión selectiva o revisión humana según la certeza de la extracción. Ya ha completado el curso sobre salida estructurada y JSON Mode. A continuación, exploraremos los embeddings vectoriales, la base de todo sistema RAG.

Preguntas frecuentes

¿La lección «Validación y reintento de salidas incorrectas» es gratis?

Sí — el texto completo de «Validación y reintento de salidas incorrectas» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Engineering Academy, actualiza a CoddyKit PRO. El curso de AI Engineering Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Validación y reintento de salidas incorrectas»?

Implementará una capa de validación que compruebe los datos extraídos según reglas de negocio, realice reintentos automáticos con comentarios correctivos cuando falle la validación y registre patrone… Practicas AI Engineering Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar AI Engineering Academy?

No se requiere experiencia previa. AI Engineering Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Validación y reintento de salidas incorrectas»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de AI Engineering Academy?

Sí. Cada lección de AI Engineering Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Modo JSON y response_format
  2. Salidas estructuradas con Pydantic
  3. Extracción de datos de texto no estructurado
  4. Validación y reintento de salidas incorrectas
← Volver a AI Engineering Academy