La mayoría de las funcionalidades con LLM de los productos reales no terminan con texto en una pantalla. Terminan con datos: la categoría de un ticket, campos extraídos de una factura, una lista de tareas pendientes, argumentos para una llamada a una API. Para eso, "responde en JSON, por favor" no basta. Incluso una tasa de fallos de análisis del 1% significa cientos de peticiones rotas al día con una escala modesta. Esta guía cubre los mecanismos que hacen fiable la salida estructurada, cómo diseñar esquemas que los modelos rellenen correctamente y cómo validar y recuperarse cuando algo sigue fallando.

Tres formas de obtener salida estructurada

Enfoque Cómo funciona Fiabilidad
Solo prompting "Responde con JSON que siga este ejemplo" Buena con los modelos actuales, pero no garantizada
Llamadas a herramientas / funciones Defines una herramienta con un esquema JSON; el modelo devuelve los argumentos Alta; con amplio soporte
Salida estructurada / decodificación restringida La API restringe la generación para que la salida cumpla el esquema La más alta: sintácticamente garantizada

Los grandes proveedores ya admiten de forma nativa la salida restringida por esquema (structured outputs de OpenAI; structured outputs de Anthropic). Para modelos self-hosted, librerías como Outlines y servidores de inferencia como vLLM implementan la decodificación restringida enmascarando los tokens que violarían la gramática (Willard y Louf, 2023).

Usa la salida restringida siempre que puedas. Elimina toda una clase de fallos —JSON que no se puede analizar, campos obligatorios ausentes, tipos erróneos— y te permite centrarte en la parte difícil: si los valores son correctos.

La sintaxis está garantizada; la semántica, no

La decodificación restringida garantiza que la salida se puede analizar y cumple el esquema. No garantiza que el total de la factura sea el número correcto, que la categoría sea la adecuada ni que la fecha no sea inventada. Un modelo obligado a rellenar un campo obligatorio lo rellenará, aunque la información no esté en la entrada. Es lo más importante que hay que entender de la salida estructurada.

Diseña teniéndolo en cuenta:

  • Haz que la información ausente sea representable. Usa campos que admitan nulos o valores "unknown" explícitos en lugar de forzar una suposición.
  • Valida las reglas de negocio en código: los totales son iguales a la suma de las líneas, las fechas están en un rango plausible, los IDs existen en tu base de datos.
  • Evalúa la precisión de los valores con un conjunto etiquetado, como harías con cualquier otra funcionalidad con LLM; consulta evals de LLM.

Un diseño de esquemas que los modelos rellenan bien

El esquema forma parte del prompt. Los modelos leen los nombres de los campos, las descripciones y los valores de los enums. Los buenos esquemas siguen unas pocas reglas:

  1. Nombres de campo descriptivos. invoice_total_with_vat es mejor que amt2.
  2. Descripciones en todos los campos no obvios, incluidas unidades y formatos: "fecha ISO 8601", "importe en EUR, decimal, sin símbolo de moneda".
  3. Enums para conjuntos cerrados de valores, con una opción "other" cuando el mundo es más desordenado que tu lista.
  4. Plano mejor que muy anidado. Dos niveles de anidamiento están bien; cinco son propensos a errores.
  5. Ordena los campos para favorecer el razonamiento. Pon un campo reasoning o evidence antes del campo de decisión si necesitas que el modelo justifique una clasificación; la generación va de izquierda a derecha.
  6. Evita los enums enormes. Para cientos de categorías, recupera primero los candidatos y dale al modelo una lista corta.
from enum import Enum
from pydantic import BaseModel, Field

class Category(str, Enum):
    billing = "billing"
    technical = "technical"
    account = "account"
    other = "other"

class TicketTriage(BaseModel):
    evidence: str = Field(description="Quote the sentence(s) that justify the category.")
    category: Category
    urgency: int = Field(ge=1, le=3, description="1 = low, 3 = service down for customer")
    customer_order_id: str | None = Field(
        default=None,
        description="Order ID like ORD-123456 if explicitly mentioned, otherwise null.",
    )

Validación y recuperación

Incluso con decodificación restringida, valida cada respuesta en el código de tu aplicación. Pydantic en Python y Zod en TypeScript permiten definir el esquema una vez y usarlo tanto para la petición a la API como para la validación:

import { z } from "zod"

const LineItem = z.object({
  description: z.string(),
  quantity: z.number().positive(),
  unitPrice: z.number().nonnegative(),
})

export const Invoice = z.object({
  supplierName: z.string(),
  supplierTaxId: z.string().regex(/^[A-Z0-9]{9}$/).nullable(),
  issueDate: z.string().date(),
  currency: z.enum(["EUR", "USD", "MXN"]),
  lines: z.array(LineItem).min(1),
  total: z.number().nonnegative(),
}).refine(
  (inv) => Math.abs(inv.lines.reduce((s, l) => s + l.quantity * l.unitPrice, 0) - inv.total) < 0.01,
  { message: "total does not match sum of lines" },
)

Cuando falla la validación, tienes opciones:

  1. Reintenta con el error. Envía al modelo su salida más el mensaje de validación: "el total no coincide con la suma de las líneas; revisa las partidas". Un reintento arregla la mayoría de los problemas; más de dos rara vez ayudan.
  2. Recurre a un modelo más potente para el elemento fallido.
  3. Deriva a una persona con el resultado parcial ya rellenado. En el procesamiento de documentos suele ser la opción correcta por defecto; consulta procesamiento de documentos con LLM.

