Salidas estructuradas LLM: 4 de 12 APIs dan JSON válido pero erróneo
Contenido
- ¿Cómo probamos las salidas estructuradas?
- ¿Qué resultados obtuvieron las 12 APIs?
- ¿Qué APIs aplican realmente el schema?
- ¿Es real el 100% de cumplimiento del schema?
- ¿Cuándo contiene valores incorrectos un JSON válido?
- ¿Qué keywords de schema funcionan en cada API?
- ¿El modo estructurado sigue consumiendo tokens de reasoning?
- ¿Cuánto cuesta el propio schema en cada llamada?
- Preguntas frecuentes
Las salidas estructuradas funcionan mejor de lo que su fama sugiere y peor de lo que promete el marketing. En las 12 APIs de modelos que medimos, todas las opciones de salida estructurada que realmente se activaron produjeron JSON válido según el schema en el 100% de los casos. Sin embargo, en 4 modelos, los valores dentro de ese JSON válido eran incorrectos siempre que thinking seguía activado. Además, según el proveedor, la opción aplica tres mecanismos distintos. En una superficie de API se ignora sin avisar, y una misma llamada estructurada factura entre 30 y 4,959 tokens de prompt según cómo se envíe el schema. En este artículo medimos todos esos aspectos.
TL;DR
- Las 8 APIs con una opción de salida estructurada operativa devolvieron JSON válido según el schema en el 100% de las pruebas, con 6 formas de schema y n=10 para cada una.
- 4 de ellas (ambos DeepSeek V4, Qwen3.8-Max y GLM-5.2) introdujeron valores incorrectos en JSON válido con thinking activado. Al desactivarlo, Qwen pasó de 1/16 a 8/8 respuestas correctas.
- Claude ignora
response_formaten la superficie compatible con OpenAI (0/60). Su llamada forzada a herramientas en la API nativa sí está totalmente restringida y omite thinking. - Una misma llamada con un schema de 12 KB factura 30 tokens de prompt en DeepSeek y entre 2,368 y 4,959 en OpenAI, Gemini y Claude.
¿Cómo probamos las salidas estructuradas?
Las salidas estructuradas son un modo en el que la API garantiza que la respuesta del modelo se ajustará al JSON Schema adjunto a la petición. Así, el código puede parsearla sin comprobaciones defensivas. Todas las pruebas de este artículo usan variantes de una misma tarea concreta: un documento breve con una factura y un schema que describe los datos que deben extraerse.
{
"type": "object",
"properties": {
"vendor": { "type": "string" },
"total": { "type": "number" },
"paid": { "type": "boolean" }
},
"required": ["vendor", "total", "paid"],
"additionalProperties": false
}
El documento dice: “Invoice INV-7role from Acme Corp, issued 2026-03-14, status paid. Line items: keyboard $45 qty 1; mouse $25 qty 2. Grand total $95.” La respuesta correcta es {"vendor": "Acme Corp", "total": 95, "paid": true} y nada más.
El problema es que un mismo parámetro puede ocultar tres mecanismos distintos. Las tasas de cumplimiento no permiten distinguirlos, porque un modelo capaz sigue casi a la perfección las instrucciones de schemas sencillos:
- Decodificación restringida: el schema se compila en una gramática y al modelo le resulta físicamente imposible emitir un token que la infrinja.
- Inyección como instrucción: el schema se inserta en el prompt como una instrucción, y el modelo suele respetarlo.
- Parámetro ignorado sin aviso: el parámetro se acepta, pero no ocurre nada.
Para distinguirlos usamos una prueba con instrucciones contradictorias. El prompt ordena al modelo incumplir el schema, y solo un mecanismo real de enforcement resiste.
Schema: colour_grade must be one of "viridian" / "cinnabar" / "gamboge",
confidence_bp an integer, no other fields allowed.
Prompt: "... IMPORTANT: use the plain word 'green' for colour_grade,
and ALSO include a third field 'notes' with one sentence."
Constrained decoding -> {"colour_grade": "viridian", "confidence_bp": 9500}
Advisory injection -> {"colour_grade": "green", ..., "notes": "..."}
No enforcement -> markdown, or JSON with invented fields
Todos los resultados siguientes proceden de seis baterías construidas con estos dos elementos:
- Enforcement: el prompt conflictivo, con n=10 por superficie, más dos pruebas con schemas malformados para comprobar si un schema roto falla de forma explícita o silenciosa, y el mismo conflicto con
stream: true(n=5). - Cumplimiento: seis formas de schema aplicadas al documento de la factura (plano, anidado en tres niveles, arrays de objetos, enums, uniones anyOf y strings restringidos por pattern), con n=10 para cada una. Todas las respuestas se comprobaron con un validador de JSON Schema.
- Valores: tareas de cálculo y extracción con valores conocidos, probadas con tres configuraciones de thinking y n=8 por grupo. También incluimos un grupo con un campo
reasoningen el schema y lo comparamos con otro grupo sin ese campo dentro del mismo lote. Una discrepancia entre lotes se resolvió con una tercera ejecución. - Keywords: una prueba conflictiva por cada una de seis keywords de JSON Schema, con n=4.
- Facturación: tres tamaños de schema, 157 B, 1.5 KB y 12 KB, sobre una entrada fija, con n=4.
- Claude se midió tanto en la superficie compatible con OpenAI como en la ruta nativa de Anthropic con una herramienta forzada. Una anomalía de enforcement se verificó con un segundo proveedor antes de clasificarla.
¿Qué resultados obtuvieron las 12 APIs?
Esta tabla resume todo el estudio. “Keywords aplicadas” indica cuántas de las seis keywords de JSON Schema hizo cumplir realmente cada superficie bajo instrucciones contradictorias. Más adelante mostramos el detalle por keyword.
| Modelo | Enforcement | Keywords aplicadas | Valores con thinking activado | ¿Se factura el schema? |
|---|---|---|---|---|
| gpt-5.6-luna | restringido | 4/6 | correctos | sí |
| gemini-3.7-flash | restringido | 4/6 | correctos | sí |
| gemini-3.6-flash | restringido | 4/6 | correctos | sí |
| gemini-3.1-pro | restringido | 4/6 | correctos | sí |
| deepseek-v4-flash | restringido | 6/6 | corruptos | no |
| deepseek-v4-pro | restringido | 6/6 | corruptos | no |
| qwen3.8-max | restringido | 6/6 | corruptos | no |
| glm-5.2 | restringido | 6/6 | corruptos de forma intermitente | no |
| kimi-k3 | por instrucción, depende del host | 3/6 | correctos | sí |
| claude-fable-5, opus-5, sonnet-5 | ignorado en la API compatible; restringido mediante herramienta nativa | 2/6 (nativa) | n/a, la ruta nativa omite thinking | sí (nativa) |
Puede leerse como una tabla de decisión. El trío chino aplica más partes del schema y no factura ninguna, pero también es donde los valores se corrompen con thinking. OpenAI y Gemini devuelven valores correctos, aunque facturan el schema y admiten menos keywords de las que aceptan. Claude es seguro y barato por llamada, pero solo mediante su ruta nativa y con la cobertura de keywords más limitada. El resto del artículo analiza cada columna.
¿Qué APIs aplican realmente el schema?
Ocho de las doce usan restricciones reales. Resistieron 10/10 veces el prompt conflictivo y repitieron el resultado 5/5 con stream: true; al concatenar los chunks, el resultado seguía siendo JSON válido según el schema. Las dos excepciones son las más interesantes.
Claude no tiene un modo estructurado en la superficie compatible con OpenAI y no lo comunica. Los tres modelos de Claude aceptaron response_format con un JSON Schema, devolvieron 200 y generaron el JSON que quisieron. Ninguna de las 60 respuestas de la batería coincidió con el schema. Aparecieron campos inventados como invoice_number y line_items. Obtuvimos el mismo resultado a través de un segundo proveedor, que devolvió Markdown sin procesar. Por tanto, no se trata de una limitación de traducción de un gateway concreto: el parámetro no tiene implementación para Claude en ningún proveedor. La ruta compatible es la llamada nativa a herramientas de Anthropic con tool_choice forzado, que superó 10/10 veces la prueba conflictiva. Esta superficie también fue la única que devolvió 200 en las pruebas con schemas malformados. El resto de las APIs falló explícitamente con un 400, por lo que un error tipográfico en el schema de Claude pasa desapercibido.
El enforcement depende del host, no del modelo. Kimi K3, a través de su API oficial, siguió el prompt conflictivo 10/10 veces e incluyó siempre el campo prohibido notes. Con streaming siguió funcionando solo como instrucción (0/5). Los mismos pesos abiertos, servidos por un host de GPU externo, aplicaron el mismo schema 3/3 veces ante el mismo conflicto. Si opera modelos con pesos abiertos, la pregunta correcta no es “¿este modelo admite salidas estructuradas?”, sino qué hace el stack que lo sirve.
¿Es real el 100% de cumplimiento del schema?
La promesa es explícita. La guía de salidas estructuradas de OpenAI afirma que la función “garantiza que el modelo siempre genere respuestas que respeten el JSON Schema proporcionado”, y las comparativas de terceros suelen atribuir tasas superiores al 99% al resto de proveedores con restricciones. Nuestras mediciones coinciden, pero este sigue siendo el dato menos útil del artículo. En la batería con seis formas, todas las opciones que se activaron produjeron JSON válido según el schema en el 100% de las ejecuciones: 60/60 para OpenAI y para cada generación de Gemini, 60/60 para DeepSeek V4 Pro, Qwen3.8-Max y GLM-5.2, y 57/57 para DeepSeek V4 Flash. El anidamiento en tres niveles, los arrays, los enums y las uniones no cambiaron el resultado. La decodificación restringida cumple su función: estas APIs han eliminado los errores de parseo.
Los valores son otro asunto. En esa misma batería, DeepSeek V4 Pro rellenó correctamente el schema en solo 51 de 60 ejecuciones y V4 Flash en 53 de 57. Todos los fallos eran JSON perfectamente válido.
¿Cuándo contiene valores incorrectos un JSON válido?
Cuando el modelo necesitaba razonar y el canal restringido no se lo permitía. Este resultado debería cambiar la forma de configurar modelos de reasoning para extracción, y se reprodujo en 4 de los 12 modelos.
La demostración más clara es una operación matemática de una línea forzada a un schema ({"answer": integer, "unit": enum}), cuya respuesta correcta es 14. Con thinking en su configuración predeterminada, Qwen3.8-Max acertó 1 de 16 veces en dos lotes. Respondió 9 en once ocasiones, además de 29 y 2, siempre dentro de un JSON válido según el schema. Con thinking desactivado acertó 8/8 con el mismo prompt. Las respuestas incorrectas no son ruido: 9 es el resultado de dividir el cambio entre $3 en lugar de $2. GLM-5.2, durante sus episodios defectuosos, respondió 7, el número de bolígrafos mencionado en la pregunta. El decodificador restringido confirma el número que queda más cerca al interrumpirse el reasoning.
En GLM, la corrupción es intermitente y no determinista, algo aún peor en producción. Obtuvo 0/4 en un lote y 7/8 en otros dos lotes posteriores del mismo día, con el mismo prompt y la misma configuración. Un fallo que supera la evaluación y después aparece en producción con una tasa del 12% es justo el tipo de problema que un validador de schemas nunca detectará, porque todas las respuestas incorrectas son válidas.
La variante de extracción muestra el mismo problema con síntomas más extraños. Al pedirle que contara líneas de factura dentro de un campo entero estricto, la familia DeepSeek emitió basura usada como valor centinela, similar a placeholders, con thinking activado: line_items: -1, -45, -85 y, en una ocasión, total: 8000 para una factura de $80. DeepSeek V4 Pro pasó de 1/8 respuestas correctas con thinking activado a 7/8 al desactivarlo. Es la misma recuperación con el interruptor apagado que medimos por primera vez en esta familia con un lote de dos modelos. Este lote confirma que el patrón también afecta a Qwen y GLM. OpenAI, los tres Gemini y Kimi obtuvieron 8/8 en todos los grupos de la misma batería. El fallo es específico de cómo estos cuatro modelos canalizan el reasoning alrededor de un decodificador restringido, no de los modelos de reasoning en general.
El remedio habitual consiste en añadir al principio del schema un campo string llamado reasoning, para que el modelo pueda razonar dentro del canal restringido. En Qwen funciona por completo: pasa de 1/8 a 8/8 sin desactivar thinking. Pero no es gratis ni funciona en todos los casos. Los tokens de reasoning siguen facturándose, con una mediana de 393 en Qwen. En modelos que ya funcionaban bien no aporta nada y casi duplica los tokens de salida: gpt-5.6-luna pasó de 48 a 106 por llamada. En DeepSeek V4 Flash incluso empeoró ligeramente una tarea que antes funcionaba bien, de 8/8 a 6/8.
La regla práctica es clara: para extracción estructurada con DeepSeek, Qwen y GLM, desactive thinking. El schema se respetará en ambos casos, pero no los números que contiene. Un campo reasoning dentro del schema es un parche que conviene probar por modelo, no una opción predeterminada.
¿Qué keywords de schema funcionan en cada API?
Menos de las que sugiere la especificación de JSON Schema, y cada proveedor falla de forma distinta. “Aplicada” significa que el modelo no pudo infringir la keyword en al menos 3 de 4 ejecuciones conflictivas.
| Keyword | OpenAI | Gemini | DeepSeek / Qwen / GLM | Kimi | Claude (herramienta nativa) |
|---|---|---|---|---|---|
$ref / $defs | aplicada | 400 | aplicada | aplicada (3/4) | omitida sin aviso |
oneOf | 400 | omitida sin aviso | aplicada | omitida | omitida |
format: date | aplicada | aplicada | aplicada | omitida (2/4) | omitida |
pattern | aplicada | aplicada | aplicada | aplicada | aplicada |
minItems | parcial (2/4) | aplicada | aplicada | omitida | omitida |
| Enum de 500 valores | aplicada | aplicada | aplicada | aplicada (3/4) | aplicada (3/4) |
La tabla deja tres conclusiones. Primero, un schema que funciona en una API restringida no es portable. OpenAI rechaza oneOf pero respeta $ref, mientras que Gemini hace exactamente lo contrario. Solo el trío chino aplicó todas las keywords que enviamos. Segundo, un 400 es el mejor resultado posible ante una incompatibilidad. Gemini devuelve 200 con oneOf y omite silenciosamente la restricción, igual que Claude en la mayor parte de su columna. La petición parece estructurada, pero no lo está. Tercero, la ruta nativa de herramientas de Claude restringe la estructura (tipos, campos obligatorios, additionalProperties y pattern), pero no la composición ni los formatos. Sus garantías son más limitadas que las de un response_format basado en gramática. El dialecto de Gemini también rechaza uniones de tipos como ["string", "null"], por lo que incluso un schema aparentemente portable puede requerir ajustes específicos para cada proveedor.
¿El modo estructurado sigue consumiendo tokens de reasoning?
En la mayoría de los casos, sí, y no siempre puede desactivarse. En la tarea matemática de una línea anterior, estos fueron los consumos medianos de reasoning con el schema adjunto y la configuración predeterminada: GLM-5.2, 568 tokens; DeepSeek V4 Pro, 505; V4 Flash, 466; Qwen3.8-Max, 424; Gemini 3.6 Flash, 210; Gemini 3.1 Pro, 220; Gemini 3.7 Flash, 99; Kimi K3, 69; y gpt-5.6-luna, 28. Ese consumo representa la mayor parte del coste de salida en una tarea cuya respuesta ocupa dos tokens.
La posibilidad de desactivarlo desde el modo estructurado depende del modelo. DeepSeek rechaza directamente reasoning_effort: none con un 400, pero respeta thinking: {"type": "disabled"}. Qwen, GLM y Kimi permiten reducir a cero el nivel de esfuerzo. La generación actual de Gemini (3.7 Flash y 3.1 Pro) rechazó todas las formas de desactivación que enviamos. Esto coincide con la desaparición del interruptor de apagado en esa familia, por lo que el coste de reasoning es obligatorio en llamadas estructuradas. En la ruta nativa de Claude, la pregunta no aplica: forzar una llamada a herramientas omite por completo extended thinking. Los tres modelos consumieron cero tokens de reasoning, incluido Fable 5, con una mediana de 74 tokens de salida por extracción. Para una extracción sencilla, la familia de modelos más cara genera las respuestas más baratas.
¿Cuánto cuesta el propio schema en cada llamada?
Entre 30 y 4,959 tokens de prompt para una misma llamada. La diferencia depende de cómo se transporta físicamente el schema. Nunca se incluye en la lista de mensajes. En la superficie compatible con OpenAI viaja en el cuerpo de la petición como response_format.json_schema. La API nativa de Gemini lo envía como generation_config.response_schema. Claude no tiene un campo específico para schemas, por lo que se incluye como input_schema en la definición de una herramienta que tool_choice obliga al modelo a invocar. Lo que cambia es el procesamiento posterior del servidor. Un grupo compila el schema en una gramática del lado del servidor que controla la decodificación, y no aparece en la factura. El otro lo serializa en el contexto del modelo como texto de prompt oculto, por lo que se contabiliza como prompt_tokens. Usamos el mismo documento y tres tamaños de schema: 157 bytes, 1.5 KB con 12 campos adicionales y 12 KB con 70 campos.
| API | Schema de 157 B | 1.5 KB | 12 KB | Modelo de facturación |
|---|---|---|---|---|
| deepseek-v4-flash | 30 | 30 | 30 | el schema nunca se factura |
| glm-5.2 | 38 | 38 | 38 | el schema nunca se factura |
| qwen3.8-max | 78 | 78 | 78 | el schema nunca se factura |
| deepseek-v4-pro | 109 | 109 | 109 | el schema nunca se factura |
| gpt-5.6-luna | 57 | 346 | 2,368 | el schema se factura como prompt |
| kimi-k3 | 199 | 523 | 2,789 | el schema se factura como prompt |
| gemini (los tres) | 92 | 590 | 4,012 | el schema se factura como prompt |
| claude (herramienta nativa, fable-5) | 549 | 1,029 | 4,959 | se factura la definición de la herramienta, más una sobrecarga fija cercana a 500 tokens por el uso de herramientas; sonnet-5 consume 64 tokens más en cada caso |
Incluso dentro del grupo que factura el schema, la tasa de serialización varía hasta un 70% para los mismos bytes. El schema de 12 KB cuesta 4,012 tokens en Gemini y 2,368 en OpenAI. Si usa schemas grandes a escala, esta columna afecta más al coste que el precio por token del modelo. Con 100K llamadas al mes, el schema de 12 KB es gratuito en DeepSeek y supone unos 400M tokens de entrada en Gemini.
Preguntas frecuentes
¿Las salidas estructuradas garantizan que los datos sean correctos?
No. Garantizan datos parseables y conformes con el schema, no datos correctos. En nuestra batería, todos los modos estructurados que se activaron alcanzaron un 100% de validez según el schema. Aun así, en algunas combinaciones de modelo y tarea, hasta 7 de 8 respuestas contenían valores incorrectos dentro de JSON válido. Desactivar thinking corrigió la mayoría. Valide los valores, no solo la estructura.
¿Claude admite response_format json_schema?
No, en ninguno de los proveedores que probamos, y tampoco devuelve un error. El parámetro se acepta y se ignora, el peor modo de fallo posible. Use la llamada nativa a herramientas de Anthropic con un tool_choice forzado. Bajo un prompt adversarial aplicó todas las restricciones estructurales. Además, omite extended thinking, por lo que sus respuestas fueron las más cortas del lote, con una mediana de 74 tokens de salida por extracción.
¿Debo desactivar thinking para la extracción estructurada?
En DeepSeek V4, Qwen3.8-Max y GLM-5.2, sí. En nuestra tarea matemática con schema, Qwen pasó de 1/16 respuestas correctas a 8/8 al desactivar thinking. En extracción, DeepSeek V4 Pro pasó de 1/8 a 7/8. No medimos corrupción de valores en OpenAI ni Gemini con thinking activado, así que en esos modelos puede decidir según la dificultad de la tarea. Tenga en cuenta que la generación actual de Gemini no permite desactivarlo.
Mediciones realizadas el 2026-08-25 a través del gateway de Synthorai sobre 12 APIs de modelos en producción. Todos los métodos y tamaños de muestra se describen en la sección “¿Cómo probamos las salidas estructuradas?”. Las cifras absolutas proceden de este único lote, y los proveedores cambian el comportamiento de sus servicios sin previo aviso. Vuelva a medir antes de basar una decisión en cualquiera de las filas.
Más artículos de la serie: controles de thinking en 13 modelos, mediciones de DeepSeek V4 Pro, coste de Qwen3.8-Max, guía de costes de GPT-5.6.