ai-machine-learning

JSON fiable desde cualquier LLM: patrones con Pydantic + Zod para 2026

Escrito por Mert Batur
Actualizado May 12, 2026
17 lectura
JSON fiable desde cualquier LLM: patrones con Pydantic + Zod para 2026

La salida estructurada de LLM es el mecanismo que garantiza que la respuesta de un modelo de lenguaje se ajuste a un esquema predefinido -- no solo JSON válido, sino JSON válido según el esquema con exactamente los campos, tipos y restricciones que especificaste. Todos los principales proveedores ahora lo soportan de forma nativa, y ha cambiado cómo se construyen las aplicaciones LLM en producción.

Resumen Rápido: Salidas Estructuradas de un Vistazo

Si tienes poco tiempo, aquí está el panorama en 2026:

AspectoDetalles
Qué esRespuestas de LLMs con esquema forzado -- estructura garantizada, no "mejor esfuerzo"
Quién lo soportaOpenAI, Anthropic, Gemini, Cohere, xAI (Grok), más local vía Ollama/vLLM
Mecanismo claveDecodificación restringida -- los tokens inválidos se enmascaran antes del muestreo
Modo JSON vs. Modo EstrictoModo JSON = solo sintaxis válida. Modo Estricto = cumplimiento completo del esquema
Biblioteca PythonPydantic (BaseModel + Field) para definición de esquema
Biblioteca TypeScriptZod (z.object + .describe) para definición de esquema
Mejor enfoque inicialOpenAI con Pydantic o Zod vía SDK nativo
Mejor biblioteca de producciónInstructor (Python) o SDK nativo (TypeScript)
Mayor trampaPoner el campo de razonamiento DESPUÉS del campo de respuesta -- el modelo decide antes de pensar
Overhead de latencia50-200ms en la primera llamada (compilación del esquema), en caché después

Ahora desglosemos cada parte.

¿Qué Son las Salidas Estructuradas de LLM?

La salida estructurada es la diferencia entre esperar que un LLM devuelva JSON válido y garantizarlo. Cuando activas la salida estructurada, el modelo físicamente no puede producir tokens que violen tu esquema. Defines un JSON Schema (o modelo Pydantic, o esquema Zod), lo pasas a la API, y recibes una respuesta que coincide con él en cada ocasión.

¿Por qué importa esto? Antes de las salidas estructuradas, los desarrolladores escribían frágiles analizadores de regex, envolvían cada llamada LLM en bloques try/catch JSON.parse, y aún así tenían que lidiar con respuestas "casi correctas" -- JSON válido al que le faltaba un campo o tenía el tipo incorrecto. Toda esa clase de errores ha desaparecido.

Hay tres niveles de imposición de estructura, y representan una evolución clara:

  1. Ingeniería de prompts -- "Por favor devuelve JSON con estos campos." Poco confiable. El modelo podría cumplir el 80-90% del tiempo.
  2. Modo JSON -- Garantiza JSON sintácticamente válido, pero no impone tu esquema. Podrías obtener {"foo": "bar"} cuando esperabas {"name": string, "age": number}.
  3. Modo Estricto / Decodificación restringida -- Garantiza 100% de cumplimiento del esquema. El modelo literalmente no puede generar tokens inválidos. Esto es lo que significa "salida estructurada" en 2026.

Desde principios de 2026, OpenAI, Anthropic y Google Gemini todos soportan salida estructurada nativa. El ecosistema ha convergido.

Veredicto: Si estás analizando respuestas LLM con regex o JSON.parse en producción, lo estás haciendo de la manera difícil. La salida estructurada nativa elimina toda esa clase de fallos.

Modo JSON vs. Modo Estricto: ¿Qué Cambió Realmente?

Esta distinción confunde a muchos desarrolladores porque los nombres suenan similares. No lo son.

CaracterísticaModo JSONModo Estricto (Salidas Estructuradas)
Parámetro APItype: "json_object"type: "json_schema" con strict: true
Garantiza JSON válido
Garantiza cumplimiento del esquemaNo
MecanismoSesgo de token post-hocDecodificación restringida (FSM)
Puede devolver campos inesperadosNo
Puede omitir campos requeridosNo
Imposición de tiposNingunaCompleta (string, number, array, etc.)
Cuándo usarNo tienes un esquema de antemanoTodo en producción

