Comparación de salida estructurada entre los principales proveedores de LLM: OpenAI, Gemini, Anthropic, Mistral y AWS Bedrock

Las APIs ligeramente diferentes requieren un enfoque especial.

Índice

Aquí tienes una comparación lado a lado del soporte de salida estructurada (obtención de JSON confiable) en los principales proveedores de LLM, junto con ejemplos mínimos en Python.

colorised bars standing

Ya hemos visto cómo solicitar salida estructurada del LLM alojado en Ollama. Una vez que el JSON está en la red, validación de salida estructurada de LLM en Python que es robusta recorre el análisis, las comprobaciones de Pydantic, los reintentos y las pruebas en tu servicio. Aquí revisamos cómo solicitar lo mismo a otros proveedores.

Matriz TL;DR

Proveedor Modo “JSON” nativo Aplicación de Esquema JSON Parámetro típico Notas
OpenAI Sí (de primera clase) response_format={"type":"json_schema", ...} Funciona a través de la API de Respuestas o Chat Completions; también puede hacer llamadas a funciones.
Google Gemini Sí (de primera clase) response_schema= + response_mime_type="application/json" Devuelve JSON estrictamente validado cuando se establece el esquema.
Anthropic (Claude) Indirecto Sí (vía Uso de Herramientas con esquema JSON) tools=[{input_schema=...}] + tool_choice Fuerza al modelo a “llamar” a tu herramienta definida por esquema; devuelve argumentos con forma de esquema.
Mistral Parcial (solo JSON; sin esquema del lado del servidor) response_format={"type":"json_object"} Garantiza JSON, pero validas contra tu esquema en el lado del cliente.
AWS Bedrock (plataforma) Varía según el modelo Sí (vía esquema de Herramienta/Converse) toolConfig.tools[].toolSpec.inputSchema La API Converse de Bedrock valida la entrada de la herramienta contra un esquema JSON.

Salida Estructurada de LLM: Información General

La salida estructurada de los LLM se refiere a la capacidad de los grandes modelos de lenguaje (LLMs) para generar respuestas que se adhieran estrictamente a un formato o estructura predefinida y específica, en lugar de producir texto libre. Esta salida estructurada puede estar en formatos como JSON, XML, tablas o plantillas, lo que hace que los datos sean legibles por máquina, consistentes y fácilmente analizables por software para su uso en diversas aplicaciones.

Las salidas estructuradas difieren de las salidas tradicionales de los LLM, que típicamente generan texto de lenguaje natural abierto. En cambio, las salidas estructuradas imponen un esquema o formato, como objetos JSON con claves y tipos de valores definidos, o clases específicas en la salida (por ejemplo, respuestas de opción múltiple, clases de sentimiento o formatos de filas de base de datos). Este enfoque mejora la fiabilidad, reduce los errores y las alucinaciones, y simplifica la integración en sistemas como bases de datos, APIs o flujos de trabajo.

La generación de salidas estructuradas en los LLM a menudo implica técnicas como:

  • Especificar instrucciones detalladas en el prompt para guiar al modelo a producir la salida en el formato deseado.
  • Utilizar herramientas de validación y análisis como Pydantic en Python para asegurar que la salida coincida con el esquema.
  • A veces, imponer restricciones de decodificación basadas en gramática o autómatas de estados finitos para garantizar el cumplimiento a nivel de token con el formato.

Los beneficios de las salidas estructuradas de los LLM incluyen:

  • Legibilidad por máquina y facilidad de integración.
  • Reducción de la variabilidad y los errores.
  • Mayor predictibilidad y verificabilidad para tareas que requieren formatos de datos consistentes.

Los desafíos incluyen diseñar esquemas efectivos, manejar datos anidados complejos y posibles limitaciones en las capacidades de razonamiento en comparación con la generación de texto libre.

En general, la salida estructurada permite que los LLM sean más útiles en aplicaciones que requieren datos precisos y formateados, en lugar de solo texto legible por humanos.

Ejemplos de Python para Salida Estructurada

Todos los fragmentos extraen información de evento como JSON: {título, fecha, ubicación}. Reemplaza las claves/modelos como desees.

1) OpenAI — Esquema JSON (estricto)

from openai import OpenAI
import json

client = OpenAI()

schema = {
    "name": "Event",
    "schema": {
        "type": "object",
        "properties": {
            "title": {"type": "string"},
            "date":  {"type": "string", "description": "YYYY-MM-DD"},
            "location": {"type": "string"}
        },
        "required": ["title", "date", "location"],
        "additionalProperties": False
    },
    "strict": True
}

resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "user", "content": "Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}
    ],
    response_format={"type": "json_schema", "json_schema": schema},
)

