Salidas estructuradas en IA: cómo definir, validar y desplegar un contrato JSON
Imagen generada con gpt-image-2.5-sunburst para InferamaFuente ↗
01

La salida estructurada es un contrato, no una garantía de verdad

Una salida estructurada es una respuesta de un sistema de IA presentada de acuerdo con una forma de datos acordada, por ejemplo, un objeto JSON con campos, tipos y valores permitidos. En una aplicación, ese contrato permite que el resultado se analice y se envíe a código posterior sin tener que extraer datos de un párrafo de texto libre. No convierte, por sí mismo, el contenido en verdadero, completo ni apropiado para ejecutar una acción.

La distinción importa. El texto «la solicitud es para el martes» podría transformarse correctamente en una cadena con una fecha; pero la aplicación todavía debe saber a qué martes se refiere, si la persona proporcionó esa fecha y si el resultado es coherente con el resto de la solicitud. La estructura define cómo se representa una respuesta. La corrección de los datos depende además de la entrada, de la tarea y de las reglas de negocio.

Por tanto, conviene pensar en tres comprobaciones separadas: si la respuesta se puede leer como JSON; si satisface el esquema acordado; y si sus valores tienen sentido y están respaldados por la información disponible. Una respuesta puede pasar la primera comprobación y fallar las otras dos. También puede satisfacer un esquema y, sin embargo, ser una extracción equivocada.

Esta guía utiliza una extracción ficticia de solicitudes de vacaciones para mostrar el diseño. El ejemplo es una propuesta de implementación local, no una afirmación sobre el formato obligatorio de ninguna API. Las capacidades concretas de cada proveedor, sus nombres de parámetros, modelos compatibles y estados de error deben comprobarse en la documentación vigente del servicio elegido.

02

Decidir si necesitas estructura

Impón una salida estructurada cuando el resultado vaya a alimentar una interfaz, una búsqueda, una cola de trabajo, una regla automatizada o una base de datos, y cuando el conjunto de campos esperado se pueda describir de forma estable. Por ejemplo, una aplicación de gestión de solicitudes puede necesitar un estado, un tipo de petición y fechas identificadas de manera consistente. En ese caso, pedir texto libre y extraer campos después añade otra etapa de interpretación y más puntos de fallo.

No siempre hace falta. Si una persona solo quiere una explicación, una respuesta conversacional puede ser más clara que un objeto de datos. Si una transformación determinista basta —como normalizar espacios o convertir una fecha que ya tiene un formato inequívoco—, una función convencional puede ser más simple y reproducible que pedir al modelo que lo haga. Y si el problema todavía no tiene campos definidos, formalizar un esquema demasiado pronto puede ocultar que la tarea está mal planteada.

La decisión debe considerar también el coste del error. Si una respuesta activa pagos, modifica permisos, borra información o compromete una decisión sensible, el esquema es solo una pieza de control. Es posible que la aplicación necesite confirmación humana, permisos adicionales o una regla que impida ejecutar la acción hasta que se resuelva una ambigüedad. No es prudente delegar esas decisiones en una supuesta seguridad del formato.

Antes de diseñar el contrato, escribe quién consume cada campo y para qué. Si ningún componente usa un campo, quizá no deba formar parte de la salida. Si un componente requiere una precisión que la entrada no garantiza, define un estado de incertidumbre o una pregunta de aclaración, en vez de obligar al modelo a inventar un valor.

Una decisión inicial

NecesidadOpción inicialPrecaución
Respuesta destinada a una persona, sin procesamiento posteriorTexto libreUna estructura rígida puede dificultar la lectura sin aportar utilidad.
Campos previsibles que consumirá códigoContrato de datos y validación localEl formato no demuestra que los valores sean correctos.
Transformación exacta y deterministaCódigo convencionalNo añadas generación si una función pequeña resuelve el problema.
Entrada ambigua o insuficienteEstado de aclaración o revisiónNo fuerces valores ficticios para completar campos.
03

Definir un contrato que represente la tarea