La cronología: OpenAI introdujo el Modo JSON a finales de 2023. Fue un paso adelante, pero los desarrolladores rápidamente se dieron cuenta de que "JSON válido" no era suficiente -- necesitaban JSON válido según el esquema. En agosto de 2024, OpenAI lanzó Salidas Estructuradas con Modo Estricto, que usa decodificación restringida para garantizar el cumplimiento del esquema. Para 2025-2026, todos los principales proveedores habían adoptado el mismo enfoque.

El Modo JSON todavía tiene un caso de uso estrecho: cuando genuinamente no conoces la forma de la respuesta de antemano y solo quieres algún JSON válido para exploración no estructurada. Pero eso es raro en producción.

Veredicto: Usa el Modo Estricto para todo en producción. El Modo JSON está efectivamente obsoleto para casos de uso vinculados a esquemas. Si tienes un esquema (y deberías tenerlo), usa type: "json_schema" con strict: true.

¿Cómo Funciona Realmente la Decodificación Restringida?

Aquí está el mecanismo que hace posible el 100% de cumplimiento del esquema -- no 99,9%, sino literalmente 100%.

Cuando envías un JSON Schema a un proveedor con el Modo Estricto activado, el esquema se compila en una máquina de estados finitos (FSM). Esta FSM representa cada camino válido a través de tu esquema. En cada paso de generación de tokens, el motor de inferencia verifica qué tokens mantendrían la salida en un camino válido y cuáles no. Los tokens inválidos tienen sus logits establecidos en infinito negativo antes del muestreo, lo que significa que tienen probabilidad cero de ser seleccionados.

<!-- IMAGE: constrained-decoding diagram showing FSM token masking during structured output generation -->

Piénsalo como el autocompletado con esteroides. Si el modelo acaba de producir {"rating": y tu esquema dice que rating es un entero, los únicos tokens permitidos a continuación son tokens de dígitos. Comillas, letras, corchetes -- todo enmascarado. El modelo no puede generar "cinco" aunque "quiera" hacerlo.

Este es el mismo mecanismo central utilizado por XGrammar (el motor detrás de vLLM, SGLang y la mayoría de servidores de inferencia local) y Outlines (la biblioteca Python de código abierto para generación restringida). Los proveedores de API simplemente lo han integrado en su infraestructura de inferencia.

Hay un intercambio a conocer: la primera solicitud con un nuevo esquema incurre en un golpe de latencia de compilación (típicamente 50-200ms) mientras se construye la FSM. Las solicitudes posteriores con el mismo esquema usan una FSM en caché y añaden un overhead prácticamente nulo. También hay una consideración de calidad sutil -- restringir el vocabulario de tokens puede ocasionalmente reducir la calidad de salida para campos creativos o de forma libre, así que mantén tus esquemas enfocados en datos verdaderamente estructurados.

Veredicto: La decodificación restringida es lo que separa "generalmente funciona" de "siempre funciona." Es la ingeniería que hace que las salidas estructuradas estén listas para producción.

Implementación Multi-Proveedor: OpenAI, Anthropic y Gemini

Aquí hay algo que ninguna de las otras guías te muestra: la misma tarea de extracción implementada en los tres principales proveedores. Extraeremos una reseña de producto estructurada a partir de texto no estructurado.

El esquema Pydantic (compartido entre todos los proveedores):

python
from pydantic import BaseModel, Field
from typing import Literal

class ProductReview(BaseModel):
    reasoning: str = Field(description="Think through the review before scoring")
    rating: int = Field(description="Rating from 1-5", ge=1, le=5)
    sentiment: Literal["positive", "negative", "neutral"]
    pros: list[str] = Field(description="Key positive points")
    cons: list[str] = Field(description="Key negative points")
    summary: str = Field(description="One-sentence summary")

Implementación OpenAI

python
from openai import OpenAI

client = OpenAI()

response = client.beta.chat.completions.parse(
    model="gpt-4o-2024-08-06",
    messages=[
        {"role": "system", "content": "Extract a structured review from the text."},
        {"role": "user", "content": review_text}
    ],
    response_format=ProductReview,  # Modelo Pydantic directamente
)

review = response.choices[0].message.parsed  # Objeto ProductReview tipado

La implementación de OpenAI es la más madura. El método parse() acepta un modelo Pydantic directamente y devuelve un objeto tipado. Una restricción: el Modo Estricto de OpenAI soporta un subconjunto de JSON Schema -- sin $ref, anyOf limitado, y todos los campos deben ser requeridos con additionalProperties: false.

Implementación Anthropic

python
from anthropic import Anthropic

client = Anthropic()

response = client.messages.create(
    model="claude-sonnet-4-5-20250514",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": f"Extract a structured review:\n\n{review_text}"}
    ],
    output_config={
        "format": {
            "type": "json_schema",
            "json_schema": ProductReview.model_json_schema()
        }
    }
)