data = json.loads(resp.choices[0].message.content)
print(data)

La función Structured Outputs de OpenAI aplica este esquema en el lado del servidor.


2) Google Gemini — esquema de respuesta + MIME JSON

import google.generativeai as genai
from google.genai import types

# Configure with your API key
# genai.configure(api_key="your-api-key")

schema = types.Schema(
    type=types.Type.OBJECT,
    properties={
        "title": types.Schema(type=types.Type.STRING),
        "date": types.Schema(type=types.Type.STRING),
        "location": types.Schema(type=types.Type.STRING),
    },
    required=["title", "date", "location"],
    additional_properties=False,
)

resp = genai.generate_content(
    model="gemini-2.0-flash",
    contents="Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'",
    generation_config=genai.GenerationConfig(
        response_mime_type="application/json",
        response_schema=schema,
    ),
)

print(resp.text)  # already valid JSON per schema

Gemini devolverá JSON estricto que cumpla con response_schema.


3) Anthropic (Claude) — Uso de Herramientas con esquema JSON

from anthropic import Anthropic
import json

client = Anthropic()

tool = {
    "name": "extract_event",
    "description": "Return event details.",
    "input_schema": {
        "type": "object",
        "properties": {
            "title": {"type": "string"},
            "date": {"type": "string"},
            "location": {"type": "string"}
        },
        "required": ["title", "date", "location"],
        "additionalProperties": False
    }
}

msg = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=256,
    tools=[tool],
    tool_choice={"type": "tool", "name": "extract_event"},  # force schema
    messages=[{"role": "user", "content":
        "Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}],
)

# Claude will "call" the tool; grab the args (which match your schema)
tool_use = next(b for b in msg.content if b.type == "tool_use")
print(json.dumps(tool_use.input, indent=2))

Claude no tiene un interruptor genérico de “modo JSON”; en cambio, el Uso de Herramientas con un input_schema te proporciona argumentos validados con forma de esquema (y puedes forzar su uso).


4) Mistral — modo JSON (validación del lado del cliente)

from mistralai import Mistral
import json

client = Mistral()

resp = client.chat.complete(
    model="mistral-large-latest",
    messages=[{"role":"user","content":
        "Return JSON with keys title, date (YYYY-MM-DD), location for: "
        "'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}],
    response_format={"type": "json_object"}  # guarantees valid JSON
)

data = json.loads(resp.choices[0].message.content)
print(data)
# Tip: validate `data` against your Pydantic/JSON Schema locally.

El json_object de Mistral aplica la forma JSON (no tu esquema exacto) — valida en el lado del cliente.


5) AWS Bedrock — Esquema de Herramienta API Converse (agnóstico al modelo)

import boto3, json

bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
model_id = "anthropic.claude-3-5-sonnet-20240620-v1:0"

tools = [{
    "toolSpec": {
        "name": "extract_event",
        "inputSchema": {
            "json": {
                "type": "object",
                "properties": {
                    "title": {"type": "string"},
                    "date": {"type": "string"},
                    "location": {"type": "string"}
                },
                "required": ["title","date","location"],
                "additionalProperties": False
            }
        }
    }
}]

resp = bedrock.converse(
    modelId=model_id,
    toolConfig={"tools": tools},
    toolChoice={"tool": {"name": "extract_event"}},  # force schema
    messages=[{"role":"user","content":[{"text":
        "Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}]}],
)

# Pull toolUse content
tool_use = next(
    c["toolUse"] for c in resp["output"]["message"]["content"] if "toolUse" in c
)
print(json.dumps(tool_use["input"], indent=2))

Bedrock valida la entrada de la herramienta contra tu esquema JSON y muchos modelos alojados (por ejemplo, Claude) lo soportan a través de Converse.


Guía práctica y Validación

  • Si deseas las garantías más fuertes del lado del servidor: salidas estructuradas de OpenAI o esquema de respuesta de Gemini.
  • Si ya estás en Claude/Bedrock: define una Herramienta con un esquema JSON y fuerza su uso; lee los argumentos de la herramienta como tu objeto tipado.
  • Si usas Mistral: habilita json_object y valida localmente (por ejemplo, con Pydantic).

Patrón de validación (funciona para todos)

from pydantic import BaseModel, ValidationError

class Event(BaseModel):
    title: str
    date: str
    location: str

try:
    event = Event.model_validate(data)  # `data` from any provider
except ValidationError as e:
    # handle / retry / ask model to fix with e.errors()
    print(e)

Enlaces útiles

Suscribirse

Recibe nuevas publicaciones sobre sistemas, infraestructura e ingeniería de IA.