Un contrato útil describe la forma y las condiciones de uso de cada campo. Empieza por identificar los campos mínimos, asignarles tipos y decidir cuáles son obligatorios. Después especifica los valores permitidos, la representación de datos ausentes y las relaciones entre campos. Finalmente, separa esas reglas del texto de instrucciones dirigido al modelo: la aplicación debe conservar una definición verificable que pueda probarse sin depender de lo que el modelo parezca haber entendido.

En la extracción de una solicitud de vacaciones, un contrato demasiado simple podría exigir siempre una fecha de inicio y una de fin. Pero una petición como «quisiera tomarme unos días el mes que viene» no permite determinar fechas exactas. Obligar a llenar las fechas empuja al sistema hacia una conjetura. Una alternativa es hacer explícitos los estados de la tarea: solicitud completa, aclaración necesaria o texto que no corresponde a una solicitud. Las fechas pueden estar vacías cuando no se conocen, y una pregunta puede explicar qué dato falta.

El siguiente esquema ilustra una decisión posible. La condición de que la fecha final no preceda a la inicial, y la relación entre el estado y los campos opcionales, se deben comprobar también en la aplicación. No des por supuesto que una modalidad de generación o un validador admiten todas las palabras clave que aparecen aquí: el esquema local y el formato aceptado por la API pueden no ser intercambiables. Si la interfaz remota admite solo un subconjunto, adapta la representación remota y conserva las comprobaciones completas en tu código.

La convención para la ausencia debe ser inequívoca. En este ejemplo, una fecha desconocida se representa como null, no como una cadena vacía, «desconocido» o una fecha inventada. Un único valor coherente facilita la validación y evita que consumidores diferentes interpreten de manera distinta el mismo caso. La aplicación también debe decidir si acepta campos extra o si los rechaza; permitirlos sin revisión puede ocultar errores de versión o salidas inesperadas.

Esquema local ilustrativo · json

Este esquema documenta una propuesta de contrato. No implica compatibilidad directa con la interfaz de generación de un proveedor.

{
  "type": "object",
  "additionalProperties": false,
  "required": ["status", "start_date", "end_date", "clarification_question"],
  "properties": {
    "status": {
      "type": "string",
      "enum": ["complete", "needs_clarification", "not_a_request"]
    },
    "start_date": {
      "type": ["string", "null"],
      "description": "Fecha ISO YYYY-MM-DD, solo si está respaldada por la solicitud"
    },
    "end_date": {
      "type": ["string", "null"],
      "description": "Fecha ISO YYYY-MM-DD, solo si está respaldada por la solicitud"
    },
    "clarification_question": {
      "type": ["string", "null"]
    }
  }
}
04

Separar formato, esquema y significado

La primera validación pregunta si los bytes recibidos pueden interpretarse como JSON. Si la respuesta contiene una coma final, texto añadido fuera del objeto o una comilla sin cerrar, el análisis puede fallar. La segunda pregunta si, una vez analizado, el valor tiene la forma permitida: un objeto, las claves esperadas y valores de los tipos apropiados. La tercera pregunta si la información representa correctamente la solicitud y respeta las reglas de la aplicación.

Por ejemplo, este objeto puede ser JSON válido y respetar los tipos básicos del contrato: status indica que la solicitud está completa, ambas fechas son cadenas y no hay pregunta de aclaración. Aun así, si la persona pidió vacaciones «del 12 al 10», la respuesta es incoherente. Del mismo modo, el rango puede ser internamente correcto pero no estar respaldado por el texto. Ninguna prueba de tipos sustituye la comprobación de correspondencia con la entrada.

Las reglas semánticas deben escribirse como condiciones explícitas y, cuando sea posible, como pruebas reproducibles. Para este ejemplo: si status es complete, las dos fechas deben existir y ser fechas calendario válidas; la fecha final no puede ser anterior a la inicial; si status es needs_clarification, debe describirse qué dato falta; y si status es not_a_request, no se debe iniciar el flujo de vacaciones. La política concreta puede variar, pero no debería quedar implícita en la interpretación del consumidor.

También se debe diferenciar una respuesta inválida de una respuesta válida que indica que la tarea no puede completarse. needs_clarification puede ser una respuesta correcta dentro del contrato, no un error técnico. Si la API comunica un rechazo, un error de generación o una interrupción en una forma propia, la aplicación debe tratar ese estado según lo documentado por el proveedor. No conviene convertir todas esas situaciones en un objeto aparentemente normal sin saber qué ocurrió.