import json
review_data = json.loads(response.content[0].text)
review = ProductReview(**review_data)

La salida estructurada nativa de Anthropic usa output_config.format con un JSON Schema. Alcanzó disponibilidad general a principios de 2026. Anthropic también soporta el patrón más antiguo de definir una herramienta "falsa" y extraer vía tool_use -- eso todavía funciona, pero la salida estructurada nativa es más limpia para extracción pura.

Implementación Gemini

python
from google import genai

client = genai.Client()

response = client.models.generate_content(
    model="gemini-2.0-flash",
    contents=f"Extract a structured review:\n\n{review_text}",
    config={
        "response_mime_type": "application/json",
        "response_schema": ProductReview,  # Modelo Pydantic directamente
    }
)

import json
review = ProductReview(**json.loads(response.text))

Gemini soporta modelos Pydantic directamente en el SDK de Python vía response_schema. Una característica única: Gemini respeta propertyOrdering en el esquema, por lo que puedes controlar el orden de salida de los campos (útil para el patrón razonamiento-primero).

Comparación de Proveedores

CaracterísticaOpenAIAnthropicGemini
Parámetro APIresponse_formatoutput_config.formatresponse_schema
Entrada de esquemaPydantic o JSON SchemaJSON SchemaPydantic o JSON Schema
Modo estrictostrict: trueImplícito con json_schemaImplícito
StreamingSí (JSON parcial)
Manejo de rechazoscampo message.refusalRespuesta de errorRespuesta de error
Alternativa tool-useSí (método original)
Caché de compilación de esquemaSí (del lado del servidor)
Ordenamiento de propiedadesSin soporte nativoNoSí (propertyOrdering)

Veredicto: OpenAI tiene la DX más pulida con su método parse(). Anthropic ofrece los modelos subyacentes más capaces. El ordenamiento de propiedades de Gemini es únicamente útil. Los tres cumplen la tarea -- elige según tu relación existente con el proveedor.

Patrones Pydantic para Desarrolladores Python

Pydantic es el estándar de facto para definir esquemas de salida estructurada en Python. Aquí están los patrones que importan.

Esquema Básico con Descripciones

python
from pydantic import BaseModel, Field
from typing import Literal, Optional

class ExtractedEntity(BaseModel):
    reasoning: str = Field(description="Think step by step about the entity")
    name: str = Field(description="Full name of the entity")
    entity_type: Literal["person", "company", "location"]
    confidence: float = Field(description="Confidence score 0.0-1.0", ge=0.0, le=1.0)
    context: Optional[str] = Field(description="Surrounding context, if relevant")

Esas cadenas description no son solo para documentación -- se convierten en parte del JSON Schema enviado al modelo e influyen directamente en lo que el modelo genera. Piénsalas como ingeniería de prompts dentro del esquema.

Modelos Anidados

python
class Address(BaseModel):
    street: str
    city: str
    country: str
    postal_code: Optional[str] = None

class Company(BaseModel):
    reasoning: str = Field(description="Analysis of the company details")
    name: str
    industry: Literal["tech", "finance", "healthcare", "retail", "other"]
    headquarters: Address  # Modelo anidado
    key_products: list[str] = Field(description="Top 3 products or services")

Mantén el anidamiento a un máximo de 2-3 niveles. Los esquemas profundamente anidados aumentan las tasas de error y ralentizan la compilación del esquema.

El Patrón Razonamiento-Primero

