Structured outputs y JSON mode, ¿en qué se diferencian?
Qué garantizan JSON mode, tool calling y structured outputs, y cómo obtener JSON de OpenAI, Claude, Gemini y los agentes de código.
Resumen. Hay tres formas de obtener JSON de un LLM: pedirle JSON al modelo (JSON mode), hacer que el modelo rellene los argumentos de una herramienta (tool calling) o vincular la respuesta a un esquema (structured outputs). El 20 de octubre de 2026 leímos la documentación de las API de OpenAI, Anthropic y Google, y la documentación de línea de comandos de Claude Code, Codex de OpenAI, GitHub Copilot (la CLI y VS Code), Cursor y Antigravity CLI de Google.
- Tres formas, tres garantías distintas. El JSON mode solo da JSON válido; en una llamada estricta a una herramienta, los argumentos cumplen el esquema; los structured outputs vinculan al esquema la propia respuesta. Ninguna garantiza que la respuesta sea correcta.
- En Claude, el método antiguo ahora da error. En Opus 5.5 y Sonnet 5.5, un
tool_choiceque obliga al modelo a llamar a una herramienta devuelve un error 400. La vía que recomienda Anthropic es strict tool use conauto, o los structured outputs. - El mismo esquema no se comporta igual en todos los proveedores. OpenAI y Anthropic rechazan con un error una regla de esquema no admitida; Gemini la ignora. El orden de los campos, la regla sobre los campos obligatorios y los límites del esquema también cambian de un proveedor a otro.
- En los agentes de programación, el esquema se pasa con un flag. Claude Code y Antigravity CLI tienen
--json-schema, y Codex,--output-schema. La documentación de Copilot CLI y de Cursor CLI describe una salida JSON, pero no menciona ninguna opción de esquema.
¿Qué son los structured outputs y en qué se diferencian del JSON mode?
Si un programa va a leer la respuesta de un modelo, esa respuesta tiene que llegar siempre con la misma forma. Si falta un campo, si un número llega como texto o si el JSON se corta a la mitad, el programa falla. En el artículo sobre el prompt de revisión de código contamos cómo pedir un hallazgo con una cita literal, y en el artículo sobre LLM-as-a-judge, cómo registrar el veredicto del juez; en los dos, es código el que lee la respuesta del modelo. Este artículo trata de la forma de esa respuesta: las maneras de obtener JSON, qué garantiza cada una y qué cambió en Claude este otoño.

