Volver a las guías
Arquitectura·17 de julio de 2026·Actualizado el 30 de septiembre de 2026·13 min de lectura

Extracción de datos de facturas: deep learning frente a machine learning frente a LLM (guía de 2026)

La extracción de facturas ha pasado por cuatro generaciones de técnicas: plantillas, machine learning clásico, deep learning ajustado y ahora una única llamada a un vision-LLM. Así se comparan, junto con el patrón LLM completo y funcional: código, salida estructurada, salvaguardas y economía por factura.

Última revisión: .

Hace cinco años, extraer datos estructurados de facturas significaba una canalización de aprendizaje profundo: una etapa de OCR, un modelo consciente del diseño (LayoutLM, Donut) ajustado con miles de documentos etiquetados manualmente y una carga de mantenimiento cada vez que un proveedor cambiaba su plantilla. En 2026, toda la canalización se reduce a una llamada a la API de un LLM de visión: entra una imagen y sale JSON validado por esquema, sin datos de entrenamiento, sin modelos alojados y con nuevos diseños gestionados sin ejemplos previos. Esta guía de Kunavo —una puerta de enlace independiente compatible con OpenAI para los modelos de visión en los que se basa este patrón— muestra el patrón completo en funcionamiento, cuánto cuesta por factura según nuestras tarifas publicadas y cómo mantener una precisión adecuada para producción.

Comparación de métodos de extracción de facturas: reglas, aprendizaje automático, aprendizaje profundo y LLM

Se han utilizado cuatro generaciones de técnicas para obtener datos estructurados de una factura. Vale la pena colocarlas una junto a otra, porque la compensación que durante dos décadas decidió entre ellas —precisión comprada con datos etiquetados— es precisamente la que dejó de aplicarse.

EnfoqueCómo funcionaCoste de configuraciónGestiona un diseño nuevo de proveedor
Plantillas / reglas (década de 1990–)OCR y después zonas de coordenadas y expresiones regulares por proveedor (“el total es el número situado a la derecha de ‘TOTAL’”)Horas por proveedor, para siempreNo; necesita una plantilla nueva
Aprendizaje automático clásico (década de 2010)OCR y después características diseñadas manualmente (posición, tamaño de fuente y palabras vecinas) introducidas en un CRF o SVM que etiqueta cada tokenIngeniería de características + unos pocos miles de etiquetasParcialmente; se degrada con diseños no vistos
Aprendizaje profundo (2020–)Transformadores conscientes del diseño (LayoutLM, Donut, DocTR) ajustados para leer conjuntamente texto, posición y píxeles3.000–50.000 facturas etiquetadas + servicio en GPUA menudo no — normalmente requiere volver a etiquetar y reentrenar
LLM de visión (2024–)Una llamada a la API: imagen de la factura más un esquema JSON, con una salida estructurada restringida a ese esquemaNinguno — un esquema y un prompt; $0.00248–$0.00497 por facturaSí, zero-shot

Las tres primeras comparten una propiedad: la precisión se compra con datos etiquetados. Cada plantilla de proveedor, cada característica de CRF y cada ajuste fino de LayoutLM es un proyecto de anotación. Un LLM de visión ya ha pagado ese coste durante el preentrenamiento, por lo que el coste marginal de un nuevo diseño es cero. Ese es todo el cambio: no que los métodos anteriores hayan dejado de funcionar, sino que aquello en lo que gastaban su presupuesto pasó a ser gratis.

Aprendizaje profundo ajustado frente a LLM de visión, en detalle

Modelo de diseño ajustado (2021)LLM de visión (2026)
Datos de entrenamiento3.000–50.000 facturas etiquetadasNinguno (zero-shot) — unos pocos ejemplos ayudan con los casos límite
Nuevo diseño de proveedorA menudo requiere volver a etiquetar + reentrenarResuelto mediante zero-shot
InfraestructuraServicio en GPU + etapa de OCRUna llamada HTTPS
Formato de salidaEtiquetas de tokens → decodificación personalizadaJSON restringido por esquema
Coste por facturaPredomina el tiempo de ingeniería$0.00248–$0.00497

Un extractor ajustado todavía puede ganar con un único diseño de alto volumen congelado, pero para la cola larga de facturas del mundo real — distintos proveedores, idiomas y calidad de escaneo — el LLM de visión zero-shot es más preciso en la práctica y radicalmente más barato de mantener. Encontrarás más patrones para esta clase de carga de trabajo en el caso de uso de extracción de datos.

