Structured Outputs con APIs OpenAI-compatible: guía JSON Schema
Guía práctica para usar JSON Schema estricto con Responses y Chat Completions, validar resultados y comprobar compatibilidad de modelos y rutas.
Structured Outputs permite pedir JSON que cumpla un esquema definido, en lugar de limitarse a indicar al modelo que «devuelva JSON». En una API OpenAI-compatible, el mismo JSON Schema puede utilizarse con Responses o Chat Completions, pero cambia el wrapper y es obligatorio verificar el modelo y la ruta reales.
El patrón seguro de producción tiene tres capas: usar un esquema strict cuando exista soporte, volver a parsear y validar el JSON en la aplicación y, por último, aplicar reglas de negocio deterministas. Cumplir el esquema reduce errores de formato; no demuestra que la respuesta sea correcta en términos semánticos o factuales.
Structured Outputs no es lo mismo que JSON mode
| Método | JSON válido | Obliga a cumplir el esquema | Uso habitual |
|---|---|---|---|
| Solo Prompt | No está garantizado | No | Prototipos que toleran fallos de parsing |
| JSON mode | Sí, si está soportado y la generación termina | No | JSON flexible validado por la aplicación |
Structured Outputs con strict: true |
Sí, gestionando finalización y refusals | Sí, dentro del subconjunto soportado | Extracción tipada y workflows de aplicación |
La guía de Structured Outputs de OpenAI recomienda este método frente a JSON mode cuando el modelo y el endpoint lo soportan. Un formato de respuesta estructurada sirve para devolver un objeto predecible; Function Calling sirve para solicitar una acción a la aplicación. Pueden compartir ideas de Schema, pero no el contrato de Call IDs y resultados.
Define un esquema antes de elegir endpoint
Para clasificar tickets de soporte se necesitan cuatro campos:
{
"type": "object",
"properties": {
"category": { "type": "string", "enum": ["billing", "technical", "account"] },
"priority": { "type": "integer", "minimum": 1, "maximum": 3 },
"requires_human": { "type": "boolean" },
"summary": { "type": "string" }
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
}
Marcar todas las propiedades como required y usar additionalProperties: false crea un objeto estable. No convierte todos los keywords de JSON Schema en portables: OpenAI documenta un subconjunto y cada proveedor OpenAI-compatible puede implementar otro o no disponer de Strict Schema Mode.
Enviar el esquema con Responses API
Responses coloca el esquema dentro de text.format:
{
"model": "<MODEL_WITH_VERIFIED_STRUCTURED_OUTPUT_SUPPORT>",
"input": "The customer was charged twice and wants a refund.",
"text": {
"format": {
"type": "json_schema",
"name": "support_ticket",
"schema": {
"type": "object",
"properties": {
"category": { "type": "string", "enum": ["billing", "technical", "account"] },
"priority": { "type": "integer", "minimum": 1, "maximum": 3 },
"requires_human": { "type": "boolean" },
"summary": { "type": "string" }
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
},
"strict": true
}
}
}
Envía el objeto a POST https://modelflare.dev/v1/responses con Authorization: Bearer <YOUR_API_KEY>. Responses devuelve contenido en output items tipados. Un SDK puede ofrecer un helper de texto o parsed output, pero un cliente HTTP debe localizar el texto completado y parsearlo; no debe asumir un objeto tipado en la raíz.
Enviar el mismo esquema con Chat Completions
Chat Completions usa response_format.json_schema:
{
"model": "<MODEL_WITH_VERIFIED_STRUCTURED_OUTPUT_SUPPORT>",
"messages": [
{ "role": "user", "content": "The customer was charged twice and wants a refund." }
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "support_ticket",
"schema": {
"type": "object",
"properties": {
"category": { "type": "string", "enum": ["billing", "technical", "account"] },
"priority": { "type": "integer", "minimum": 1, "maximum": 3 },
"requires_human": { "type": "boolean" },
"summary": { "type": "string" }
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
},
"strict": true
}
}
}
El JSON suele llegar como string en choices[0].message.content; todavía hay que parsearlo.
| Objetivo | Responses API | Chat Completions |
|---|---|---|
| Contenedor | text.format |
response_format.json_schema |
| Tipo | text.format.type |
response_format.type |
| Nombre | text.format.name |
response_format.json_schema.name |
| Cuerpo | text.format.schema |
response_format.json_schema.schema |
| Strict | text.format.strict |
response_format.json_schema.strict |
| Resultado | Output items / helper | choices[0].message.content |
La capa de compatibilidad de Modelflare convierte estos wrappers en rutas OpenAI/Codex soportadas, pero no puede añadir Structured Outputs a un modelo upstream que no lo implemente. En otras familias que funcionan como Chat Completions pass-through, el contrato del proveedor sigue siendo la fuente de verdad.
Validar estructura y significado por separado
{
"category": "billing",
"priority": 2,
"requires_human": true,
"summary": "Customer reports a duplicate charge and requests a refund."
}
Validación estructural
Un validador JSON Schema debe confirmar propiedades required, ausencia de propiedades inesperadas, tipos, enum y rangos. Incluso con Strict Adherence, la validación de aplicación protege frente a rutas sin soporte, errores de integración, contenido truncado y cambios futuros.
Validación semántica y de negocio
El esquema no sabe si existió un cobro duplicado, si la prioridad es correcta ni quién puede aprobar un reembolso. En workflows de impacto, trata el objeto como clasificación propuesta, compáralo con registros autoritativos y aplica permisos y límites financieros en código determinista. Un JSON válido nunca debe eludir autorización, billing o seguridad.
Gestionar salidas incompletas y excepcionales
Refusals
Comprueba Status y la representación del refusal antes de buscar JSON. Una negativa de seguridad no es un error de parsing.
Truncamiento y límites
Si la generación se detiene antes del cierre, el esquema no recupera el contenido perdido. Revisa Completion Status y Stop Reason y asigna un Output Limit suficiente para el mayor objeto válido.
Keywords no soportados
Empieza con Objects, Arrays, Primitive Types, Enums, Required y additionalProperties. Consulta documentación antes de usar References, Recursion, Unions complejas o validadores avanzados.
Latencia del primer esquema
Algunos proveedores preprocesan y cachean un esquema nuevo. Reutiliza esquemas estables y versionados y separa la primera llamada de las métricas de steady state.
Incompatibilidad de modelo o ruta
Un endpoint puede aceptar Chat Completions normal y rechazar json_schema, ignorar strict o enviarlo a un modelo sin soporte. Un 200 con JSON no demuestra Strict Adherence; prueba inputs inválidos y extremos.
Ejecutar una matriz de compatibilidad
| Prueba | Evidencia |
|---|---|
| Objeto mínimo required | Status, model, route, objeto parseado y resultado de validación |
| Cada enum | Producción y parsing de todos los valores |
| Información ausente | Valor limitado o solicitud de aclaración |
| Input de seguridad | Representación del refusal y manejo |
| Output Limit bajo | Completion Status y truncamiento |
| Keyword no soportado | Error explícito o restricción ignorada |
| Wrappers Responses y Chat | Objetos equivalentes para la aplicación |
| Esquema repetido | Tiempo inicial frente a steady state |
No atribuyas diferencias al modelo si también cambian Schema, Prompt, Region, Streaming Mode u Output Limit. Registra la fecha de la prueba.
Elegir endpoint después de probar el workflow
Usa Responses si la aplicación ya trabaja con output items tipados, streaming events o un workflow amplio de Tools. Usa Chat Completions si existe una integración estable basada en messages y la ruta soporta response_format. Consulta Responses API vs Chat Completions.
En ambos casos:
- conserva una única fuente versionada para JSON Schema;
- cambia solo el wrapper, no el significado;
- valida la respuesta completada;
- ejecuta reglas de negocio deterministas después de la estructura;
- monitoriza parsing, refusal, truncation y compatibilidad por separado.
Para migrar un cliente, empieza por la guía OpenAI-Compatible API. Para eventos incompletos, consulta la guía de streaming de AI API. Comprueba Modelos y precios y prueba Structured Outputs en la ruta exacta que utilizará la API Key.
Structured Outputs convierte una respuesta de modelo en una interfaz más predecible, pero no sustituye la verificación factual, la autorización ni la lógica de facturación. «OpenAI-compatible» sigue siendo una afirmación que debe probarse función por función.