Por qué las salidas estructuradas superan al formato JSON basado en prompts
Todo desarrollador que ha integrado un LLM en una pipeline ha escrito un prompt como "responde solo con JSON válido, sin ningún otro texto".
Esa instrucción usualmente funciona.
"Usualmente" es el problema.
Las salidas estructuradas resuelven esto moviendo la garantía de JSON del prompt a la propia solicitud de la API, utilizando un parámetro llamado output_config.format. En lugar de pedirle amablemente a Claude que formatee su respuesta de cierta manera, tú declaras la forma exacta que requieres, y la API restringe la generación para que la respuesta se ajuste a ella. Esta página explica por qué esa distinción importa, qué cambia en la forma en que construyes con la API de Claude una vez que confías en ella, y dónde terminan sus garantías.
Resumen
- Idea Central:
output_config.formatrestringe la respuesta de Claude en el momento de la generación para que coincida con un JSON Schema que tú proporcionas, en lugar de depender de la redacción del prompt para solicitar JSON. - Por qué es importante: Las solicitudes JSON basadas en prompts son probabilísticas. El modelo aún puede agregar un preámbulo, envolver el JSON en cercas de markdown, omitir un campo o producir una sintaxis inválida bajo algunas entradas, y cada uno de esos modos de fallo debe ser detectado y reintentado por tu propio código.
- Conceptos clave: JSON Schema, output_config.format, respuesta válida según el esquema, client.messages.parse, modelo Pydantic, truncamiento por max_tokens.
- Cuándo usarlo: Siempre que el código posterior analice la respuesta del modelo como datos: pipelines de extracción, flujos de trabajo adyacentes a herramientas, llenado de formularios, clasificación con campos estructurados, o en cualquier lugar donde una respuesta mal formada rompería un programa en lugar de simplemente parecer extraña a un lector humano.
- Limitaciones / Compromisos: Las salidas estructuradas no admiten todos los constructos de JSON Schema, y una respuesta aún puede ser incompleta si alcanza el límite de
max_tokensantes de que el JSON se cierre. - Temas relacionados: Diseño de JSON Schema, validación Pydantic, uso estricto de herramientas, manejo de truncamiento.
Fundamentos
Antes de que las salidas estructuradas existieran como una característica a nivel de solicitud, el patrón estándar para obtener JSON de un LLM se basaba completamente en el prompt.
Escribías un prompt de sistema instruyendo al modelo a "solo generar JSON válido que coincida con esta forma", quizás incluías un ejemplo, y luego analizabas cualquier texto que regresara con json.loads().
Esto funciona la mayor parte del tiempo, que es exactamente por qué es arriesgado.
El modelo está generando texto token por token, y "responder solo con JSON" es una preferencia que usualmente respeta, no una regla que no puede romper.
Bajo ciertas entradas, especialmente conversaciones más largas, instrucciones ambiguas o datos de casos extremos, el modelo podría agregar una explicación de una línea antes del JSON, envolver el objeto en una cerca de código markdown, usar un nombre de campo que es similar pero no exactamente lo que pediste, o dejar un valor completamente fuera.
Cada una de esas salidas todavía "parece" que el modelo intentó cooperar, pero cada una de ellas rompe una llamada ingenua a json.loads().
Las salidas estructuradas invierten esta relación.
En lugar de describir la forma deseada en prosa, la describes con un JSON Schema, una especificación legible por máquina de los campos, tipos y propiedades requeridas exactas que debe tener tu respuesta.
Pasas ese esquema en el campo output_config.format de tu solicitud, y la API restringe el proceso de generación en sí para que el contenido devuelto se ajuste al esquema.
El prompt ya no realiza el trabajo de aplicación.
Un modelo mental simple: el formato basado en prompts es como pedirle a un colega que complete un formulario correctamente describiéndole el formulario con palabras. Las salidas estructuradas son como entregarle el formulario real, con campos que solo aceptan el tipo correcto de entrada. Uno se basa en la interpretación; el otro restringe el espacio de posibles respuestas antes de que ocurra la generación.
from anthropic import Anthropic
client = Anthropic()
schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"is_urgent": {"type": "boolean"},
},
"required": ["name", "is_urgent"],
"additionalProperties": False,
}
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
output_config={"format": {"type": "json_schema", "schema": schema}},
messages=[{"role": "user", "content": "Extract the ticket subject and urgency."}],
)Observa lo que falta en el prompt: no hay "responde solo con JSON", no hay instrucciones de formato, no hay salida de ejemplo. El esquema hace ese trabajo.
Mecánica e interacciones
La diferencia práctica entre los dos enfoques se manifiesta más claramente en cómo se comportan los fallos.
Con JSON basado en prompts, un fallo es un fallo de análisis completo. Tu código llama a json.loads() en una cadena que podría contener prosa inicial, comentarios finales o una cerca de markdown, y si algo de eso está presente, el análisis falla y no tienes nada utilizable. Entonces te encuentras escribiendo lógica defensiva de eliminación de cadenas: expresiones regulares para encontrar el primer { y el último }, eliminando cercas ```json, capturando y reintentando en JSONDecodeError. Ese código defensivo es un impuesto que pagas en cada integración, para siempre, porque la garantía subyacente nunca mejoró; simplemente mejoraste en la limpieza después de ella.
Con output_config.format, la restricción se aplica durante la generación, no después. La API está aplicando el esquema token por token a medida que se produce la respuesta, por lo que una respuesta exitosa es válida según el esquema por construcción, no por buena suerte. Esto elimina una categoría completa de errores de análisis de tu código, porque ya no existe un resultado de "el modelo casi lo hizo bien" que manejar; la respuesta satisface el esquema, o sucedió algo más (truncamiento, una negativa o un error genuino de la API) y lo manejas como su propia condición distinta y detectable en lugar de como texto ruidoso.
Aquí es también donde el asistente de nivel superior del SDK de Python importa. client.messages.create(...) aún devuelve contenido de respuesta sin procesar que extraerías y deserializarías tú mismo. client.messages.parse(...) va un paso más allá: devuelve un objeto ya validado y analizado, comúnmente respaldado por un modelo Pydantic que defines junto con el esquema. La distinción es la misma que la de output_config.format en sí: mover el trabajo de "escribir código para verificar esto" a "dejar que el framework garantice esto".
from pydantic import BaseModel
from anthropic import Anthropic
class Ticket(BaseModel):
name: str
is_urgent: bool
client = Anthropic()
response = client.messages.parse(
model="claude-sonnet-5",
max_tokens=1024,
output_config={"format": {"type": "json_schema", "schema": Ticket.model_json_schema()}},
messages=[{"role": "user", "content": "Ticket: server down, please help fast!"}],
)
ticket = response.parsed_output # una instancia de Ticket validada, no una cadena sin procesarHay una sutileza que vale la pena internalizar aquí: la aplicación del esquema ocurre en la capa de la API, pero no es magia que borre todos los modos de fallo. Dos condiciones aún requieren tu propio manejo. Primero, la respuesta puede ser truncada si alcanza max_tokens antes de que el objeto JSON se cierre; el texto parcial no será JSON válido, aunque la API estuviera intentando seguir fielmente tu esquema. Segundo, no todos los constructos que podrías querer expresar en JSON Schema son compatibles con el motor de aplicación de la API; hay límites en los tipos de campo, el manejo de enumeraciones y ciertas restricciones de esquema, lo que significa que el diseño del esquema en sí se convierte en una habilidad con su propio material de referencia (ver Relacionados a continuación) en lugar de "simplemente escribe cualquier JSON Schema que quieras".
Consideraciones y aplicaciones avanzadas
Una vez que confías en las salidas estructuradas, la forma de tu código de integración cambia de una manera que se acumula en una base de código, no solo en una única solicitud.
El formato JSON basado en prompts empuja la lógica de validación hacia abajo y la duplica: cada sitio de llamada que espera JSON de vuelta necesita su propio análisis, su propio manejo de errores, su propia verificación de "el modelo realmente siguió las instrucciones esta vez". Las salidas estructuradas centralizan esa garantía en la definición del esquema. Cambia el esquema una vez, y cada respuesta que lo usa se valida de la misma manera, por la misma capa de aplicación, con los mismos modos de fallo.
Esto es más importante precisamente en los lugares donde el JSON basado en prompts es más arriesgado: pipelines de producción con volumen, donde una baja tasa de fallos de análisis de un solo dígito es invisible en las pruebas pero costosa a escala; y sistemas posteriores (bases de datos, otros servicios, componentes de UI) que se bloquearán o corromperán el estado con una entrada mal formada en lugar de simplemente mostrar una cadena fea a un humano.
Vale la pena ser preciso sobre lo que significa "garantía" aquí. output_config.format garantiza que la respuesta, si se genera con éxito, se ajusta a tu esquema. No garantiza que la respuesta sea correcta en el sentido de ser fácticamente correcta o completa; el modelo aún puede poner un valor plausible pero incorrecto en un campo correctamente tipado. Las salidas estructuradas resuelven un problema de fiabilidad sintáctica, no un problema de precisión semántica. Todavía necesitas tu propia validación, revisión humana o verificaciones posteriores para la corrección; simplemente ya no necesitas defenderte de una sintaxis mal formada además de eso.
| Enfoque | Fortaleza | Debilidad | Mejor ajuste |
|---|---|---|---|
| Solicitud JSON basada en prompt | No hay superficie de API que aprender, funciona con cualquier modelo | Probabilístico; la salida mal formada requiere análisis defensivo y reintentos | Prototipos rápidos, uso manual de bajo volumen |
output_config.format (sin procesar) | Garantiza JSON válido según el esquema a nivel de API | Aún analizas el texto devuelto tú mismo | Cuando quieres la garantía pero no un objeto tipado |
client.messages.parse() con Pydantic | Esquema garantizado y un objeto tipado validado en una sola llamada | Requiere definir modelos Pydantic; capa de conveniencia específica del SDK de Python | La mayoría de las integraciones de Python en producción que extraen datos estructurados |
Conceptos erróneos comunes
- "Si pregunto con suficiente claridad en el prompt, el modelo siempre devolverá JSON válido." - Los prompts más claros realmente reducen las tasas de fallo, pero no pueden eliminarlas, porque el modelo sigue generando texto libre y cualquier proceso de generación tiene alguna posibilidad de desviarse. Las salidas estructuradas eliminan la "posibilidad" al restringir la generación misma.
- "Las salidas estructuradas significan que ya no necesito validar nada." - La API garantiza que tu respuesta coincide con la forma del esquema. No garantiza que el contenido sea fácticamente correcto. Todavía necesitas tus propias verificaciones de precisión para cualquier cosa de alto riesgo.
- "Puedo describir cualquier forma JSON que quiera, por muy anidada o restringida que esté." - La función de salidas estructuradas admite un subconjunto significativo pero limitado de JSON Schema. Ciertos constructos (como algunas restricciones de rango numérico) no se aplican, y los esquemas muy complejos o recursivos pueden no ser compatibles en absoluto.
- "Una respuesta válida según el esquema nunca puede ser truncada." - Sí puede. Si la respuesta alcanza
max_tokensantes de que el modelo termine de escribir el JSON, lo que se devuelve es una cadena incompleta, aunque el modelo estuviera cooperando con el esquema todo el tiempo que estuvo generando.
Preguntas frecuentes
¿Es output_config.format un nuevo endpoint de API, o un parámetro en la API de Messages existente?
- Es un parámetro (
output_config) en las mismas llamadasmessages.create()/messages.parse()que ya utilizas. - No se requiere un endpoint o cliente separado.
¿Todavía necesito json.loads() si uso output_config.format con client.messages.create()?
- Sí,
client.messages.create()todavía devuelve el contenido de la respuesta como texto; la garantía es que el texto es JSON válido según el esquema, no que ya esté deserializado. - Usa
client.messages.parse()en su lugar si quieres un objeto ya analizado y validado.
¿Cuál es la diferencia entre client.messages.create() y client.messages.parse() para salidas estructuradas?
create()devuelve la respuesta sin procesar; tú extraes el contenido del texto y lo analizas tú mismo.parse()valida la respuesta contra tu esquema (a menudo un modelo Pydantic) y devuelve un objeto analizado listo para usar.
¿Pueden las salidas estructuradas garantizar que los datos son fácticamente correctos, no solo bien formados?
- No. La garantía es sintáctica: la respuesta coincide con los tipos y campos requeridos de tu esquema.
- El modelo aún puede poblar un campo correctamente tipado con un valor inexacto; las verificaciones de corrección siguen siendo tu responsabilidad.
¿Por qué una característica válida según el esquema a veces produciría JSON roto?
- La causa más común es alcanzar el límite de
max_tokensantes de que el objeto JSON se cierre, lo que trunca la salida a mitad de la estructura. - Detectar esto y reintentar con un límite de tokens mayor es una parte estándar de una integración de salidas estructuradas.
¿Funciona cada palabra clave de JSON Schema con output_config.format?
- No, hay límites en los tipos de campo admitidos, el manejo de enumeraciones y ciertas restricciones de esquema.
- Consulta la referencia de tipos de campo y restricciones de salida estructurada antes de diseñar cualquier cosa más allá de un objeto simple o ligeramente anidado.
¿Esto solo es útil para tareas de extracción de datos?
- La extracción (contratos, correos electrónicos, formularios) es el caso de uso más común, pero cualquier flujo de trabajo donde el código posterior consume la salida del modelo como datos se beneficia: clasificación con campos estructurados, generación de formularios, resúmenes estructurados que alimentan otro sistema.
¿En qué se diferencia esto de simplemente usar un esquema de herramienta/llamada a función?
- Los esquemas de uso de herramientas restringen la entrada a una llamada a herramienta que el modelo decide realizar.
output_config.formatrestringe la respuesta completa del mensaje en sí, ya sea que haya o no una herramienta involucrada, y los dos se pueden combinar para que tanto la respuesta como los parámetros de cualquier llamada a herramienta estén garantizados como válidos.
¿Necesito Pydantic para usar salidas estructuradas en Python?
- No, Pydantic no es obligatorio; puedes escribir a mano el diccionario JSON Schema y usar
client.messages.create(). - Pydantic es una conveniencia que se suele combinar con
client.messages.parse()porqueModel.model_json_schema()genera el esquema por ti y obtienes una instancia validada.
¿Qué sucede si el modelo no puede producir una salida que coincida con mi esquema en absoluto?
- Esta es una condición separada del truncamiento: una negativa o una incapacidad para cumplir se manifiesta como un resultado distinto en lugar de un blob JSON parcialmente formado, lo cual es una de las razones por las que las salidas estructuradas son más fáciles de manejar programáticamente que las solicitudes JSON de texto libre.
¿Debería dejar de escribir instrucciones de "devolver solo JSON" en mis prompts ahora?
- Puedes eliminarlas como un requisito:
output_config.formates lo que aplica la forma. - Mantener una breve instrucción sobre la intención de la extracción (no el formato) aún puede ayudar al modelo a producir mejores valores, pero no una mejor sintaxis.
Relacionado
- Conceptos básicos de salidas estructuradas - solicita una respuesta de esquema JSON y analízala con client.messages.parse.
- Definición de un JSON Schema para output_config.format - cómo escribir el esquema que esta página asume que usarás.
- Referencia de tipos de campo y restricciones de salida estructurada - qué constructos de JSON Schema son realmente compatibles.
- Manejo de salida JSON mal formada o truncada - el modo de fallo de truncamiento que esta página señala como aún bajo tu responsabilidad.
Versiones de la pila: Escrito para la línea de modelos de Claude actual a partir de junio de 2026: Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (el predeterminado) y Claude Haiku 4.5, y el SDK oficial de Python
anthropic(última versión 0.x). Los nombres de los modelos, las versiones del SDK y los precios cambian rápidamente; verifica las especificaciones actuales en platform.claude.com/docs antes de confiar en ellas.