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:

  1. conserva una única fuente versionada para JSON Schema;
  2. cambia solo el wrapper, no el significado;
  3. valida la respuesta completada;
  4. ejecuta reglas de negocio deterministas después de la estructura;
  5. 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.