05

Elegir una modalidad sin asumir compatibilidades

Algunas API ofrecen opciones específicas para solicitar salidas estructuradas, pero los nombres, requisitos y límites corresponden a cada producto y pueden cambiar. La documentación oficial de Amazon Nova trata el requerimiento de salida estructurada y formatos como JSON. La documentación de Anthropic describe salidas estructuradas con un esquema JSON y el parámetro output_config. Google documenta resultados estructurados para Gemini API y menciona esquemas JSON. OpenAI presenta su funcionalidad de salidas estructuradas en la API. Son referencias sobre productos concretos, no pruebas de que compartan el mismo contrato, el mismo nivel de restricción o la misma forma de gestionar fallos.

Microsoft Learn también tiene una página sobre salidas estructuradas para modelos de chat en la documentación identificada como Foundry clásico. Esa delimitación es relevante: una ruta de documentación asociada a un producto o modalidad no debe tratarse como una especificación universal de todos los servicios de Microsoft. Del mismo modo, documentación de Claude Code y de Claude Platform corresponde a productos diferentes; no se debe trasladar automáticamente una opción de uno a otro.

Antes de elegir, comprueba en la documentación primaria vigente qué modalidad se ofrece, qué modelos o versiones cubre, qué parte del esquema se acepta, qué sucede con campos opcionales y enumeraciones, cómo se representan los rechazos o las generaciones incompletas y qué estructura de respuesta recibe la aplicación. Registra la fecha y versión de la documentación consultada. Si un requisito de tu contrato no está especificado, trátalo como una incertidumbre y verifícalo con pruebas en el entorno correspondiente, no como una capacidad garantizada.

No confundas pedir JSON con exigir el cumplimiento de un esquema. Una modalidad que produce una respuesta con sintaxis JSON puede reducir trabajo de análisis, pero la aplicación sigue necesitando comprobar claves, tipos y reglas de tarea. Incluso cuando la documentación de una modalidad describa restricciones de esquema, mantén validación local: el contrato que protege el sistema consumidor debe verificarse en el límite de confianza de la aplicación.

Comprobación de compatibilidad antes de implementar

  1. 01Identifica la API, el producto, la versión y el modelo exactos que planeas usar.
  2. 02Consulta documentación oficial vigente para la modalidad de salida estructurada y sus restricciones documentadas.
  3. 03Compara los campos, tipos, opcionalidad, enumeraciones y reglas anidadas de tu contrato con lo que la interfaz admite.
  4. 04Busca cómo se describen los rechazos, errores, interrupciones y respuestas que no completan el formato solicitado.
  5. 05Prueba la integración en el entorno elegido y conserva la validación local, aunque la solicitud incluya restricciones de formato.
06

Implementar la validación en la aplicación

La aplicación debe analizar la respuesta en un punto controlado y validar antes de entregarla al siguiente componente. Una secuencia práctica es: recibir el resultado de la API; comprobar primero los estados de transporte y generación documentados; extraer el contenido pertinente; analizar JSON; verificar que la raíz sea un objeto con las claves admitidas; validar tipos y valores; y por último aplicar reglas de negocio. Si falla una etapa, el flujo debe conservar información suficiente para diagnosticar el problema sin actuar como si el resultado fuese correcto.

El ejemplo de abajo es código Python ejecutable con la biblioteca estándar. Espera una cadena JSON, permite exactamente cuatro claves, comprueba los tipos, interpreta las fechas con el formato ISO y aplica relaciones entre estado, fechas y pregunta. No valida que una fecha haya aparecido realmente en el texto de entrada: para eso hace falta conservar el texto, definir una estrategia de verificación de evidencia y probarla aparte. Tampoco sustituye la gestión de errores de API, que debe adaptarse al servicio utilizado.

El ejemplo rechaza deliberadamente campos adicionales. Esa política evita que un cambio imprevisto pase desapercibido, aunque un sistema con evolución controlada puede elegir otra regla. Lo importante es decidirlo expresamente y versionar el contrato. Si se usa una biblioteca de validación externa en producción, fija una versión compatible, prueba sus reglas y trata los errores del validador como parte de la interfaz interna, no como una razón para saltarse la comprobación.

