La salida estructurada es una respuesta con una forma que decidiste de antemano: un objeto JSON con claves con nombre, una tabla con columnas fijas o una plantilla con los mismos encabezados cada vez. La necesitas siempre que un programa lea la respuesta, y también ayuda cuando la lee una persona, porque todas las respuestas se ven igual y son fáciles de revisar o comparar.
El truco está en describir la forma con tanta precisión que al modelo no le quede nada por elegir. La palabra "JSON" por sí sola no es una descripción. El bloque de abajo extrae datos de un informe de error. Compara una petición de "los datos clave" con una que detalla el esquema.
Estos son los datos clave del informe de error:
- Problema: la app se cierra al exportar proyectos
- Desencadenante: tocar Exportar en proyectos con más de 50 fotos
- Desde: la última actualización
- Plataforma: Android 14
- Versión de la app: 3.2.0
- Impacto: alto, porque está bloqueando una entrega a un cliente
Los proyectos más pequeños parecen exportarse sin problemas.
La respuesta en texto libre es correcta y se lee bien, pero no habrá dos ejecuciones con las mismas etiquetas, la gravedad es una frase y no un valor, y un programa tendría que adivinar dónde empieza cada campo. La respuesta JSON puede ir directamente a un gestor de incidencias. Fíjate en que la respuesta llegó igualmente dentro de un bloque de código: las apps de chat suelen envolver el JSON así, y eso importa cuando lo analizas en el código.
Cómo pedir JSON
Una buena petición de JSON responde a todas las preguntas que el modelo respondería por ti:
- Cada clave, escrita exactamente. Escribe los nombres de las claves entre comillas tal como deben aparecer. Di "exactamente estas claves" para que el modelo no añada otras.
- El tipo de cada valor. String, número, booleano, array de strings, objeto anidado.
- Los valores permitidos para cualquier campo con un conjunto fijo, como la gravedad o la categoría. Sin la lista, en cuatro ejecuciones obtienes "High", "high", "severe" y "P1".
- Qué hacer cuando a la entrada le falta un valor. Si no dices "null si no se indica", el modelo tiende a rellenar el hueco con una suposición verosímil, y una versión de la app inventada se ve exactamente igual que una real.
- Nada alrededor. "Devuelve solo el JSON, sin texto antes ni después" elimina la frase de bienvenida.
Cuando la forma es anidada o poco habitual, mostrar un objeto de ejemplo completo funciona mejor que describirla. Eso es el prompting few-shot aplicado al formato. Usa en el ejemplo valores claramente distintos de los de la entrada real para que el modelo no los copie.
Un prompt de extracción reutilizable
Este bloque tiene la misma petición dividida en partes. Desactiva la parte de formato y el modelo sigue devolviendo JSON, porque las restricciones lo piden, pero elige sus propios nombres de clave, como title en lugar de job_title, y el código que espera tus claves se rompe. La parte de restricciones es la que le impide completar un teléfono a medio escribir o deducir una empresa a partir del dominio de un correo. Pega una firma real en el campo de entrada para probarlo.
{
"name": "string",
"job_title": "string o null",
"company": "string o null",
"email": "string o null",
"phone": "string o null"
}{
"name": "Priya Nair",
"job_title": "Directora de Datos",
"company": "Northwind Labs",
"email": "priya.nair@northwind.example",
"phone": null
}
Tablas y plantillas fijas
La salida estructurada no es solo para programas. Cuando lees tú mismo la respuesta, una tabla de markdown o una plantilla fija te da la misma ventaja: sabes dónde estará cada dato antes de mirar.
| Tipo | Ordenado | Mutable | Permite duplicados | Uso típico |
|---|---|---|---|---|
| list | Sí | Sí | Sí | Una secuencia a la que añades, quitas u ordenas elementos |
| tuple | Sí | No | Sí | Un grupo fijo de valores, como unas coordenadas |
| set | No | Sí | No | Eliminar duplicados y comprobar pertenencia rápido |
Una plantilla funciona igual para textos más largos: da los encabezados en orden y di qué va debajo de cada uno. "Responde con tres etiquetas en negrita: Causa, Solución, Cómo comprobarlo" produce las mismas tres etiquetas cada vez, y eso hace fácil comparar un lote de respuestas.
El modo JSON en la API
Varias API de modelos tienen un ajuste que obliga a producir JSON sintácticamente válido. En el SDK de Python de OpenAI es response_format. El modo JSON exige que la palabra "JSON" aparezca en algún lugar de tus mensajes, así que el prompt de sistema de abajo la nombra y enumera las claves.
import json
from openai import OpenAI
client = OpenAI()
MODEL = "your-model-id" # e.g. from your provider's model list
report_text = "Since yesterday's update the app crashes when I tap Export..."
response = client.chat.completions.create(
model=MODEL,
response_format={"type": "json_object"},
messages=[
{
"role": "system",
"content": (
"Extract the bug report into JSON with the keys "
"summary (string), severity (one of low, medium, high, critical) "
"and steps_to_reproduce (array of strings)."
),
},
{"role": "user", "content": report_text},
],
)
data = json.loads(response.choices[0].message.content)
El modo JSON garantiza que el texto se pueda analizar (salvo que la respuesta se corte en el límite de tokens), no que coincida con tu esquema. Una clave puede seguir faltando o una gravedad puede seguir siendo "urgent". Varios proveedores también aceptan un JSON Schema completo, como formato de salida o como esquema de entrada de una definición de herramienta (función), y algunos de estos modos restringen la respuesta al esquema. Consulta la documentación de tu proveedor para ver el parámetro exacto, porque estas funciones cambian de una API a otra.
Valida el resultado en el código
Trata el JSON del modelo como tratas cualquier entrada que viene de fuera de tu programa: analízalo y luego compruébalo. El análisis detecta la sintaxis rota. La comprobación detecta un objeto válido con el contenido equivocado.
ALLOWED_SEVERITIES = {"low", "medium", "high", "critical"}
def problems(data):
if not isinstance(data, dict):
return ["the answer must be a JSON object"]
found = []
if not isinstance(data.get("summary"), str):
found.append("summary must be a string")
if data.get("severity") not in ALLOWED_SEVERITIES:
found.append("severity must be low, medium, high or critical")
steps = data.get("steps_to_reproduce")
if not isinstance(steps, list) or not all(isinstance(s, str) for s in steps):
found.append("steps_to_reproduce must be an array of strings")
return found
Cuando la comprobación falla, un solo reintento suele arreglarlo: envía al modelo su propia salida junto con la lista de problemas y pídele el JSON corregido. Limita el número de reintentos y registra los fallos, porque un campo que falla a menudo es señal de que el prompt no está claro. En proyectos más grandes, una biblioteca de validación como Pydantic o un validador de JSON Schema sustituye a la función escrita a mano.
Dos hábitos más evitan errores silenciosos. Fija el límite de tokens de salida lo bastante alto para la respuesta más larga que esperas, porque un objeto truncado nunca se puede analizar. Y cuando la entrada es larga o viene de usuarios, sepárala de tus instrucciones con delimitadores o etiquetas XML para que sea menos probable que el texto de dentro se lea como una instrucción. Si un mismo prompt tiene que razonar y además producir JSON, considera el encadenamiento de prompts: deja que un paso piense en texto libre y que un segundo paso convierta el resultado en la estructura.
Preguntas frecuentes
¿Cómo consigo que ChatGPT devuelva JSON?
Di que la respuesta debe ser JSON, enumera cada clave con su tipo y da los valores permitidos para cualquier campo con un conjunto fijo. Añade "Devuelve solo el JSON, sin texto antes ni después". En la API, activa además el modo JSON con response_format={"type": "json_object"}, que exige que la palabra JSON aparezca en tus mensajes.
¿Por qué el modelo añade texto alrededor del JSON?
Los modelos de chat están entrenados para ser conversacionales, así que a menudo empiezan con una frase como "Aquí tienes el JSON" o envuelven el objeto en un bloque de código de markdown. Pide solo el JSON, y en el código usa el modo JSON de la API o quita el bloque de código que lo rodea antes de analizarlo.
¿Qué es el modo JSON?
El modo JSON es un ajuste de la API que hace que el modelo produzca JSON sintácticamente válido, siempre que la respuesta no se corte por el límite de tokens de salida. No hace que el modelo siga tu esquema: las claves pueden seguir faltando, estar mal escritas o tener un tipo incorrecto. Algunos proveedores ofrecen además un modo más estricto que recibe un JSON Schema completo y restringe la salida a él.
¿Un LLM puede devolver siempre JSON válido?
No solo con un prompt. Incluso una instrucción clara falla de vez en cuando, y una respuesta cortada por el límite de tokens de salida siempre es inválida. Analiza cada respuesta con un parser de JSON de verdad, comprueba los campos que necesitas y reintenta o falla de forma visible cuando la comprobación no pase.