Librerías como Instructor envuelven este bucle de validar y reintentar para muchos proveedores. El Vercel AI SDK ofrece generateObject con esquemas de Zod para TypeScript; lo uso en cómo crear un chat de IA en Next.js.

¿Forzar JSON perjudica la calidad?

Algunas investigaciones encontraron que las restricciones de formato estrictas pueden degradar el razonamiento en ciertas tareas frente a las respuestas libres (Tam et al., 2024). En la práctica, el efecto depende del modelo y de la tarea, y las implementaciones actuales de salida estructurada han reducido la diferencia. Dos mitigaciones funcionan bien:

  • Primero razona, después estructura. Deja que el modelo piense (razonamiento extendido, o un campo de razonamiento en texto libre colocado antes de los campos de la respuesta) y que después emita el resultado estructurado.
  • Pipelines en dos pasos para tareas difíciles: una llamada produce un análisis libre y una segunda llamada, más barata, extrae de él los campos estructurados.

Mide con tu propio conjunto de evals en lugar de suponer nada.

Las llamadas a herramientas como salida estructurada

Las llamadas a herramientas y la salida estructurada son dos caras del mismo mecanismo: el modelo emite argumentos que cumplen un esquema. Usa llamadas a herramientas cuando el modelo deba decidir si actúa y qué acción toma; usa salida estructurada cuando siempre quieras una forma concreta. En los agentes, el esquema de entrada de cada herramienta merece el mismo cuidado que los esquemas anteriores; consulta diseño de herramientas para agentes LLM.

Salida estructurada en streaming

En funcionalidades de cara al usuario, puede que quieras mostrar resultados parciales a medida que se generan: una lista de tareas que aparecen una a una, un formulario que se rellena poco a poco. Muchos SDK admiten el streaming de objetos parciales; el cliente recibe JSON incompleto que se "repara" para formar un objeto parcial válido. Valida solo el objeto final y diseña una interfaz que tolere campos que aparecen y cambian.

Consejos de rendimiento y coste

  • Los esquemas cuestan tokens. Los esquemas grandes con descripciones largas añaden tokens de entrada a cada llamada; como son estables, el prompt caching los abarata; consulta optimización de costes de LLM.
  • Latencia de la primera llamada. Algunos proveedores compilan el esquema en una gramática la primera vez que se usa; cuenta con un retraso puntual para los esquemas nuevos.
  • Extracción por lotes. Para trabajos offline (miles de documentos), las APIs por lotes reducen mucho los costes.
  • A menudo bastan modelos más pequeños para una extracción bien definida con buenos esquemas. Evalúa antes de recurrir por defecto al modelo más grande.

Medir la calidad de la extracción

La salida estructurada facilita la evaluación frente al texto libre, porque puedes comparar los campos directamente. Construye un conjunto etiquetado de 100–300 entradas reales y mide por campo:

Métrica Qué te dice
Precisión por campo Proporción de coincidencias exactas (o normalizadas) por campo
Precisión de los nulos Cuando el modelo devuelve null, ¿faltaba de verdad el valor?
Recall de los nulos Cuando faltaba el valor, ¿devolvió el modelo null en lugar de inventarlo?
Precisión a nivel de documento Proporción de entradas en las que todos los campos críticos son correctos
Tasa de fallos de validación Con qué frecuencia las reglas de negocio rechazan la salida

El recall de los nulos es la métrica que más equipos olvidan y la que revela los valores alucinados. La precisión a nivel de documento es la que importa para decidir qué automatizar: si el 92% de las facturas son totalmente correctas, puedes procesar automáticamente las que pasan la validación y enviar el resto a revisión.

Errores habituales

  • Campos obligatorios para información opcional, lo que provoca valores inventados.
  • Fiarse ciegamente de los enums. Una categoría con buena pinta puede seguir siendo errónea; mide la precisión por clase.
  • Sin validación de negocio. Que cumpla el esquema no significa que sea correcto.
  • Extraer JSON del markdown con expresiones regulares en 2026. Usa las funciones nativas de salida estructurada.
  • Un esquema gigante para todo. Divide la extracción en llamadas enfocadas cuando el esquema crezca más allá de lo que una persona podría rellenar de una vez.

Preguntas frecuentes

¿El modo JSON es lo mismo que las salidas estructuradas? No. El antiguo "modo JSON" garantiza JSON válido, pero no un esquema concreto. Las salidas estructuradas restringidas por esquema garantizan ambas cosas.

¿Y XML o YAML? JSON con esquema es la opción con mejor soporte. Las etiquetas XML siguen siendo útiles para las secciones de texto libre dentro de prompts y respuestas.

¿Cómo gestionamos listas muy largas? Pagina o divide la entrada, extrae por fragmento y fusiona en código. Las salidas largas aumentan la probabilidad de truncamiento y omisiones.

¿Podemos usar salida estructurada con modelos self-hosted? Sí. vLLM y servidores similares admiten decodificación guiada con esquemas JSON; consulta LLM self-hosted.

Fuentes

  1. OpenAI. Structured outputs y function calling.
  2. Anthropic. Structured outputs y tool use.
  3. Willard, B., Louf, R. (2023). Efficient Guided Generation for Large Language Models.
  4. Tam et al. (2024). Let Me Speak Freely? A Study on the Impact of Format Restrictions on Performance of Large Language Models.
  5. JSON Schema, Pydantic, Zod, Instructor.