No conviertas un error en un valor predeterminado silencioso. Si falta end_date, rellenarla con start_date puede cambiar el significado de la solicitud. Si una fecha es inválida, tratarla como null puede borrar una señal útil de fallo. Es preferible devolver un resultado tipado de validación —válido, requiere aclaración o inválido— y decidir de forma visible si se reintenta, se pregunta a la persona o se envía a revisión.

Validación local con Python estándar · python

Guarda el código como validate_leave.py y ejecútalo con Python 3. El ejemplo imprime los datos solo después de validar el contrato y sus relaciones básicas.

import json
from datetime import date

REQUIRED = {
    "status",
    "start_date",
    "end_date",
    "clarification_question",
}
STATUSES = {"complete", "needs_clarification", "not_a_request"}


def validate(raw):
    try:
        data = json.loads(raw)
    except json.JSONDecodeError as exc:
        raise ValueError("JSON inválido") from exc

    if not isinstance(data, dict) or set(data) != REQUIRED:
        raise ValueError("Objeto con claves ausentes o inesperadas")
    if data["status"] not in STATUSES:
        raise ValueError("Estado no permitido")

    for key in ("start_date", "end_date", "clarification_question"):
        value = data[key]
        if value is not None and not isinstance(value, str):
            raise ValueError(f"Tipo incorrecto: {key}")

    dates = []
    for key in ("start_date", "end_date"):
        value = data[key]
        if value is None:
            dates.append(None)
            continue
        parsed = date.fromisoformat(value)
        if parsed.isoformat() != value:
            raise ValueError(f"Formato de fecha incorrecto: {key}")
        dates.append(parsed)

    start, end = dates
    if data["status"] == "complete":
        if start is None or end is None or end < start:
            raise ValueError("Intervalo completo ausente o incoherente")
        if data["clarification_question"] is not None:
            raise ValueError("Una solicitud completa no debe pedir aclaración")
    elif data["status"] == "needs_clarification":
        if not data["clarification_question"]:
            raise ValueError("Indica qué dato debe aclararse")
    elif data["status"] == "not_a_request":
        if start is not None or end is not None:
            raise ValueError("Un texto ajeno a la solicitud no debe incluir fechas")

    return data


if __name__ == "__main__":
    sample = '{"status":"complete","start_date":"2027-06-12","end_date":"2027-06-14","clarification_question":null}'
    print(validate(sample))
07

Definir qué hacer ante cada fallo

Una política de fallos útil distingue al menos cuatro clases de situación. Primero, errores de transporte o del servicio: no se obtuvo una respuesta utilizable. Segundo, errores de generación o rechazo: la API puede informar que no produjo el resultado solicitado. Tercero, errores de formato o esquema: el contenido no se puede analizar o no cumple el contrato. Cuarto, errores semánticos: la forma pasa las comprobaciones, pero el contenido es incompleto, contradictorio o no respaldado.

Cada clase necesita un resultado explícito. Un error temporal documentado puede permitir un reintento limitado; repetir una solicitud ambigua no crea información que no estaba en la entrada. Un fallo de esquema puede justificar un nuevo intento con instrucciones más claras solo si se acota el número de intentos y se vuelve a validar. Un dato semánticamente incoherente puede requerir una pregunta a la persona o revisión humana. Un rechazo debe mostrarse o tratarse según la política de producto y los detalles que documente el proveedor.

Establece límites para no entrar en bucles de reintento y evita reparar automáticamente valores que cambien el significado. Si el reintento produce una salida diferente, no elijas silenciosamente la que parezca más conveniente. Conserva el estado de validación, la versión del contrato y el motivo categorizado del fallo. Para operaciones con consecuencias, considera una fase de confirmación antes de ejecutar el efecto real.

Los registros técnicos deberían permitir diagnosticar con la menor exposición de datos posible. Guarda identificadores de correlación, versión del contrato, categoría del error y métricas necesarias. Evalúa si hace falta retener el texto de entrada o la salida completa; si no es necesario, no los registres por defecto. Si se retienen, define acceso, plazo y tratamiento adecuados a la sensibilidad de esos datos.