El patrón completo: imagen → JSON validado por esquema

Todo lo que aparece a continuación se ejecuta contra el endpoint compatible con OpenAI de Kunavo: apunta base_url a https://api.kunavo.com/v1 y el mismo código puede usar Claude o GPT cambiando una cadena:

extract_invoice.py
from openai import OpenAI
import base64, json

client = OpenAI(
    base_url="https://api.kunavo.com/v1",
    api_key="sk-kn-...",
)

SCHEMA = {
    "name": "invoice",
    "schema": {
        "type": "object",
        "properties": {
            "vendor_name":    {"type": "string"},
            "vendor_tax_id":  {"type": ["string", "null"]},
            "invoice_number": {"type": "string"},
            "invoice_date":   {"type": "string", "description": "ISO 8601"},
            "due_date":       {"type": ["string", "null"]},
            "currency":       {"type": "string", "description": "ISO 4217"},
            "line_items": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "description": {"type": "string"},
                        "quantity":    {"type": "number"},
                        "unit_price":  {"type": "number"},
                        "amount":      {"type": "number"},
                    },
                    "required": ["description", "amount"],
                },
            },
            "subtotal":  {"type": ["number", "null"]},
            "tax":       {"type": ["number", "null"]},
            "total":     {"type": "number"},
        },
        "required": ["vendor_name", "invoice_number", "invoice_date",
                     "currency", "line_items", "total"],
    },
}

def extract(path: str) -> dict:
    image_b64 = base64.b64encode(open(path, "rb").read()).decode()
    resp = client.chat.completions.create(
        model="claude-haiku-4-5",   # the cheapest capable model for clean invoices
        response_format={"type": "json_schema", "json_schema": SCHEMA},
        messages=[
            {"role": "system", "content":
                "Extract the invoice into the schema. Copy values exactly as "
                "printed; use null when a field is absent. Never invent data."},
            {"role": "user", "content": [
                {"type": "image_url",
                 "image_url": {"url": f"data:image/png;base64,{image_b64}"}},
            ]},
        ],
    )
    return json.loads(resp.choices[0].message.content)

inv = extract("invoice_0231.png")

# Cheap arithmetic guardrail: reject when the line items don't add up.
delta = abs(sum(li["amount"] for li in inv["line_items"])
            + (inv.get("tax") or 0) - inv["total"])
if delta > 0.02:
    raise ValueError(f"line items disagree with total by {delta:.2f}")

print(inv["vendor_name"], inv["total"], inv["currency"])

Tres detalles hacen la mayor parte del trabajo: el esquema JSON en response_format (en Claude el modelo no puede emitir nada más; consulta la nota sobre GPT en la referencia enlazada más abajo), la regla del prompt del sistema «copia los valores exactamente; null cuando falten; nunca inventes» (elimina los identificadores fiscales alucinados) y la protección aritmética: las partidas + el impuesto deben ser iguales al total; de lo contrario, el documento pasa a un modelo más potente o a una persona. Consulta la referencia de finalizaciones de chat para saber qué aplica response_format en cada familia de modelos.

El mismo esquema, como modelo Pydantic tipado

Un diccionario de esquema JSON sin formato está bien para un endpoint y resulta engorroso para diez. En producción, la mayoría de los equipos define el esquema una sola vez como modelo Pydantic y deriva de él tanto la restricción como el objeto analizado, de modo que el extractor y el resto del código nunca puedan discrepar sobre la estructura:

schema_pydantic.py
from pydantic import BaseModel, Field
from typing import Literal
from openai import OpenAI

client = OpenAI(base_url="https://api.kunavo.com/v1", api_key=KEY)

class LineItem(BaseModel):
    description: str
    quantity: float | None = None
    unit_price: float | None = None
    amount: float

class Invoice(BaseModel):
    vendor_name: str
    vendor_tax_id: str | None = None
    invoice_number: str
    invoice_date: str = Field(description="ISO 8601")
    currency: str = Field(description="ISO 4217")
    line_items: list[LineItem]
    tax: float | None = None
    total: float
    # A confidence field the model fills in itself is a cheap, surprisingly
    # reliable router: low self-reported confidence correlates well with the
    # documents a human should see.
    confidence: Literal["high", "medium", "low"]

resp = client.chat.completions.create(
    model="claude-haiku-4-5",
    response_format={
        "type": "json_schema",
        "json_schema": {"name": "invoice", "schema": Invoice.model_json_schema()},
    },
    messages=[...],
)