Este es el patrón de diseño de esquema de mayor impacto. Pon un campo reasoning antes de tus campos de respuesta:

python
# Malo -- el modelo se compromete con una respuesta antes de pensar
class ClassificationBad(BaseModel):
    category: Literal["spam", "ham"]
    confidence: float

# Bueno -- el modelo razona primero a través del problema
class ClassificationGood(BaseModel):
    reasoning: str = Field(description="Analyze the text before classifying")
    category: Literal["spam", "ham"]
    confidence: float = Field(ge=0.0, le=1.0)

Los LLMs generan tokens de izquierda a derecha. Si category viene primero, el modelo elige una categoría y luego la racionaliza. Si reasoning viene primero, el modelo trabaja el problema y luego se compromete con una categoría. Es chain-of-thought horneado en el esquema.

Exportación de JSON Schema

python
# Genera el JSON Schema para cualquier modelo Pydantic
schema = ProductReview.model_json_schema()
# Pasa esto a cualquier proveedor que acepte JSON Schema crudo

Veredicto: Pydantic + campos descriptivos + orden razonamiento-primero es el trío Python de salidas estructuradas. Domina estos tres patrones y manejarás el 90% de los casos de uso.

Patrones Zod para Desarrolladores TypeScript

Zod es el equivalente TypeScript de Pydantic -- y es igual de central para los flujos de trabajo de salida estructurada.

Esquema Básico con Descripciones

typescript
import { z } from "zod";

const ProductReview = z.object({
  reasoning: z.string().describe("Think through the review before scoring"),
  rating: z.number().int().min(1).max(5),
  sentiment: z.enum(["positive", "negative", "neutral"]),
  pros: z.array(z.string()).describe("Key positive points"),
  cons: z.array(z.string()).describe("Key negative points"),
  summary: z.string().describe("One-sentence summary"),
});

// Inferir el tipo TypeScript automáticamente
type ProductReview = z.infer<typeof ProductReview>;

Al igual que Field(description=...) de Pydantic, .describe() de Zod se convierte en parte del JSON Schema y guía la salida del modelo.

Integración con el SDK Node de OpenAI

typescript
import OpenAI from "openai";
import { zodResponseFormat } from "openai/helpers/zod";

const client = new OpenAI();

const response = await client.beta.chat.completions.parse({
  model: "gpt-4o-2024-08-06",
  messages: [
    { role: "system", content: "Extract a structured review." },
    { role: "user", content: reviewText },
  ],
  response_format: zodResponseFormat(ProductReview, "product_review"),
});

const review = response.choices[0].message.parsed; // ¡Tipado!

Integración con el SDK Vercel AI

typescript
import { generateObject } from "ai";
import { openai } from "@ai-sdk/openai";

const { object: review } = await generateObject({
  model: openai("gpt-4o"),
  schema: ProductReview,
  prompt: `Extract a structured review:\n\n${reviewText}`,
});
// review está completamente tipado como ProductReview

El SDK Vercel AI usa Zod de forma nativa con generateObject(), convirtiéndolo en la integración TypeScript más limpia. Funciona con OpenAI, Anthropic, Gemini y otros proveedores a través de una API unificada.

Conversión de JSON Schema

typescript
import { zodToJsonSchema } from "zod-to-json-schema";

const jsonSchema = zodToJsonSchema(ProductReview);
// Usar con cualquier proveedor que acepte JSON Schema crudo

Veredicto: Zod + .describe() + el SDK Vercel AI es el stack TypeScript de salidas estructuradas. Si estás en el ecosistema Node/Next.js, este es el camino de menor resistencia.

Salida Estructurada vs. Llamada de Función: ¿Cuándo Usar Cada Una?

Esta es una de las fuentes de confusión más comunes. Ambas involucran esquemas, ambas devuelven datos estructurados -- pero resuelven problemas diferentes.

La salida estructurada dice: "Dame datos en esta forma exacta." Es para extracción, clasificación y formato. Estás extrayendo información estructurada de texto no estructurado.

La llamada de función (uso de herramientas) dice: "Aquí hay acciones que puedes tomar -- decide cuál ejecutar y proporciona los argumentos." Es para flujos de trabajo de agentes donde el modelo elige entre múltiples herramientas y activa acciones.