Con el JSON mode, le pides JSON al modelo y la API promete que la respuesta será JSON válido. No promete qué campos traerá. La documentación de OpenAI lo dice claramente: el JSON mode garantiza que la respuesta se puede parsear sin errores, no que cumpla un esquema.
Con el tool calling (también llamado function calling), le describes una herramienta al modelo. Cuando el modelo quiere llamar a esa herramienta, escribe los argumentos en JSON. Si la herramienta no es estricta, es solo un intento, no una garantía: según la documentación de Anthropic, con una herramienta no estricta el modelo puede escribir "2" en lugar de 2 u omitir un campo obligatorio. Con una herramienta estricta, los argumentos cumplen el esquema.
Con los structured outputs, le das el esquema a la propia respuesta. Al elegir cada token, el modelo solo escoge entre los tokens que encajan con el esquema; esto se llama constrained decoding. Strict tool use aplica la misma técnica a los argumentos de una herramienta. El resultado es un JSON válido que lleva los campos del esquema.
Entonces, ¿cuál para qué? La documentación de Google resume la diferencia: los structured outputs sirven para dar formato a la respuesta final, y el function calling, para que se haga algo en mitad de la conversación. Si vas a guardar la respuesta en una base de datos, structured outputs; si el modelo quiere leer un archivo o ejecutar un comando, tool calling.
¿Cómo se activan los structured outputs en los tres proveedores?
Los tres proveedores ofrecen las mismas tres formas, pero cambian los nombres, la manera de activarlas y los límites. Sacamos la tabla de la documentación de cada proveedor el 20 de octubre de 2026:
| Proveedor | Vincular la respuesta a un esquema | Argumentos de herramientas | JSON sin esquema | Regla no admitida | Forzar una herramienta |
|---|---|---|---|---|---|
| OpenAI | text.format: json_schema y strict: true | strict: true; en la Responses API, por defecto cuando es posible | JSON mode: json_object | Devuelve un error | required o una sola función por su nombre |
| Anthropic | output_config.format: json_schema | strict: true | La documentación no lo dice | Devuelve 400 | Opus 5.5 y Sonnet 5.5: 400; Haiku 5.5 lo acepta, pero se salta el razonamiento |
response_format o response_json_schema | Modo validated | La documentación no lo dice | El modelo la ignora | Modo any |
Fuente: la documentación de OpenAI, Anthropic y Google, 20 de octubre de 2026. «La documentación no lo dice»: en las páginas que leímos ese día no encontramos ninguna frase que describa este caso. En Google, response_format es un campo de la Interactions API, y response_json_schema, de generateContent. La Responses API de OpenAI pasa a modo estricto, cuando es posible, una función que no está marcada como strict; en Chat Completions, el modo estricto no es el predeterminado.
En OpenAI, los structured outputs se activan en la Responses API dentro de text.format, con type: "json_schema" y strict: true. La documentación pone dos condiciones: todos los campos deben ser required, y todos los objetos deben llevar additionalProperties: false. Si quieres un campo opcional, añades null a su tipo. Los límites del esquema también están escritos: como máximo 5.000 campos, 10 niveles de objetos anidados, 1.000 valores de enum y 120.000 caracteres sumando todos los nombres y valores. Una palabra clave no admitida devuelve un error. La primera petición con un esquema es más lenta, porque la API procesa el esquema; las siguientes no.
En tool calling, strict: true hace que los argumentos cumplan el esquema, y OpenAI recomienda activarlo siempre. Obligar al modelo a llamar a una herramienta sigue siendo una opción en OpenAI: si pones tool_choice en required, el modelo llama al menos a una función; si das el nombre de una función, llama exactamente a esa.
En Anthropic, los structured outputs tienen dos mitades: output_config.format para la respuesta (JSON outputs) y strict: true para las herramientas (strict tool use). Las dos pueden usarse juntas en la misma petición. El esquema se compila en una gramática. Por eso la primera petición con un esquema concreto tarda más; el esquema compilado se guarda 24 horas después de su último uso. Los límites son por petición: como máximo 20 herramientas estrictas, 24 campos opcionales en total entre todos los esquemas y 16 campos que usen anyOf o más de un tipo; la compilación tiene además un tiempo límite de 180 segundos. Una regla no admitida, como minimum o maxLength, devuelve un error 400. Los campos llegan en el orden del esquema, pero los obligatorios se escriben antes que los opcionales. Un detalle más: cambiar output_config.format invalida la caché de prompts de esa conversación. Explicamos por qué se rompe la caché en el artículo sobre prompt caching.
El cambio principal de este otoño también está en Anthropic. Una forma conocida de obtener JSON de Claude era definir una sola herramienta y obligar al modelo a llamarla con tool_choice. Según las notas de la versión, en Opus 5.5, publicado el 22 de septiembre, y en Sonnet 5.5, publicado el 28 de septiembre, los tipos any y tool de tool_choice devuelven un error 400. La vía que recomienda Anthropic es strict tool use con auto, y structured outputs si necesitas una forma JSON fija. Según la documentación de herramientas, los modelos que piensan por defecto, como Haiku 5.5, siguen aceptando que se les obligue, pero en una llamada forzada el modelo llama directamente a la herramienta, sin pensar. En Haiku 5.5, publicado el 7 de octubre, otro método antiguo, escribir tú el principio del mensaje del asistente (prefill), puede devolver un error 400; de todos modos, los structured outputs no funcionan con prefill.
En Google, la guía actual describe la Interactions API: le das a response_format un mime_type: "application/json" y un schema. En generateContent, lo mismo se hace con response_json_schema. Según la documentación, el modelo escribe un JSON sintácticamente válido que cumple el esquema y ordena los campos como en el esquema; la API puede rechazar esquemas muy grandes o muy anidados. Aquí hay una diferencia importante: OpenAI y Anthropic rechazan con un error una regla de esquema que no admiten, mientras que Gemini la ignora. Si tu esquema tiene una regla que Gemini no admite, la petición sale bien igualmente, pero la regla no se aplica. En function calling, el modo validated hace que los argumentos cumplan el esquema; el modo any, en cambio, obliga al modelo a llamar a una función cada vez.
¿Qué no garantizan los structured outputs?
Un esquema garantiza la forma de la respuesta, no su contenido. Google lo dice claramente: no se garantiza que los valores sean correctos en su significado. OpenAI también dice que los structured outputs pueden contener errores, y añade un aviso: si la entrada no tiene nada que ver con el esquema, el modelo puede inventarse cosas al intentar cumplirlo. Anthropic, por su parte, enumera los casos en los que el esquema puede no cumplirse en absoluto. Lo que hay que revisar:

Los dos casos más habituales son el rechazo y la respuesta cortada. En Anthropic, una petición rechazada por motivos de seguridad no llega como error, sino como una respuesta normal con stop_reason: "refusal": devuelve HTTP 200, los tokens generados se cobran y la respuesta puede no cumplir el esquema. En OpenAI, el rechazo llega en un campo aparte, refusal. Una respuesta que choca con max_tokens queda incompleta en los dos proveedores; Anthropic recomienda reintentar con un max_tokens más alto. Así que tu código debe mirar primero por qué terminó la respuesta, y solo después parsear el JSON.
Una trampa propia de Claude es el campo de razonamiento en el esquema. Poner un campo reasoning en el esquema y pedirle al modelo que piense ahí antes de responder puede provocar un rechazo en Claude. La documentación de Anthropic sobre rechazos incluye un campo llamado reasoning, thinking o trace, en una salida JSON o en la entrada de una herramienta, entre las causas frecuentes de un rechazo de tipo reasoning_extraction. En su lugar, recomienda pedir una explicación breve, la evidencia en la que se basa el resultado o un resumen de lo que se hizo. Desde el 24 de septiembre también se cobra un rechazo de este tipo que llega antes de que se haya producido ninguna salida. La regla es propia de este proveedor: uno de los ejemplos de la guía de OpenAI es un esquema que da la respuesta con explicaciones paso a paso. En Claude, cuánto piensa el modelo lo decide el ajuste de effort, no el esquema; lo contamos en el artículo sobre effort.
Hasta qué punto es sólido el esquema en manos del proveedor es otra cuestión. En nuestros propios registros, enviamos un esquema JSON estricto a un endpoint que el catálogo de un servicio de enrutamiento presentaba como compatible con response_format, pero no con structured outputs. La respuesta no fue un error: HTTP 200 y 1.639 caracteres de markdown. Y el contenido era correcto: nombraba el error real del diff que usamos para la prueba. Lo único que faltaba era el JSON. Es el registro de una sola prueba, no una tasa. Pero muestra que la promesa de esquema de una API no sustituye a la comprobación en el código que lee la respuesta. En diffox, una respuesta que no se puede parsear como JSON no cuenta como éxito; la llamada falla y pasa al registro de la revisión, junto con qué agente o qué paso cayó y por qué.
¿Cómo se obtiene JSON de un agente de programación en la línea de comandos?
La misma pregunta aparece con los agentes de programación. Si ejecutas un agente en CI o en un script y pasas su resultado a otro paso, necesitas poder fiarte de la forma de la respuesta. Las cinco herramientas tienen una forma de ejecutarse desde la línea de comandos, pero no todas dan lo mismo: unas meten la respuesta en un sobre JSON y otras piden que la propia respuesta siga un esquema. Sacamos la tabla de la documentación de cada herramienta el 20 de octubre de 2026:
| Herramienta | Comando | Salida | Esquema |
|---|---|---|---|
| Claude Code | claude -p --output-format json --json-schema '…' | Sobre JSON; el resultado va en el campo structured_output | Lo valida; si no encaja, vuelve a pedirlo, y al llegar al límite de intentos devuelve un error |
| Codex | codex exec --output-schema schema.json | Eventos JSONL con --json; -o escribe el último mensaje en un archivo | Pide una respuesta final que cumpla el esquema; cómo se aplica, la documentación no lo dice |
| GitHub Copilot CLI | copilot -p '…' --output-format json | Eventos JSONL | La documentación no lo dice |
| Copilot, VS Code | code chat '…' | Abre el chat en el editor | La documentación no lo dice |
| Cursor CLI | agent -p --output-format json | Un único sobre JSON; la respuesta va como texto en el campo result | La documentación no lo dice |
| Antigravity CLI | agy -p '…' --output-format json --json-schema '…' | Sobre JSON; el resultado va en el campo structured_output | Aplica el esquema; cómo, la documentación no lo dice |
Fuente: la documentación de cada herramienta, 20 de octubre de 2026. «La documentación no lo dice»: en las páginas que leímos ese día no encontramos ninguna frase que describa este caso. Que Claude Code valide contra el esquema y vuelva a pedir la respuesta lo cuenta la documentación del Agent SDK; claude -p ejecuta ese SDK desde la línea de comandos. En Cursor CLI, si la ejecución falla, no se escribe JSON y el proceso termina con un código distinto de cero.
La diferencia de la tabla importa. Un sobre JSON significa que la respuesta llega dentro de un objeto JSON; la respuesta que va dentro sigue siendo texto libre. Si el siguiente paso va a leer campos de la respuesta, necesitas una herramienta con opción de esquema o un paso que valide la respuesta en el código. La documentación de Claude Code también es clara aquí: aunque el resultado parezca correcto, trátalo como un fallo si falta el campo structured_output. Claude Code cuenta un detalle más: la palabra clave format de un esquema, por ejemplo "format": "email", la trata solo como una anotación y no la aplica.
Si todavía usas Gemini CLI, gemini -p --output-format json da la respuesta como texto en el campo response; esta documentación no menciona ninguna opción de esquema. Para los usuarios del nivel gratuito y de Google One, Google sustituyó Gemini CLI por Antigravity CLI el 18 de junio de 2026; con claves de API de pago, Gemini CLI se puede seguir usando.
¿Cómo se obtiene JSON al final de un bucle de agente?
En un bucle de agente, el modelo primero lee, busca y prueba cosas con herramientas; al final da un resultado. Para que ese resultado fuera JSON en Claude, bastaba con obligar al modelo a llamar a una herramienta «submit» en el último paso. Como esa vía devuelve un error 400 en Opus 5.5 y Sonnet 5.5, ahora el patrón es este:
- Deja
tool_choiceenauto. Según Anthropic, el prompt sigue influyendo en qué herramienta se elige, y un modelo al que no se obliga puede pensar antes de llamar a la herramienta. - Dale al modelo una herramienta final con
strict: true, por ejemplosubmit, y en el prompt pídele que la llame exactamente una vez, al final, cuando termine el trabajo. Como los argumentos cumplen el esquema, no tienes que parsear el resultado aparte. - Termina el bucle cuando llegue esa llamada. Si el modelo se detiene sin llamar a la herramienta, si la respuesta se corta en
max_tokenso si llega un rechazo, no lo cuentes como resultado; lanza un error explícito. - Pon un límite a las llamadas a herramientas. Cuando se alcance, en lugar de ejecutar la herramienta, dile al modelo que decida ya. Corta también los turnos con un límite superior.
- Si quieres una justificación, pon en el esquema un campo
evidencebreve, noreasoning.
Para la respuesta final también puedes usar structured outputs en lugar de una herramienta: la documentación de Anthropic dice que las herramientas estrictas y output_config.format funcionan juntas en la misma petición. Las llamadas a herramientas cumplen el esquema, y la respuesta final en texto también llega vinculada al esquema. El ejemplo de abajo muestra la primera vía, con el SDK de Python de Anthropic:
# Ejemplo: tool_choice se queda en auto y el bucle termina cuando llega submit.
import anthropic
client = anthropic.Anthropic()
SUBMIT = {
"name": "submit",
"description": "Give the final answer. Call exactly once, last.",
"strict": True,
"input_schema": {
"type": "object",
"properties": {
"verdict": {"type": "string", "enum": ["HOLDS", "REFUTED", "UNSURE"]},
"evidence": {"type": "string"},
},
"required": ["verdict", "evidence"],
"additionalProperties": False,
},
}
def run(model, messages, tools, run_tool, max_calls=8):
calls = 0
for _ in range(max_calls + 3):
response = client.messages.create(
model=model, max_tokens=8000, tools=tools + [SUBMIT], messages=messages)
if response.stop_reason in ("max_tokens", "refusal"):
raise RuntimeError("no answer: " + response.stop_reason)
messages.append({"role": "assistant", "content": response.content})
uses = [b for b in response.content if b.type == "tool_use"]
for b in uses:
if b.name == "submit":
return b.input # el resultado, conforme al esquema
if not uses:
raise RuntimeError("the model stopped without calling submit")
results = []
for b in uses:
calls += 1
out = (run_tool(b.name, b.input) if calls <= max_calls
else "Tool budget spent: call submit now.")
results.append({"type": "tool_result", "tool_use_id": b.id, "content": out})
messages.append({"role": "user", "content": results})
raise RuntimeError("turn limit reached without submit")En diffox, así funciona el paso que comprueba un hallazgo contra el propio repositorio antes de escribirlo en el pull request. La nota del código lo dice claro: «A MANUAL LOOP, tool_choice left at auto». El modelo puede hacer como máximo 8 llamadas con herramientas de git de solo lectura, y el prompt le pide que llame a la herramienta submit_verdict exactamente una vez, al final, cuando termine. submit_verdict es una herramienta estricta: el veredicto es uno de tres valores (HOLDS, REFUTED, UNSURE), con un campo de evidencia al lado; el esquema no tiene campo de razonamiento. Cuando se alcanza el límite, la herramienta no se ejecuta y se le pide al modelo que dé su veredicto; los turnos se cortan 3 por encima del límite. Si el modelo se detiene sin dar un veredicto, se anota «sin veredicto» en el registro y el hallazgo queda como está: ningún hallazgo se da por refutado por defecto.
¿Cuál conviene usar en cada caso?
La regla es sencilla: elige según lo que necesite quien vaya a leer el JSON, y revisa igualmente cada respuesta que llegue. Un resumen para los casos habituales:

Lo que tienen en común las tarjetas: el esquema garantiza la forma, y el resto depende de tu código. Las reglas que un esquema no puede expresar, como la longitud de un texto, el rango de un número o la coherencia de un campo con otro, se comprueban en el código cuando llega la respuesta. Si la comprobación falla, pide la respuesta una vez más; si el segundo intento también falla, no te tragues el error, lánzalo de forma explícita. El Claude Agent SDK funciona igual: vuelve a pedir la salida que no encaja y devuelve un error cuando se alcanza el límite de intentos. Contamos este tipo de comprobaciones, que se ejecutan después de la respuesta del modelo, en el artículo sobre harness engineering.
Preguntas frecuentes
¿Qué son los structured outputs?
Una función de la API que vincula la respuesta de un LLM al esquema JSON que le das. Mientras genera tokens, el modelo no puede salirse del esquema (constrained decoding); la respuesta llega como JSON válido y con los campos del esquema. En OpenAI se activan dentro de text.format con json_schema y strict: true; en Anthropic, con output_config.format, y en Google, con response_format o response_json_schema. El esquema garantiza la forma de la respuesta, no que su contenido sea correcto.
¿Qué diferencia hay entre JSON mode y structured outputs?
El JSON mode solo asegura que la respuesta sea JSON válido; no garantiza qué campos traerá. Los structured outputs vinculan la respuesta al esquema que das: los campos obligatorios no faltan, y los tipos y los valores de enum son los del esquema. OpenAI ofrece los dos y recomienda structured outputs cuando están admitidos. En OpenAI, si el JSON mode está activado y la palabra «JSON» no aparece en el prompt, la API devuelve un error.
¿Qué diferencia hay entre function calling y structured outputs?
El function calling es el modelo llamando a una herramienta para que se haga algo; el JSON son los argumentos de esa herramienta. Los structured outputs, en cambio, son la forma de la respuesta final del modelo. La documentación de Google lo separa así: structured outputs para dar formato a la respuesta final, function calling para que se haga algo en mitad de la conversación. Para que también los argumentos de las herramientas cumplan el esquema, se usa strict: true en OpenAI y Anthropic, y el modo validated en Gemini.
¿Por qué tool_choice any da error en Claude?
En Opus 5.5 y Sonnet 5.5, los tipos de tool_choice que obligan al modelo a llamar a una herramienta, any y tool, devuelven un error 400. La vía que recomienda Anthropic es dejar tool_choice en auto y usar strict tool use, y structured outputs si necesitas una forma JSON fija. El prompt sigue influyendo en qué herramienta se llama. Haiku 5.5 todavía acepta que se le obligue, pero en una llamada forzada el modelo llama directamente a la herramienta, sin pensar.
¿Cómo se le da a Gemini un esquema JSON?
En la Interactions API, le das a response_format un type: "text", un mime_type: "application/json" y un schema; en generateContent, lo mismo se hace con response_json_schema (responseFormat.text.schema en REST). Gemini admite un subconjunto de JSON Schema e ignora las reglas que no admite; OpenAI y Anthropic, en cambio, devuelven un error ante una regla así. Google recomienda validar igualmente los valores de la respuesta en tu aplicación.
¿Garantizan los structured outputs que el contenido sea correcto?
No. Google y OpenAI lo dicen claramente en su documentación: el esquema garantiza la forma de la respuesta, no que los valores sean correctos. Anthropic también enumera los casos en los que el esquema puede no cumplirse: un rechazo, una respuesta cortada en max_tokens y las mayúsculas y minúsculas de los valores de enum. Mira primero por qué terminó la respuesta y luego valídala en el código; si no pasa, pídela una vez más.
¿Cómo obtiene diffox JSON de un modelo?
diffox es una herramienta de revisión de código con IA y pide a los modelos sus respuestas con un esquema JSON. El paso que comprueba un hallazgo contra el propio repositorio es un bucle de agente: tool_choice está en auto, las llamadas a herramientas tienen un límite de 8 y el bucle termina con una herramienta estricta, submit_verdict. Una respuesta que no se puede parsear como JSON no cuenta como éxito; pasa como error al registro de la revisión. Si el modelo se detiene sin dar un veredicto, el hallazgo queda como está.