Respuesta por categoría de fallo

SituaciónRespuesta prudenteEvitar
Fallo de transporte o servicioAplicar la política de reintentos documentada, con límite y registro.Tratar una respuesta inexistente como una extracción vacía válida.
JSON mal formado o esquema incumplidoMarcar la salida como inválida y decidir si se intenta una corrección acotada.Eliminar partes inesperadas hasta que el resultado parezca aceptable.
Campo necesario ausente o entrada ambiguaSolicitar el dato faltante o derivar a revisión.Inventar un valor predeterminado que altere la solicitud.
Valores contradictorios o no respaldadosBloquear la acción posterior y conservar la categoría del fallo.Confiar en que un formato válido equivale a contenido correcto.
08

Probar entradas reales y casos límite

Las pruebas deben verificar comportamientos, no solo que la respuesta de ejemplo se pueda analizar. Prepara un conjunto pequeño y mantenible de entradas representativas, resultados esperados y reglas que deben cumplirse. Incluye casos ordinarios, ambiguos, incompletos, contradictorios y ajenos a la tarea. Cada caso debería probar tanto la salida que se considera aceptable como la decisión de la aplicación después de recibirla.

Para la solicitud de vacaciones, una entrada clara podría pedir fechas completas en un rango coherente. Un caso ambiguo puede decir «el viernes» sin contexto temporal suficiente. Otro puede pedir fechas pero omitir el año. Un caso incompleto podría no especificar fecha final. Prueba también un rango invertido, una fecha imposible, un tipo incorrecto, una clave extra y una respuesta que no sea JSON. Para cada uno, decide si la aplicación acepta, pide aclaración o rechaza; no dejes esa decisión a una interpretación improvisada durante la integración.

Incluye pruebas semánticas con salidas preparadas, además de pruebas con el modelo. Si solo pruebas generaciones reales, el resultado puede variar y será difícil aislar si falló la generación, el contrato o el código consumidor. Las pruebas deterministas del validador permiten comprobar casos exactos; las pruebas de integración observan cómo se comporta el flujo completo con la API y configuración que realmente se desplegarán.

Al cambiar el esquema, añade pruebas para compatibilidad entre versiones. Si un consumidor espera un campo que deja de estar disponible, un cambio aparentemente pequeño puede romper el procesamiento. Versiona el contrato y decide cómo se despliegan productor y consumidor: por ejemplo, admitir un periodo de transición con ambas versiones o coordinar el cambio. No presentes un cambio de nombres o de nulabilidad como una modificación cosmética.

Matriz mínima de pruebas

CasoComprobaciónResultado esperado de la aplicación
Solicitud clara con fechas coherentesTipos, formato, intervalo y correspondencia con la entrada.Aceptar como completa si todas las reglas pasan.
Fecha relativa o ambiguaNo convertir una referencia incompleta en una fecha segura sin fundamento.Pedir aclaración o marcar incertidumbre.
Fecha ausenteVerificar la convención null y la relación con status.No rellenar automáticamente con otra fecha.
Fecha imposible o rango invertidoValidar calendario y orden temporal.Rechazar como salida semánticamente inválida.
Clave extra, tipo incorrecto o JSON mal formadoProbar límites de análisis y del contrato.Bloquear el procesamiento y clasificar el error.
Texto que no es una solicitudComprobar el estado not_a_request y que no haya fechas de acción.No iniciar el flujo de vacaciones.
09

Desplegar y mantener el contrato

Un contrato desplegado necesita un responsable, una versión y un proceso de cambio. Guarda la definición junto al código que la valida y registra cuál se usó para cada resultado procesado. Antes de cambiar campos, tipos, enumeraciones o reglas de ausencia, evalúa a los consumidores y añade pruebas de regresión específicas. Si cambia el modelo, el prompt, la configuración o la API, repite las pruebas relevantes: el contrato no sustituye la verificación del comportamiento del sistema completo.