invoice = Invoice.model_validate_json(resp.choices[0].message.content)

Aquí merece la pena copiar dos cosas. Los campos tipados como float | None en lugar de float dan al modelo una forma válida de decir «ausente»; sin ello, una línea fiscal faltante ejerce una fuerte presión para inventar un cero. Y el campo confidence autodeclarado cuesta tres tokens de salida y permite enrutar sorprendentemente bien; no está calibrado, pero «bajo» es una señal fiable de que una persona debería revisar el documento.

Enrutamiento de dos niveles: lo que realmente se ejecuta en producción

Usar un único modelo para todos los documentos es una estructura equivocada. Las facturas impresas limpias — la abrumadora mayoría — se procesan con el modelo de visión más barato disponible; la cola difícil (escritura a mano, escaneos deficientes y diseños inusuales) es donde un modelo más potente justifica su tarifa. Escala por fallo de validación, no por una suposición:

two_tier_routing.py
CHEAP, STRONG = "claude-haiku-4-5", "claude-sonnet-5"

def valid(inv: dict) -> bool:
    """Arithmetic is the guardrail no confidence score replaces."""
    items = sum(li["amount"] for li in inv["line_items"])
    return abs(items + (inv.get("tax") or 0) - inv["total"]) <= 0.02

def extract_routed(path: str) -> tuple[dict, str]:
    inv = extract(path, model=CHEAP)
    if valid(inv) and inv.get("confidence") != "low":
        return inv, CHEAP

    # Escalate: same prompt, same schema, stronger model. Only the failures
    # pay the higher rate, which is what keeps the blended cost near the
    # cheap tier's.
    inv = extract(path, model=STRONG)
    if not valid(inv):
        raise NeedsHumanReview(path, inv)
    return inv, STRONG

# Blended cost at a 90/10 split, per 10,000 invoices:
#   9,000 x $0.00248 + 1,000 x $0.00497
#   = $27.29

La comprobación aritmética hace el trabajo real: es la única señal independiente de la propia opinión del modelo sobre sí mismo. Un documento cuyas partidas suman el total indicado casi nunca contiene un error relevante; uno que falla merece el modelo más potente o una persona, independientemente de lo que afirmara el campo de confianza.

Coste por factura, por modelo

Una factura de una página ≈ 1.800 tokens de entrada (enviada como imagen) + ~350 tokens de salida de JSON:

ModeloPor facturaPor 10.000 facturasUsar para
claude-haiku-4-5$0.00248$24.80Predeterminado: facturas impresas limpias y ejecuciones masivas más baratas
claude-sonnet-5$0.00497$49.70Nivel de escalado: escaneos, escritura a mano y fallos

Las tarifas están actualizadas en la página de precios (aproximadamente un 30% por debajo de los precios de lista de los proveedores) y las solicitudes fallidas no se facturan. Con un enrutamiento de dos niveles — todo pasa por Haiku y los fallos de la comprobación aritmética se escalan a Sonnet — 10.000 facturas al mes normalmente cuestan menos de $27.29.

Precisión: la lista de comprobación que te lleva a producción

  1. Primero el esquema. Marca como required los campos que realmente sean obligatorios y permite null en todos los demás: forzar un valor fuerza una alucinación.
  2. Envía la imagen, no el texto OCR. El diseño transmite significado (columnas y recuadros de totales); los modelos de visión lo leen directamente. Recurre al texto solo para PDF nacidos digitales en los que puedas extraer una capa de texto limpia.
  3. Valida la aritmética en el código. Partidas + impuesto = total detecta gratis la mayoría de los errores de extracción.
  4. Escala, no reintentes a ciegas. Los fallos pasan a Sonnet con el error de validación incluido en el prompt; los fallos persistentes pasan a una cola humana.
  5. Usa few-shot con los casos límite reales. Dos o tres ejemplos de tus peores diseños (abonos y múltiples divisas) en el prompt del sistema superan cualquier cantidad de ajuste de instrucciones.

Más allá de las facturas

El mismo patrón restringido por esquema cubre recibos, órdenes de compra, albaranes, formularios aduaneros y extractos bancarios: cambia el esquema y conserva las protecciones. Explora el caso de uso más amplio de extracción de datos estructurados para patrones adyacentes a RAG, o la guía de optimización de costes para estrategias de enrutamiento cuando crezca el volumen. Una clave de Kunavo cubre todos los niveles de modelos utilizados aquí: regístrate gratis y crea una clave para ejecutar el fragmento tal cual.

