// blog

El truco para que un modelo de lenguaje sea confiable: dejar de pedirle texto

La diferencia entre una demo que impresiona y una automatización que aguanta producción está en tres decisiones: esquema de salida, validación determinista y una cola de revisión humana.

4 min de lectura
IAAutomatizaciónArquitectura

La demo siempre funciona. Le pasas una factura en PDF al modelo, le pides que extraiga los datos, y devuelve algo asombrosamente correcto. El cliente se entusiasma. Se aprueba el proyecto.

Tres semanas después, en producción, aparece un proveedor que factura en dos monedas, otro que pone el IVA desglosado por línea, y un tercero cuyo PDF es en realidad una foto escaneada torcida. Ahí es donde la mayoría de los proyectos de IA se caen.

La diferencia no está en el modelo. Está en tres decisiones de diseño.

1. Nunca pidas texto libre

Este es el error que más veces he visto:

Extrae los datos de esta factura y devuélvelos en formato JSON.

El modelo devolverá JSON. Casi siempre. Hasta que un día devuelva JSON envuelto en un bloque de código con una explicación arriba, o use "total" en lugar de "monto_total", o decida que un campo vacío se representa con "N/A" en vez de null.

La solución es declarar un esquema y forzar al modelo a cumplirlo:

extraer-factura.ts
const facturaSchema = {
  type: "object",
  required: ["ruc_emisor", "fecha", "subtotal", "iva", "total", "moneda"],
  properties: {
    ruc_emisor: { type: "string", pattern: "^[0-9]{13}$" },
    fecha:      { type: "string", format: "date" },
    subtotal:   { type: "number" },
    iva:        { type: "number" },
    total:      { type: "number" },
    moneda:     { type: "string", enum: ["USD", "EUR"] },
    lineas: {
      type: "array",
      items: {
        type: "object",
        required: ["descripcion", "cantidad", "precio_unitario"],
        properties: {
          descripcion:     { type: "string" },
          cantidad:        { type: "number" },
          precio_unitario: { type: "number" },
        },
      },
    },
  },
} as const;

Con un esquema, la salida deja de ser una cadena que hay que parsear con esperanza y pasa a ser un objeto tipado. El campo pattern del RUC y el enum de la moneda ya descartan una familia entera de errores antes de que lleguen a tu base de datos.

2. Valida con código, no con más IA

Cuando el modelo devuelve algo que no cuadra, la tentación es pedirle que lo revise: "verifica que los totales sean correctos". Es una mala idea. Estás usando un componente probabilístico para validar la salida de otro componente probabilístico, y duplicando el costo.

La aritmética se valida con aritmética:

function validar(factura: Factura) {
  const errores: string[] = [];
 
  const sumaLineas = factura.lineas.reduce(
    (total, linea) => total + linea.cantidad * linea.precio_unitario,
    0,
  );
 
  // Tolerancia de un centavo por redondeos del emisor.
  if (Math.abs(sumaLineas - factura.subtotal) > 0.01) {
    errores.push("El detalle no suma el subtotal declarado");
  }
 
  if (Math.abs(factura.subtotal + factura.iva - factura.total) > 0.01) {
    errores.push("Subtotal + IVA no coincide con el total");
  }
 
  if (new Date(factura.fecha) > new Date()) {
    errores.push("La fecha de emisión es futura");
  }
 
  return errores;
}

Esta función tiene veinte líneas, cuesta cero y atrapa la inmensa mayoría de los errores reales de extracción. Un modelo alucinando un número casi nunca produce un conjunto de números que además cuadre entre sí.

3. Diseña la cola de revisión desde el día uno

La pregunta correcta no es "¿el modelo acierta?". Es "¿qué pasa cuando no acierta?".

El flujo que uso tiene tres salidas:

  1. Pasa todas las validaciones → se carga automáticamente al sistema.
  2. Falla alguna validación → va a una cola de revisión, con el documento original al lado y el campo problemático resaltado.
  3. El modelo no pudo extraer campos obligatorios → va a la cola marcado como no procesable.

El caso 2 es el importante. Una persona revisando veinte documentos dudosos con el campo señalado tarda minutos. Esa misma persona transcribiendo doscientos documentos desde cero tarda un día.

Y la cola de revisión genera algo valioso: cada corrección humana es un caso de prueba real. Guárdalos.

Mide antes de tocar el prompt

Con veinte o treinta documentos corregidos ya tienes un conjunto de evaluación. Antes de subir cualquier cambio de prompt a producción, lo corres contra ese conjunto y comparas el acierto campo por campo:

ruc_emisor        28/30   93%
fecha             30/30  100%
total             29/30   97%
lineas.cantidad   24/30   80%   ← aquí está el trabajo

Sin esto, cambiar un prompt es una apuesta: mejoras el caso que te reportaron ayer y rompes tres que ya funcionaban, y nadie se entera hasta el cierre de mes.

Lo que queda

Un sistema de extracción serio termina siendo, en proporción, poco código de IA:

  • La llamada al modelo con su esquema: ~20 líneas
  • Las validaciones deterministas: ~80 líneas
  • La cola de revisión y su interfaz: ~300 líneas
  • Las evaluaciones y sus casos: ~150 líneas

El modelo es la pieza más pequeña. Todo lo demás es lo que hace que se pueda confiar en él — y es exactamente la parte que las demos no muestran.

Si esto te resultó útil o tienes un caso parecido en tu empresa, escríbeme: me gusta conversar sobre estos problemas aunque no termine en un proyecto.