La confusión tiene sentido históricamente. La "salida estructurada" original de Anthropic era literalmente una llamada de función -- definías una herramienta falsa llamada extract_review y obtenías los argumentos. Eso todavía funciona, pero la salida estructurada nativa es más simple para extracción pura.

EscenarioMejor EnfoquePor Qué
Extraer datos de textoSalida estructuradaDirecto, menor latencia, esquema único
Clasificar en categoríasSalida estructuradaUna respuesta, un esquema
Agente decidiendo qué herramienta llamarLlamada de funciónEl modelo elige entre múltiples herramientas
Orquestación multi-pasoLlamada de funciónInvocaciones de herramientas secuenciales
Extraer datos Y decidir la próxima acciónAmbasSalida estructurada para extracción, llamada de función para orquestación

La salida estructurada impulsa los pipelines de llamada de herramientas en los sistemas de agentes de IA. Consulta nuestra guía de agentes de IA para negocios para ver cómo encajan en los flujos de trabajo de producción.

Veredicto: Usa salida estructurada cuando sabes qué forma deben tener los datos. Usa llamada de función cuando el modelo necesita elegir una acción. En la práctica, la mayoría de las aplicaciones usan ambas -- salida estructurada para extracción de datos y llamada de función para orquestación de agentes.

Patrones de Producción: Errores, Reintentos y Streaming

Hacer que las salidas estructuradas funcionen en una demostración es fácil. Mantenerlas confiables en producción requiere manejar tres cosas: rechazos, fallos de validación y streaming.

Manejo de Rechazos

A veces un modelo rechaza generar tu salida solicitada -- típicamente porque los filtros de seguridad marcaron la entrada. Cuando esto sucede, las APIs de salida estructurada no devuelven tu esquema. Devuelven un rechazo.

python
response = client.beta.chat.completions.parse(
    model="gpt-4o-2024-08-06",
    messages=messages,
    response_format=ProductReview,
)

# SIEMPRE verifica el rechazo antes de acceder al contenido analizado
if response.choices[0].message.refusal:
    print(f"Model refused: {response.choices[0].message.refusal}")
else:
    review = response.choices[0].message.parsed

Si omites la verificación de rechazo e intentas acceder a .parsed en un rechazo, obtendrás None y un error posterior confuso. Verifica primero, siempre.

Patrones de Reintento con Retroalimentación de Validación

El cumplimiento del esquema está garantizado por la decodificación restringida, pero la corrección semántica no. El modelo podría devolver {"rating": 1, "sentiment": "positive"} -- esquema válido, contenido contradictorio. Ahí es donde entran la validación + los reintentos.

python
import instructor

client = instructor.from_openai(OpenAI())

# Instructor maneja los reintentos automáticamente
review = client.chat.completions.create(
    model="gpt-4o",
    response_model=ProductReview,
    max_retries=3,  # Reintentos con retroalimentación de error de validación
    messages=[
        {"role": "user", "content": review_text}
    ],
)

Instructor retroalimenta el error de validación al modelo en el reintento, para que pueda autocorregirse. Para patrones de reintento manual sin Instructor:

python
from pydantic import ValidationError

for attempt in range(3):
    try:
        response = client.beta.chat.completions.parse(
            model="gpt-4o-2024-08-06",
            messages=messages,
            response_format=ProductReview,
        )
        review = response.choices[0].message.parsed
        # Ejecuta validación semántica adicional aquí
        break
    except ValidationError as e:
        messages.append({"role": "assistant", "content": str(response)})
        messages.append({"role": "user", "content": f"Validation error: {e}. Fix it."})

Streaming de Salida Estructurada

Para respuestas estructuradas grandes -- arrays largos, muchos campos, objetos anidados complejos -- el streaming te permite renderizar resultados parciales progresivamente.

python
import instructor

client = instructor.from_openai(OpenAI())

# Streamear resultados parciales a medida que los campos se llenan
review_stream = client.chat.completions.create_partial(
    model="gpt-4o",
    response_model=ProductReview,
    messages=[{"role": "user", "content": review_text}],
)

for partial_review in review_stream:
    # Los campos se llenan uno por uno a medida que los tokens llegan en streaming
    if partial_review.summary:
        print(f"Summary so far: {partial_review.summary}")

