Comparación de salida estructurada entre proveedores populares de LLM - OpenAI, Gemini, Anthropic, Mistral y AWS Bedrock

Las APIs ligeramente diferentes requieren un enfoque especial.

Índice

Aquí tienes una comparación de soporte lado a lado de salida estructurada (obtener JSON confiable) entre proveedores populares de LLM, más ejemplos mínimos en Python

barras colorizadas de pie

Ya hemos visto cómo solicitar salida estructurada del LLM alojado en Ollama. Aquí revisamos cómo solicitar lo mismo de otros proveedores.

TL;DR matriz

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

Salida estructurada de LLM - Información general

La salida estructurada de LLM se refiere a la capacidad de los modelos de lenguaje grandes (LLMs) para generar respuestas que estrictamente se adhieran a un formato o estructura predefinida, 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áquinas, coherentes y fácilmente analizables por software para su uso en diversas aplicaciones.

Las salidas estructuradas difieren de las salidas tradicionales de LLM, que normalmente generan texto natural abierto. En cambio, las salidas estructuradas imponen un esquema o formato, como objetos JSON con claves y tipos de valor 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 confiabilidad, reduce errores y alucinaciones, y simplifica la integración en sistemas como bases de datos, APIs o flujos de trabajo.

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

  • Especificar instrucciones detalladas en el prompt para guiar al modelo para que produzca la salida en el formato deseado.
  • Usar 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 máquinas de estado finito para asegurar el cumplimiento del formato a nivel de token.

Ventajas de las salidas estructuradas de LLM incluyen:

  • Legibilidad por máquinas y facilidad de integración.
  • Reducción de variabilidad y errores.
  • Mejor 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 salida estructurada en Python

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

1) OpenAI — Esquema JSON (estricto)

from openai import OpenAI
import json

client = OpenAI()

schema = {
    "name": "Evento",
    "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": "Extrae el evento de: 'PyData Sydney es el 2025-11-03 en Darling Harbour.'"}
    ],
    response_format={"type": "json_schema", "json_schema": schema},
)

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

La función Salidas Estructuradas de OpenAI impone 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

# Configura con tu clave API
# genai.configure(api_key="tu-clave-api")

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="Extrae el evento de: 'PyData Sydney es el 2025-11-03 en Darling Harbour.'",
    generation_config=genai.GenerationConfig(
        response_mime_type="application/json",
        response_schema=schema,
    ),
)

print(resp.text)  # ya es JSON válido según el esquema

Gemini devolverá JSON estricto que se ajusta a response_schema.


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

from anthropic import Anthropic
import json

client = Anthropic()

tool = {
    "name": "extract_event",
    "description": "Devolver detalles del evento.",
    "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"},  # forzar esquema
    messages=[{"role": "user", "content":
        "Extrae el evento de: 'PyData Sydney es el 2025-11-03 en Darling Harbour.'"}],
)

# Claude "llamará" a la herramienta; obtén los argumentos (que coinciden con tu esquema)
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 de “modo JSON” general; en su lugar, Uso de Herramienta con un input_schema te da argumentos validados con forma de esquema (y puedes forzar su uso).


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

from mistralai import Mistral
import json

client = Mistral()

resp = client.chat.complete(
    model="mistral-large-latest",
    messages=[{"role":"user","content":
        "Devuelve JSON con claves título, fecha (YYYY-MM-DD), ubicación para: "
        "'PyData Sydney es el 2025-11-03 en Darling Harbour.'"}],
    response_format={"type": "json_object"}  # garantiza JSON válido
)

data = json.loads(resp.choices[0].message.content)
print(data)
# Consejo: valora `data` contra tu esquema Pydantic/JSON localmente.

El json_object de Mistral impone forma JSON (no tu esquema exacto) — valora en el lado del cliente.


5) AWS Bedrock — esquema de herramienta de API Converse (modelo-agnostic)

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"}},  # forzar esquema
    messages=[{"role":"user","content":[{"text":
        "Extrae el evento de: 'PyData Sydney es el 2025-11-03 en Darling Harbour.'"}]}],
)

# Extraer contenido de uso de herramienta
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 entrada de herramienta contra tu esquema JSON y muchos modelos alojados (por ejemplo, Claude) lo admiten 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 valora localmente (por ejemplo, con Pydantic).

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

from pydantic import BaseModel, ValidationError

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

try:
    evento = Evento.model_validate(data)  # `data` de cualquier proveedor
except ValidationError as e:
    # manejar / reintentar / pedir al modelo que lo corrija con e.errors()
    print(e)

Enlaces útiles