El modo JSON no es salida estructurada
Se suelen confundir dos funciones. El modo JSON pide al modelo un JSON sintácticamente válido; no promete que las claves, los tipos o los valores permitidos coincidan con lo que espera tu código. La salida estructurada va más allá: proporcionas un JSON Schema y el proveedor restringe o valida la generación para que la respuesta lo cumpla. Cuando el proveedor aplica el esquema durante la decodificación, no puede aparecer un campo ausente ni un valor de enumeración desconocido, lo que elimina toda una familia de errores de análisis.
Ninguna de las dos funciones dice nada sobre si el contenido es correcto. Una respuesta válida según el esquema puede elegir la etiqueta equivocada con total seguridad. Trata el esquema como una garantía sobre la forma y reserva la evaluación y la revisión para la decisión en sí.
Un esquema para una decisión de clasificación
Los ejemplos de esta página usan el mismo esquema pequeño: una etiqueta restringida por una enumeración y un motivo breve. La enumeración es la restricción más útil para clasificar, porque convierte un campo de texto abierto en una lista cerrada sobre la que tu código puede decidir. Mantén additionalProperties en false y marca todos los campos como obligatorios; varios proveedores necesitan ambas cosas para aplicar el modo estricto.
{
"type": "object",
"properties": {
"label": { "type": "string", "enum": ["billing", "technical support", "account access", "other"] },
"reason": { "type": "string" }
},
"required": ["label", "reason"],
"additionalProperties": false
}OpenAI y Gemini
En OpenAI, Structured Outputs se pide en Chat Completions con response_format de tipo json_schema, un nombre, el esquema y strict en true; la API Responses recibe el mismo esquema en text.format. El modo estricto admite un subconjunto documentado de JSON Schema, exige que todas las propiedades figuren como obligatorias y que additionalProperties sea false. Los SDK de Python y JavaScript pueden generar el esquema a partir de un modelo Pydantic o un objeto Zod y analizar el resultado. Cuando el modelo rechaza una solicitud por seguridad, la respuesta llega como refusal y no como salida del esquema, así que trata ese caso de forma explícita. El antiguo modo json_object solo garantiza JSON válido.
Gemini recibe response_mime_type application/json junto con un response_schema en la configuración de generación; el formato es un subconjunto del objeto de esquema de OpenAPI y las versiones recientes de la API también aceptan JSON Schema estándar. Para clasificación pura Gemini ofrece un atajo: response_mime_type text/x.enum con un esquema de enumeración devuelve exactamente una de las cadenas permitidas, sin envoltorio JSON. Los esquemas muy grandes o muy anidados pueden rechazarse, así que mantén planos los esquemas de clasificación.
Guía de Structured Outputs de OpenAI · Guía de salida estructurada de Gemini
Anthropic, LangChain y Ollama
Con los modelos de Anthropic, el patrón establecido es el uso de herramientas: defines una herramienta cuyo input_schema es tu JSON Schema, la fuerzas con tool_choice indicando esa herramienta y lees su entrada como resultado estructurado. Consulta la documentación actual de Anthropic para tus modelos, porque se han añadido nuevas opciones de salida estructurada junto al uso de herramientas.
LangChain envuelve estas funciones en una sola llamada: with_structured_output acepta una clase Pydantic, un TypedDict o un JSON Schema y devuelve objetos analizados. Su argumento method elige entre llamada a funciones, modo JSON y JSON Schema nativo cuando el proveedor lo admite. Con include_raw=True recibes el mensaje original y cualquier error de análisis junto al valor analizado, que es lo que conviene en producción en lugar de una excepción que pierde la respuesta original.
Ollama acepta un campo format en sus endpoints de chat y generación. El valor json pide cualquier JSON válido; un objeto JSON Schema completo restringe el modelo local a ese esquema. Ollama recomienda describir también la estructura esperada en el prompt y usar una temperatura baja, porque los modelos locales pequeños siguen los esquemas con menos fiabilidad que los grandes modelos alojados.
# OpenAI Chat Completions
response_format = {
"type": "json_schema",
"json_schema": { "name": "ticket_label", "strict": True, "schema": SCHEMA }
}
# Gemini (google-genai SDK)
config = { "response_mime_type": "application/json", "response_schema": TicketLabel }
# LangChain (any supported chat model)
labeller = llm.with_structured_output(TicketLabel, include_raw=True)
# Ollama /api/chat
{ "model": "llama3.1", "messages": [...], "format": SCHEMA, "stream": false }Documentación de salida estructurada de LangChain · Salidas estructuradas en Ollama
Lo que la salida estructurada no resuelve
Un esquema no puede decir al modelo cómo elegir entre dos etiquetas que se solapan, ni expresar que un mensaje es demasiado ambiguo para clasificarlo si no añades una etiqueta para ese caso. Tampoco produce una confianza calibrada: un campo llamado confianza que rellena el modelo es solo otro valor generado, salvo que el proveedor devuelva probabilidades de tokens o que midas tú mismo la concordancia.
Tampoco incluye tu política de revisión. Algo en tu aplicación tiene que decidir qué resultados se aplican automáticamente, cuáles ve una persona y cómo se registran las correcciones. Cuanta más lógica de ese tipo reconstruyes alrededor de una llamada directa al modelo, más cerca estás de escribir tu propio servicio de decisiones.
Cuándo una API de decisiones es más sencilla
Si la tarea consiste en elegir una etiqueta de una lista, Jev API Pro recibe el texto, tus instrucciones y las etiquetas permitidas, y devuelve una de ellas con un valor de confianza cuando el modelo lo proporciona, además de una marca de revisión si el resultado queda por debajo de tu umbral. No mantienes código de esquemas por proveedor, ni gestión de rechazos, ni un analizador. Si necesitas extraer libremente muchos campos, la salida estructurada de un proveedor es la mejor herramienta, y la comparación anterior te indica qué campo configurar.
Prueba una clasificación en el área de pruebas · Clasifica texto desde Python