Una trampa: los fragmentos de streaming individuales no son schema-válidos por sí solos. El campo reasoning podría estar lleno mientras rating todavía sea None. Planifica tu UI en consecuencia -- muestra un estado de carga para los campos no llenos.

Veredicto: Las verificaciones de rechazo son no negociables. Los reintentos con retroalimentación de validación capturan errores semánticos. El streaming vale la pena para cualquier respuesta que tome más de un par de segundos.

Comparación de Bibliotecas de Salida Estructurada

Puedes usar salidas estructuradas a través de APIs nativas, pero las bibliotecas añaden validación, reintentos, streaming y soporte multi-proveedor. Aquí está el panorama.

Instructor es la opción más popular con 11K+ estrellas en GitHub y 3M+ descargas mensuales. Envuelve OpenAI, Anthropic, Gemini, Cohere, Ollama y más con una interfaz unificada basada en Pydantic. Características clave: reintentos automáticos con retroalimentación de validación, streaming vía create_partial(), y configuración simple (instructor.from_openai(client)). Si eres un equipo Python, empieza aquí.

BAML adopta un enfoque diferente: esquema-primero vía un DSL personalizado. Defines esquemas en archivos .baml y auto-generas clientes para Python, TypeScript, Ruby y más. Su algoritmo SAP (schema-aligned parsing) maneja elegantemente las salidas desordenadas del modelo. Mejor para equipos multi-lenguaje o cuando quieres contratos entre tu capa LLM y la capa de aplicación. Desventaja: paso de construcción adicional y nueva sintaxis que aprender.

LangChain ofrece .with_structured_output(schema) para salida estructurada independiente del proveedor. Conveniente si ya estás en el ecosistema LangChain. Desventaja: es una dependencia pesada, y la abstracción puede ocultar características específicas del proveedor que podrías necesitar.

APIs nativas -- llamadas directas con response_format / output_config -- no requieren dependencias más allá del SDK del proveedor. Obtienes control total y visibilidad total. Mejor para casos de uso simples o equipos que prefieren abstracción mínima.

BibliotecaLenguajesProveedoresReintentos AutoStreamingEstrellas GitHubCurva de Aprendizaje
InstructorPython, TS15+11K+Baja
BAMLPython, TS, Ruby, GoTodos (DSL-agnóstico)7K+Media
LangChainPython, TS20+Parcial100K+Media-Alta
APIs nativasCualquiera1 por SDKNoN/ABaja

Elegir la biblioteca de salida estructurada correcta es parte de una decisión más amplia sobre el stack de IA. Desglosamos el stack completo en nuestra guía Best AI Stack para SaaS.

Consulta nuestras Mejores Bibliotecas para Salidas Estructuradas de LLM [próximamente] para una comparación en profundidad de Instructor, BAML, Mirascope y más.

Veredicto: Empieza con Instructor para Python, APIs nativas para TypeScript. Muévete a BAML si necesitas contratos de esquema entre lenguajes. Evita LangChain solo para salidas estructuradas -- es excesivo.

Mejores Prácticas de Diseño de Esquemas (y Errores Comunes)

El diseño de tu esquema impacta directamente la calidad de la salida. Aquí están los patrones que importan y los errores que cuestan precisión.

Poner el Razonamiento Antes que las Respuestas

Cubrimos esto en la sección de Pydantic, pero vale la pena repetirlo porque es la decisión de diseño de mayor impacto:

python
# Antes: el modelo adivina la respuesta, luego la racionaliza
class Bad(BaseModel):
    answer: str
    reasoning: str

# Después: el modelo piensa primero, luego se compromete
class Good(BaseModel):
    reasoning: str = Field(description="Think step by step")
    answer: str

Los LLMs generan de izquierda a derecha. El orden de los campos es el orden del prompt. Razonamiento primero significa que el modelo debe trabajar el problema antes de comprometerse con una respuesta.

La Tabla de Anti-Patrones

