11 · Salida estructurada (JSON)
Para usar la salida de un modelo en código necesitas estructura, no prosa. Pide JSON, extráelo, parséalo, valídalo contra un esquema — y repara o reintenta cuando el modelo devuelve algo mal formado.
Cuando el código consume la salida de un modelo, el texto libre es un riesgo — necesitas JSON que coincida con un esquema. El patrón robusto es: pide JSON, EXTRAE el substring JSON (los modelos añaden prosa de más), hazle json.loads, VALIDA claves y tipos requeridos, y al fallar REPARA lo que puedas o REINTENTA. Nunca confíes en el string crudo.
Sin esto:
Sin parsear-y-validar te caerás el día en que el modelo envuelva su JSON en 'Claro, aquí tienes:' o se salte un campo requerido. Con ello, tu pipeline se degrada con gracia — repara, reintenta o rechaza — en vez de reventar en producción.
Un chatbot puede responder en prosa, pero una app con LLM normalmente necesita la salida en código — para guardarla, ramificar con ella o llamar a otra función. Eso significa que el modelo debe devolver datos estructurados, casi siempre JSON, que coincidan con un esquema fijo (qué claves deben existir, de qué tipo es cada valor).
El truco: un modelo de lenguaje emite texto. Aunque pidas JSON, puede añadir un preámbulo amable ("¡Claro! Aquí está el JSON:"), envolverlo en bloques de código Markdown, omitir un campo requerido o producir una coma final que rompe el parser. Así que el lado consumidor necesita un pequeño pipeline defensivo:
- Extrae el substring JSON. A los modelos les encanta rodear el JSON con prosa — corta desde el primer
{hasta el último}que cierra, en vez de darle toda la respuesta al parser. - Parsea con
json.loads. Si lanza error, tienes JSON mal formado — no te caigas, manéjalo. - Valida contra el esquema: cada clave requerida presente, cada valor del tipo correcto. Que el parseo tenga éxito no es lo mismo que un objeto válido.
- Repara o reintenta al fallar. Algunos errores los puedes arreglar localmente (quitar fences, convertir un número escrito como string); otros se los devuelves al modelo con el mensaje de error y le pides reintentar.
Este bucle "pide → extrae → parsea → valida → repara/reintenta" es la columna vertebral de toda función de LLM fiable que alimenta una base de datos u otro servicio.
Abajo, cuatro salidas de modelo simuladas sustituyen lo que devuelve un modelo real — un objeto JSON limpio, uno envuelto en prosa charlatana, uno dentro de un bloque de código Markdown y uno realmente mal formado (una coma final). Pasamos cada uno por extraer → parsear → validar contra un esquema, luego aplicamos un pequeño paso de reparación y re-validamos. Observa qué casos pasan al primer intento y cuáles solo pasan tras reparar.
Python (in browser)
Extraer → parsear → validar → reparar → re-validar sobre cuatro respuestas simuladas: la limpia pasa al instante; los casos con fence/coma final solo pasan tras una reparación local barata.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Algunas fallas no se pueden arreglar localmente — si el modelo omite una clave requerida, ninguna cirugía de strings la inventará. La jugada honesta es reintentar: envía la petición de nuevo con el error de validación añadido, para que el modelo vea qué salió mal y lo corrija. Abajo hay un modelo simulado determinista (un sustituto) que devuelve JSON malo en su primer intento y JSON bueno en el segundo, envuelto en un bucle de reintentos con un tope duro para que nunca cicles para siempre.
Python (in browser)
Reintento-con-retroalimentación: añade el error de validación y vuelve a preguntar. El modelo simulado falla una vez, luego se autocorrige — acotado por max_retries para que no cicle infinitamente.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Las APIs reales ofrecen decoding restringido por esquema (response_format json_schema de OpenAI) para que el modelo no se desvíe de tu forma — pero parsear + validar a la defensiva sigue siendo tu red de seguridad.
Un modelo devuelve el string '```json\n{"label": "bug"}\n```'. Tu código hace json.loads sobre la respuesta cruda y lanza un JSONDecodeError. ¿Cuál es el arreglo correcto?
- Las apps necesitan datos estructurados (JSON que coincide con un esquema), no prosa — el lado consumidor debe extraer, parsear, validar y reparar/reintentar.
- Los modelos envuelven el JSON en prosa o bloques de código: corta del primer { al último } antes de json.loads en vez de pasar toda la respuesta.
- Que el parseo tenga éxito ≠ objeto válido: chequea siempre claves requeridas Y tipos de valor; repara localmente cuando es barato, reintenta con el error retroalimentado cuando no (acotado por max_retries).
El tool/function calling, las funciones de extracción de datos y los pasos de agentes dependen de JSON válido contra esquema; bibliotecas como Pydantic, Instructor y el response_format json_schema de OpenAI automatizan justo este bucle extraer-parsear-validar.
Si lo quitas: Sáltate la validación y tu app se cae o guarda basura en silencio la primera vez que el modelo añade un preámbulo, omite una clave o devuelve el tipo equivocado — fallas que solo aparecen en producción.