Preguntas frecuentes

¿Necesito un modelo de aprendizaje profundo para extraer datos de facturas?

Ya no. La canalización clásica —OCR y después un modelo de diseño como LayoutLM ajustado con miles de facturas etiquetadas— ha sido sustituida para la mayoría de los equipos por una sola llamada a un LLM de visión: envía la imagen de la factura junto con un esquema JSON y recibe datos estructurados validados. No hay conjunto de entrenamiento que etiquetar, ningún modelo que alojar y los cambios de diseño que rompían los modelos ajustados se gestionan sin ejemplos previos.

¿Cómo se compara la extracción de facturas mediante aprendizaje automático con el uso de un LLM?

Los extractores clásicos de aprendizaje automático (CRF o SVM sobre la salida de OCR, más características de posición y fuente diseñadas manualmente) y sus sucesores de aprendizaje profundo (LayoutLM, Donut) compran precisión con datos etiquetados: miles de facturas anotadas por implementación, que deben volver a etiquetarse cuando cambian los diseños. Un LLM de visión pagó ese coste durante el preentrenamiento, por lo que lee sin ejemplos previos el diseño de un proveedor desconocido y devuelve JSON restringido por esquema en una sola llamada de API. El aprendizaje automático sigue ganando con un único diseño congelado de volumen muy alto cuando domina el coste de inferencia; para la cola larga de facturas reales, el LLM es más preciso en la práctica y mucho más barato de mantener.

¿Todavía puedo usar LayoutLM o Donut para extraer facturas?

Sí, y siguen siendo razonables cuando procesas millones de documentos con un único diseño estable y puedes amortizar el etiquetado y el servicio en GPU. Lo que cambió es el valor predeterminado: para un proyecto nuevo, un LLM de visión alcanza una precisión de producción en una tarde sin conjunto de entrenamiento, y el coste por documento es lo bastante pequeño como para que la economía del ajuste rara vez compense. Un camino intermedio habitual es ejecutar primero el LLM y ajustar solo más adelante, utilizando las propias salidas aceptadas del LLM como corpus etiquetado.

¿Cuánto cuesta extraer facturas por documento?

Una factura de una página contiene aproximadamente 1.800 tokens de entrada (como imagen) más ~350 tokens de salida en JSON. En Kunavo, eso cuesta aproximadamente $0.00248 por factura con claude-haiku-4-5 y $0.00497 con claude-sonnet-5; es decir, incluso con la calidad de Sonnet, mil facturas cuestan unos pocos dólares. Las solicitudes fallidas no se facturan.

¿Qué modelo debo usar para extraer datos de facturas?

Empieza con claude-haiku-4-5: gestiona facturas impresas limpias casi perfectamente al precio más bajo. Dirige solo los fallos —escritura manuscrita, escaneos de baja resolución, diseños poco comunes o documentos que no superen tu comprobación aritmética— a claude-sonnet-5. Este enrutamiento en dos niveles suele mantener más del 90 % del volumen en el nivel económico.

¿Cómo garantizo que la salida sea JSON válido?

Usa salidas estructuradas: pasa response_format con un esquema JSON y el modelo Claude queda restringido a emitir exactamente esa estructura, sin procesamiento posterior mediante expresiones regulares. Añade una protección económica a nivel de aplicación: comprueba que las partidas más los impuestos equivalgan al total y envía las discrepancias al modelo más potente o a una cola humana.

¿Puedo extraer datos de facturas PDF y no solo de imágenes?

Sí: renderiza cada página del PDF a PNG (por ejemplo, con pdf2image) y envíala como se indicó arriba. En los PDF digitales también puedes extraer la capa de texto y enviarla como texto sin formato, lo que es más barato; conserva la ruta de imagen para los escaneos.

¿Qué precisión tiene la extracción de facturas mediante LLM?

En facturas impresas limpias, la extracción visual restringida por esquema con la protección aritmética supera habitualmente el 98 % de precisión a nivel de campo, y, a diferencia de un modelo ajustado, mantiene la precisión en diseños que nunca ha visto. Mídela con tus propios documentos: 100 facturas etiquetadas constituyen un conjunto de evaluación sólido.

¿Se utilizan mis datos de facturas para entrenar modelos?

No: las solicitudes realizadas mediante Kunavo se transmiten a los proveedores de modelos bajo condiciones de uso de API, no de aplicaciones de consumo, y no se utilizan para entrenar modelos. Consulta la documentación sobre facturación y datos para conocer los detalles de retención.