ErrorProblemaSolución
Campo de razonamiento después de la respuestaEl modelo decide antes de pensarMover el razonamiento antes de la respuesta
Profundamente anidado (4+ niveles)Mayor tasa de error, compilación más lentaAplanar a 2-3 niveles
Sin descripciones de camposEl modelo adivina lo que quieresAñadir .describe() / Field(description=...)
Manejo de nulos faltanteEl modelo alucina un valor para llenar el campoUsar Optional / .nullable()
Esquemas demasiado grandes (50+ campos)Timeout de compilación, degradación de calidadDividir en múltiples llamadas
Opciones de enum vagasEl modelo elige la categoría incorrectaUsar opciones específicas y no superpuestas

Manejar los Nulos Explícitamente

Si un campo podría no tener datos en el texto fuente, hazlo opcional. Forzar un campo requerido cuando los datos no existen conduce a alucinaciones:

python
class PersonInfo(BaseModel):
    name: str  # Siempre presente
    email: Optional[str] = Field(None, description="Email if mentioned, null otherwise")
    phone: Optional[str] = Field(None, description="Phone if mentioned, null otherwise")

Mantener los Esquemas Enfocados

Un esquema por tarea. No intentes extraer todo en un solo esquema masivo. Si necesitas 50+ campos, divide en múltiples llamadas de extracción. El Modo Estricto de OpenAI tiene límites prácticos en la complejidad del esquema, y aunque funcione, los esquemas muy grandes degradan la calidad de la salida.

Veredicto: Razonamiento-primero, campos descriptivos, nulos explícitos y esquemas enfocados. Haz bien estos cuatro y tu precisión en salidas estructuradas sube de manera medible.

Salida Estructurada con LLMs Locales

No necesitas un proveedor de API para salidas estructuradas. Los motores de inferencia local lo soportan a través de decodificación restringida basada en gramática -- el mismo mecanismo fundamental, ejecutándose en tu propio hardware.

Ollama

El camino más fácil para salidas estructuradas locales. Ollama acepta un JSON Schema vía el parámetro format:

python
import ollama
from pydantic import BaseModel

class Country(BaseModel):
    name: str
    capital: str
    languages: list[str]

response = ollama.chat(
    model="llama3.2",
    messages=[{"role": "user", "content": "Tell me about Japan."}],
    format=Country.model_json_schema(),
)

import json
country = Country(**json.loads(response.message.content))

Ollama usa XGrammar internamente para la decodificación restringida. La misma garantía que los proveedores de API: 100% de cumplimiento del esquema.

vLLM y SGLang

Para inferencia local de calidad producción, vLLM y SGLang ambos soportan salidas estructuradas a través de los parámetros guided_json y guided_regex. XGrammar es el backend predeterminado, entregando un overhead prácticamente nulo en la generación de JSON -- hasta 3,5x más rápido que los motores de gramática alternativos.

Outlines

Outlines es la biblioteca Python de código abierto que pionó la generación restringida basada en gramática. Funciona con cualquier modelo de Hugging Face y soporta restricciones de JSON Schema, regex y gramática libre de contexto completa (CFG/EBNF). También está integrada en vLLM y SGLang como opción de backend de gramática.

La diferencia clave con los proveedores de API: la salida estructurada local no tiene limitaciones de subconjunto de esquema. Controlas completamente la gramática. Pero la calidad del modelo varía más -- un modelo local de 7B parámetros no igualará a GPT-4o o Claude en tareas de extracción complejas. El esquema siempre será válido; la calidad del contenido depende del modelo.

Veredicto: Ollama para desarrollo, vLLM/SGLang con XGrammar para producción. Las salidas estructuradas locales son suficientemente maduras para la mayoría de los casos de uso, con la advertencia de que los modelos más pequeños producen contenido de menor calidad dentro del esquema.

Preguntas Frecuentes

¿Qué es la salida estructurada en LLMs?

La salida estructurada es un mecanismo que garantiza que la respuesta de un LLM se ajuste a un JSON Schema predefinido. A diferencia del texto plano o incluso el Modo JSON, la salida estructurada usa decodificación restringida para asegurar que cada campo, tipo y restricción en tu esquema se cumple -- el 100% del tiempo, no "generalmente".

¿Cuál es la diferencia entre el Modo JSON y las Salidas Estructuradas?

El Modo JSON garantiza JSON sintácticamente válido pero no impone tu esquema -- podrías obtener cualquier objeto JSON válido. Las Salidas Estructuradas (Modo Estricto) garantizan cumplimiento completo del esquema a través de la decodificación restringida. Usa el Modo Estricto para producción; el Modo JSON solo es relevante cuando no tienes un esquema de antemano.

¿Qué proveedores LLM soportan salidas estructuradas de forma nativa?

OpenAI (desde agosto de 2024), Google Gemini (2024, expandido 2026), Anthropic (beta noviembre 2025, GA principios de 2026), Cohere y xAI (Grok) todos soportan salidas estructuradas nativas. En el lado local, Ollama, vLLM y SGLang las soportan a través de decodificación restringida basada en gramática.

¿Cómo garantiza la decodificación restringida el cumplimiento del esquema?

El JSON Schema se compila en una máquina de estados finitos (FSM). En cada paso de generación de tokens, solo se permiten tokens que mantengan la salida en un camino válido a través de la FSM -- los tokens inválidos tienen sus logits establecidos en infinito negativo. Esto significa que los tokens inválidos tienen probabilidad cero de ser generados, dándote una garantía matemática, no estadística.

¿Debo usar salida estructurada o llamada de función?

Usa salida estructurada para extracción y clasificación -- cuando quieres datos en una forma específica. Usa llamada de función para flujos de trabajo de agentes -- cuando el modelo necesita decidir qué acción tomar. Muchas aplicaciones de producción usan ambas: salida estructurada para extracción de datos y llamada de función para orquestación.

¿Puedo hacer streaming de salidas estructuradas?

Sí. OpenAI soporta streaming con el método parse(), e Instructor proporciona create_partial() para hacer streaming de modelos Pydantic que se llenan campo por campo. Ten en cuenta que los fragmentos de streaming individuales no son individualmente schema-válidos -- los campos se llenan incrementalmente.

¿Qué es la biblioteca Instructor?

Instructor es la biblioteca de salida estructurada más popular (11K+ estrellas en GitHub, 3M+ descargas mensuales). Envuelve los SDKs de proveedores con validación basada en Pydantic, reintentos automáticos con retroalimentación de validación y soporte de streaming. Funciona con OpenAI, Anthropic, Gemini, Cohere, Ollama y 10+ otros proveedores.

¿Funciona la salida estructurada con LLMs locales?

Sí. Ollama soporta salidas estructuradas vía el parámetro format con JSON Schema. vLLM y SGLang las soportan a través de los parámetros guided_json. Los tres usan XGrammar u Outlines para la decodificación restringida. La garantía de cumplimiento del esquema es la misma que los proveedores de API; la calidad del contenido depende del modelo.

¿Cuáles son los errores comunes en el diseño de esquemas?

Los principales errores: poner el campo de razonamiento después del campo de respuesta (el modelo decide antes de pensar), esquemas profundamente anidados (4+ niveles aumentan errores), descripciones de campos faltantes (el modelo adivina la intención), sin manejo de nulos para datos opcionales (fuerza alucinaciones), y esquemas demasiado grandes (50+ campos degradan la calidad).

¿La salida estructurada añade latencia?

Hay un overhead de compilación del esquema en la primera solicitud -- típicamente 50-200ms mientras se construye la FSM. Las solicitudes posteriores con el mismo esquema usan una FSM en caché y añaden una latencia prácticamente nula. Para la mayoría de las aplicaciones, esto es insignificante comparado con el tiempo total de inferencia del modelo.

¿Puedo usar salidas estructuradas con imágenes o entradas multimodales?

Sí. La salida estructurada se aplica al formato de respuesta, no a la entrada. Puedes enviar una imagen a GPT-4o o Gemini con un esquema de salida estructurada y obtener de vuelta un análisis schema-conforme de la imagen. Esto es poderoso para flujos de trabajo de extracción visual -- extrayendo datos estructurados de recibos, formularios o imágenes de productos.

Fuentes

Etiquetas

llm salida estructuradastructured outputsjson schemapydanticzodopenaianthropicgemini

Compartir este artículo

Inicia Tu Proyecto

¿Listo para construir algo extraordinario?

Convirtamos tu visión en realidad. Nuestro equipo está listo para ayudarte a crear software que marque la diferencia.