Mide por separado la proporción de respuestas que se pueden analizar, la que cumple el esquema, la que supera reglas semánticas y la que requiere aclaración o revisión. Un único indicador de «éxito» oculta diferencias importantes. Por ejemplo, que casi todas las respuestas sean JSON válido no revela si las fechas extraídas coinciden con las solicitudes. Las métricas deben tener definiciones estables y evitar recoger contenido sensible cuando una categoría agregada sea suficiente.

Vigila cambios en documentación y versiones de proveedor, pero no asumas que una actualización conserva exactamente las capacidades de la configuración anterior. Registra qué combinación se probó y verifica las condiciones de compatibilidad antes de modificarla. La página de documentación de un servicio es una referencia para ese servicio y alcance descritos; no certifica un comportamiento idéntico en otro producto, versión o modelo.

Si el sistema toma una decisión con consecuencias, añade una barrera proporcional al riesgo. Puede ser revisión humana, confirmación explícita o una comprobación independiente. Diseña también una ruta de recuperación: qué hacer con resultados pendientes, cómo corregir un dato validado como erróneo y cómo evitar ejecutar dos veces una acción tras un reintento. El contrato de datos es parte de un flujo operativo, no una sustitución de sus controles.

Lista de aceptación antes de conectar una acción posterior

  1. 01Cada campo tiene un consumidor, un tipo y una política explícita para valores ausentes.
  2. 02Las condiciones entre campos y las reglas de negocio están escritas y se prueban localmente.
  3. 03El sistema distingue errores de servicio, errores de formato, estados de aclaración e incoherencias semánticas.
  4. 04La aplicación no ejecuta acciones a partir de respuestas que no hayan superado todas las comprobaciones necesarias.
  5. 05Hay pruebas para entradas ambiguas, campos ausentes, tipos incorrectos, valores límite y respuestas inesperadas.
  6. 06El contrato está versionado y los cambios se revisan con los componentes que lo consumen.
  7. 07Los registros permiten investigar fallos sin conservar datos personales o sensibles innecesarios.
  8. 08La compatibilidad de la API y del modelo se ha comprobado en documentación oficial vigente y en el entorno de despliegue.
10

Criterios prácticos para cerrar el diseño

Antes de dar por terminado el trabajo, comprueba que la estructura responde a una necesidad real del producto y no solo a la preferencia por recibir JSON. El contrato debe ser lo bastante pequeño para que cada campo tenga un uso claro, pero lo bastante expresivo para representar incertidumbre, ausencia y resultados que no corresponden a la tarea. Si los casos ambiguos no tienen una representación segura, todavía no está listo para producción.

Asegúrate de que el consumidor valida la respuesta recibida, no solo las instrucciones enviadas. Comprueba el formato, las claves, los tipos y las reglas semánticas por separado. Define qué se bloquea, qué se reintenta y qué se pregunta. Prueba las decisiones con ejemplos verificables, incluida la entrada que no contiene suficiente información y la salida que parece válida pero contradice el texto.

Por último, anota las incertidumbres externas: compatibilidad concreta de la API, límites de esquema, cobertura de modelos y forma de los errores. Resuélvelas consultando documentación oficial del producto y probando la versión que se va a desplegar. Así, el contrato local puede seguir siendo una frontera de seguridad y mantenimiento, aunque cambie la modalidad de generación.

Qué sigue abierto

  • Las fuentes aportadas no detallan de forma completa los modelos, versiones y subconjuntos de esquemas compatibles con cada modalidad; hay que verificarlos en documentación oficial vigente para la implementación elegida.
  • Las fuentes aportadas no proporcionan una especificación unificada de cómo representan los proveedores rechazos, errores de generación o respuestas incompletas; el tratamiento debe comprobarse para la API concreta.
  • El esquema JSON del ejemplo y el código Python son propuestas locales ilustrativas. No afirman que el esquema pueda enviarse sin adaptación a una API concreta.
  • Las reglas para resolver fechas relativas, confirmar solicitudes o escalar casos ambiguos dependen de la aplicación y no pueden deducirse de la sintaxis JSON.
11

Continúa explorando

11

Fuentes consultadas

03

Correcciones y transparencia

Si detectas un dato incorrecto o desactualizado, puedes enviarnos una corrección indicando la página y la fuente que debemos revisar.

Proponer una corrección