Menu

Salida estructurada: JSON fiable de un LLM

La salida estructurada consiste en pedirle al modelo una respuesta con una forma fija, como JSON, una tabla o una plantilla, para que un programa o una persona pueda usarla sin reorganizarla. Da el esquema, di qué hacer con los valores que faltan y valida el resultado en el código.

Puedes editar cada prompt de esta página y abrirlo después en ChatGPT, Claude u otra app de IA.

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.

Saca los datos clave de este informe de error: "Desde la actualización de ayer, la app se cierra cuando toco Exportar en un proyecto con más de 50 fotos. Los proyectos más pequeños se exportan bien. Uso Android 14, versión 3.2.0 de la app. Esto me está bloqueando una entrega a un cliente."
Try it
Example replyReplies vary between models and runs.

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.

Extraer datos de contacto como JSON
Fill in
Parts
Extrae los datos de contacto de la firma de correo de abajo.
Devuelve un objeto JSON con exactamente estas claves: { "name": "string", "job_title": "string o null", "company": "string o null", "email": "string o null", "phone": "string o null" }
Usa null para cualquier valor que no esté en el texto. No adivines ni completes valores parciales. Devuelve solo el JSON.
Priya Nair | Directora de Datos, Northwind Labs | priya.nair@northwind.example
Try it
Example replyReplies vary between models and runs.
{
  "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.

Tabla
Compara las listas, las tuplas y los conjuntos de Python en una tabla de markdown con estas columnas: Tipo, Ordenado, Mutable, Permite duplicados, Uso típico. Una fila por tipo. Nada de texto fuera de la tabla.
Try it
Example replyReplies vary between models and runs.
TipoOrdenadoMutablePermite duplicadosUso típico
listSíSíSíUna secuencia a la que añades, quitas u ordenas elementos
tupleSíNoSíUn grupo fijo de valores, como unas coordenadas
setNoSíNoEliminar 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.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR