
Fine-Tuning para salida estructurada: Más allá del modo JSON hacia esquemas garantizados
El modo JSON te da JSON válido. Fine-tuning te da cumplimiento de esquema garantizado — cada campo, cada tipo, cada vez. Así es cómo entrenar modelos que producen exactamente la estructura que tu app espera.
Tu app espera un objeto JSON con exactamente 8 campos, tipos específicos para cada campo, enums para dos de ellos, y un array anidado de objetos con su propio esquema. Le pides a GPT-4 que lo produzca. La mayor parte del tiempo, obtienes lo que pediste. A veces obtienes 7 campos. Ocasionalmente obtienes un string donde esperabas un entero. De vez en cuando, el modelo inventa un valor de enum que no existe.
Al 95% de cumplimiento de esquema, 1 de cada 20 llamadas a la API produce salida que tu parser no puede manejar. Si tu app hace 10,000 llamadas de salida estructurada por día, eso son 500 fallos. Cada día. Tu código de manejo de errores se vuelve más complejo que tu lógica de negocio real. Añades reintentos, prompts de fallback, correctores de post-procesamiento. El sistema funciona, pero es frágil — sostenido con cinta adhesiva y loops de reintento.
Fine-tuning cambia la ecuación. Un modelo entrenado con 500-1,000 ejemplos de tu esquema exacto no se desvía, no alucina campos, no inventa valores de enum. El cumplimiento de esquema pasa de 95% con prompting a 99.5%+ con fine-tuning. A 10,000 llamadas por día, esa es la diferencia entre 500 fallos y 50.
El espectro de salida estructurada
Hay una jerarquía de enfoques de salida estructurada, del menos confiable al más:
Nivel 1: Basado en prompt ("Por favor produce JSON")
Pídele al modelo que produzca JSON en tu prompt. Incluye un ejemplo. Espera lo mejor.
- Tasa de cumplimiento: 80-90%
- Modos de fallo: JSON inválido (comillas faltantes, comas finales), campos faltantes, tipos incorrectos, campos extra, envuelto en markdown
- Cuándo usar: Solo para prototipado
Nivel 2: Modo JSON
El modo JSON de OpenAI, o configuraciones equivalentes en otras APIs. Fuerza al modelo a producir JSON sintácticamente válido.
- Tasa de cumplimiento: 95-98% (JSON válido, pero el cumplimiento de esquema varía)
- Modos de fallo: JSON válido que no coincide con tu esquema — campos requeridos faltantes, nombres de campo incorrectos, tipos no coincidentes, campos extra
- Cuándo usar: Cuando necesitas JSON válido pero puedes tolerar desviación de esquema
Nivel 3: Function Calling / API de Structured Outputs
Los structured outputs de OpenAI con un JSON schema, o endpoints de function-calling. La API impone el esquema a nivel de decodificación.
- Tasa de cumplimiento: 99%+ para estructura de esquema
- Modos de fallo: Estructura correcta pero valores incorrectos — valores de enum alucinados, contenido semánticamente incorrecto, strings vacíos donde se espera contenido
- Cuándo usar: Cuando necesitas cumplimiento de esquema desde APIs en la nube y puedes aceptar el costo por token
Nivel 4: Modelo ajustado
Un modelo entrenado con cientos de ejemplos de tu esquema exacto. Conoce los nombres de campos, tipos, valores válidos y expectativas semánticas.
- Tasa de cumplimiento: 99.5-99.9%
- Modos de fallo: Casos extremos raros en entradas muy fuera de la distribución de entrenamiento
- Cuándo usar: Sistemas de producción con alto volumen donde la confiabilidad y el costo importan
Nivel 5: Modelo ajustado + decodificación restringida
Modelo ajustado con decodificación restringida (gramática de llama.cpp, Outlines, o guidance) que hace imposibles los tokens inválidos.
- Tasa de cumplimiento: 100% cumplimiento estructural
- Modos de fallo: JSON estructuralmente perfecto con valores semánticamente incorrectos (raro con fine-tuning)
- Cuándo usar: Cuando necesitas cero fallos estructurales y estás corriendo inferencia local
Por qué el prompting tiene un techo
El problema fundamental con la salida estructurada basada en prompts: el modelo está generando tokens secuencialmente, y nada le impide generar un token que viole tu esquema.
Cuando le pides a GPT-4 que produzca "status": "approved" | "denied" | "pending", el modelo puede generar "status": "approved" en una llamada y "status": "Approved" en la siguiente. O "status": "approve". Cada uno es un string JSON válido — el modelo no tiene restricción que diga "solo estos tres valores exactos son aceptables."
Prompts más largos con esquemas más detallados ayudan, pero tienen rendimientos decrecientes:
- Descripción de esquema de 1 línea: ~85% cumplimiento
- Esquema detallado con ejemplos: ~92% cumplimiento
- Esquema + ejemplos few-shot + restricciones explícitas: ~95-97% cumplimiento
No puedes llegar al 99.5% con prompts. La generación de tokens del modelo es probabilística. Sin importar cuán bueno sea tu prompt, la probabilidad de generar el token incorrecto nunca es exactamente cero.
Fine-tuning cambia la distribución de probabilidad del modelo directamente. Después de entrenar con 500 ejemplos donde status es siempre exactamente "approved", "denied", o "pending", el modelo asigna probabilidad cercana a cero a cualquier otro valor para ese campo. El cumplimiento viene de los pesos, no del prompt.
Construyendo datasets de entrenamiento que cumplen con el esquema
La calidad de tus datos de entrenamiento determina la calidad de tu cumplimiento de esquema. Así es cómo construir un dataset que produce salida estructurada confiable.
Paso 1: Define tu esquema formalmente
Comienza con una especificación JSON Schema:
{
"type": "object",
"required": ["id", "status", "score", "category", "findings", "metadata"],
"properties": {
"id": {"type": "string", "pattern": "^RPT-[0-9]{6}quot;},
"status": {"type": "string", "enum": ["approved", "denied", "pending", "review"]},
"score": {"type": "number", "minimum": 0, "maximum": 100},
"category": {"type": "string", "enum": ["financial", "operational", "compliance", "technical"]},
"findings": {
"type": "array",
"items": {
"type": "object",
"required": ["description", "severity", "recommendation"],
"properties": {
"description": {"type": "string"},
"severity": {"type": "string", "enum": ["low", "medium", "high", "critical"]},
"recommendation": {"type": "string"}
}
}
},
"metadata": {
"type": "object",
"required": ["analyst", "date", "version"],
"properties": {
"analyst": {"type": "string"},
"date": {"type": "string", "format": "date"},
"version": {"type": "string"}
}
}
}
}
Paso 2: Genera ejemplos diversos y válidos
Necesitas 500-1,000 ejemplos de entrenamiento. Cada uno debe ser una instancia perfectamente válida de tu esquema. Hay tres enfoques para generarlos:
Desde datos de producción: Si tienes datos existentes que siguen este esquema (de una base de datos, de salidas previas de API), conviértelos en ejemplos de entrenamiento. Esta es la mejor fuente porque refleja distribuciones de valores del mundo real.
Desde GPT-4 con validación: Usa GPT-4 para generar ejemplos diversos, luego valida cada uno contra tu JSON Schema. Descarta cualquiera que no valide. Al 95% de cumplimiento de GPT-4, descartarás aproximadamente 1 de cada 20 — aceptable para generación de datasets. Pasa los ejemplos validados por tu parser real para verificar doblemente.
Generación programática: Escribe un script que genere instancias aleatorias válidas de tu esquema. Esto garantiza validez estructural pero puede carecer de coherencia semántica. Úsalo para complementar ejemplos reales, no como tu única fuente.
Paso 3: Varía las entradas, no solo las salidas
Tus datos de entrenamiento necesitan entradas diversas pareadas con salidas que cumplen el esquema. Si cada entrada es "Analiza este informe financiero," el modelo aprende el esquema pero no puede generalizar a nuevas frases de entrada.
Incluye variaciones:
- Diferentes longitudes y complejidad de entrada (una oración a tres párrafos)
- Diferentes formatos de entrada (solicitudes formales, preguntas casuales, instrucciones estructuradas)
- Diferentes niveles de contexto (información completa vs. información parcial)
- Casos extremos (entradas ambiguas, entradas con información faltante, entradas que deberían producir arrays vacíos)
Paso 4: Incluye casos extremos explícitamente
Los casos donde los modelos con prompts fallan más son los casos extremos. Asegúrate de que tus datos de entrenamiento cubran:
- Arrays vacíos: Cuando
findingsdebería ser[]porque no hay hallazgos - Valores mínimos/máximos:
score: 0yscore: 100— los modelos tienden a evitar extremos - Valores de string largos: Campos con valores de más de 200 caracteres (los modelos a veces truncan)
- Caracteres especiales: Comillas, saltos de línea, unicode en campos de string
- Manejo de null: Si tu esquema permite campos nullables, incluye ejemplos con valores null
Paso 5: Formatea como pares de entrenamiento
Estructura cada ejemplo como una conversación system/user/assistant:
{
"messages": [
{
"role": "system",
"content": "You are a report analysis system. Analyze the provided report and output a structured assessment."
},
{
"role": "user",
"content": "Report: Q3 revenue declined 12% due to supply chain disruptions. Two compliance violations were identified in the procurement process. The technical infrastructure handled the load increase from the holiday promotion without incidents."
},
{
"role": "assistant",
"content": "{\"id\": \"RPT-004521\", \"status\": \"review\", \"score\": 62, \"category\": \"operational\", \"findings\": [{\"description\": \"Revenue decline of 12% attributed to supply chain disruptions\", \"severity\": \"high\", \"recommendation\": \"Diversify supplier base and establish backup procurement channels\"}, {\"description\": \"Two compliance violations in procurement process\", \"severity\": \"medium\", \"recommendation\": \"Audit procurement workflows and retrain staff on compliance requirements\"}], \"metadata\": {\"analyst\": \"system\", \"date\": \"2026-03-15\", \"version\": \"1.0\"}}"
}
]
}
La respuesta del asistente debe ser JSON crudo — sin formato markdown, sin texto explicativo, sin bloques de código. Entrena al modelo para producir JSON y nada más.
Midiendo el cumplimiento de esquema
No te fíes de "se ve bien." Mide el cumplimiento programáticamente:
Cumplimiento estructural
Pasa cada salida por un validador de JSON Schema. Cuenta el porcentaje que pasa. Esto captura campos faltantes, tipos incorrectos, valores de enum inválidos y violaciones de estructura de esquema.
Cumplimiento semántico
La validez estructural no significa que el contenido es correcto. Un modelo puede producir "status": "approved", "score": 0 — estructuralmente válido pero semánticamente sospechoso (¿aprobado con una puntuación de 0?). Construye verificaciones semánticas:
- ¿Los valores de enum correlacionan correctamente con otros campos?
- ¿Los valores numéricos están en rangos realistas?
- ¿Los valores de string contienen contenido relevante (no lorem ipsum ni strings vacíos)?
- ¿Las longitudes de arrays son apropiadas para la entrada?
Métricas de comparación
Después del fine-tuning, compara contra tu línea base:
| Métrica | GPT-4o con prompts | 8B ajustado | 8B ajustado + gramática |
|---|---|---|---|
| JSON válido | 99.5% | 99.8% | 100% |
| Cumplimiento de esquema | 93-96% | 99.2-99.7% | 100% |
| Precisión semántica | 90-94% | 93-97% | 93-97% |
| Tokens promedio/respuesta | 350 | 280 | 280 |
| Costo por 1K llamadas | $2.50-$8.00 | $0 (local) | $0 (local) |
El modelo ajustado tiene más cumplimiento de esquema, usa menos tokens (no rellena respuestas con verbosidad innecesaria), y no cuesta nada por llamada.
Combinando fine-tuning con decodificación restringida
Para aplicaciones donde incluso 99.5% no es suficiente — transacciones financieras, registros médicos, documentos legales — combina fine-tuning con decodificación restringida.
La decodificación restringida (también llamada generación guiada por gramática) restringe los tokens de salida del modelo a solo aquellos que producen salida válida según una especificación de gramática. llama.cpp soporta esto vía gramáticas GBNF. Outlines y guidance proporcionan funcionalidad similar para inferencia basada en Python.
Una gramática GBNF para tu esquema:
root ::= "{" ws "\"id\":" ws string "," ws "\"status\":" ws status "," ws "\"score\":" ws number "," ws "\"category\":" ws category "," ws "\"findings\":" ws findings "," ws "\"metadata\":" ws metadata ws "}"
status ::= "\"approved\"" | "\"denied\"" | "\"pending\"" | "\"review\""
category ::= "\"financial\"" | "\"operational\"" | "\"compliance\"" | "\"technical\""
...
Con un modelo ajustado + gramática, obtienes:
- 100% cumplimiento estructural (la gramática previene estructuras inválidas)
- 99.5%+ precisión semántica (fine-tuning enseña valores correctos)
- Cero costo por token (inferencia local)
- Inferencia rápida (las restricciones de gramática realmente aceleran la generación ligeramente al reducir el espacio de búsqueda de tokens)
La gramática maneja la estructura. El fine-tuning maneja el contenido. Juntos, producen salida en la que tu parser puede confiar incondicionalmente.
Impacto en el rendimiento
Los modelos ajustados generando salida estructurada son típicamente más rápidos que los modelos con prompts, por dos razones:
-
Salidas más cortas: Los modelos con prompts frecuentemente incluyen texto explicativo, formato markdown, o meta-comentarios alrededor del JSON. Los modelos ajustados producen solo JSON crudo. Esto reduce tokens de salida en 20-40%.
-
Generación más confiada: Cuando un modelo "conoce" el esquema (del fine-tuning), genera cada token con mayor confianza. Menos retrocesos en el proceso de muestreo. Time-to-first-token mediblemente menor y tasa de generación de tokens más rápida.
Benchmark en un esquema de clasificación de tickets de soporte (8 campos, 2 enums, 1 objeto anidado):
| Configuración | Tokens promedio de salida | Latencia promedio | Validez estructural |
|---|---|---|---|
| GPT-4o + prompt | 420 tokens | 1.8s | 96.2% |
| GPT-4o + API structured outputs | 310 tokens | 1.4s | 99.8% |
| 8B ajustado (Ollama) | 270 tokens | 0.4s | 99.5% |
| 8B ajustado + gramática | 265 tokens | 0.35s | 100% |
El modelo local ajustado es 4x más rápido y produce salida más corta y más confiable.
Errores comunes en fine-tuning de esquemas
Error 1: Datos de entrenamiento inconsistentes
Si el 80% de tus ejemplos de entrenamiento usan "date": "2026-03-15" y el 20% usan "date": "March 15, 2026", el modelo aprende ambos formatos y cambia probabilísticamente entre ellos. Cada campo, cada formato, cada convención debe ser 100% consistente en todos los ejemplos de entrenamiento.
Error 2: No entrenar con casos vacíos/null
Si tu esquema permite campos opcionales o arrays vacíos, pero todos los ejemplos de entrenamiento tienen valores poblados, el modelo alucinará valores en lugar de producir null o []. Incluye 10-15% de ejemplos con cada campo opcional establecido en null y cada array vacío.
Error 3: Entradas excesivamente homogéneas
Si todas tus entradas de entrenamiento son aproximadamente de la misma longitud, complejidad y tema, el modelo se sobreajusta a esa distribución de entrada. Cuando ve una entrada significativamente diferente en producción, el cumplimiento de esquema cae. Varía tus entradas agresivamente.
Error 4: Entrenar con JSON formateado bonito
Si entrenas con JSON formateado con indentación, el modelo desperdicia tokens en espacios en blanco. Entrena con JSON minificado: "id":"RPT-004521","status":"approved",.... Esto reduce tokens de salida en 15-25% y mejora la velocidad de generación.
Error 5: No validar los datos de entrenamiento
Si incluso un ejemplo de entrenamiento tiene una violación de esquema, el modelo aprende que las violaciones son aceptables. Valida cada ejemplo de entrenamiento contra tu JSON Schema antes de incluirlo en el dataset. Automatiza esto. Sin excepciones.
Primeros pasos
- Define tu esquema objetivo como un JSON Schema formal
- Recopila o genera 500-1,000 ejemplos de entrenamiento validados
- Formatea como pares de entrenamiento JSONL (system/user/assistant)
- Valida cada ejemplo programáticamente — descarta cualquiera con violaciones de esquema
- Ajusta en Ertas — Llama 3.1 8B o Qwen 2.5 7B son opciones fuertes para salida estructurada
- Evalúa con más de 100 ejemplos reservados, midiendo cumplimiento estructural y semántico
- Despliega vía Ollama, opcionalmente con gramática GBNF para estructura garantizada
- Monitorea salidas de producción y añade casos de fallo a tus datos de entrenamiento para la próxima iteración
El objetivo es simple: el parser JSON de tu app nunca debería fallar por culpa del modelo. Fine-tuning te lleva ahí.
Ship AI that runs on your users' devices.
Ertas early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.
Lectura adicional
- Fine-Tuning para salida JSON — Guía fundamental para entrenar modelos que producen JSON válido y parseable.
- Fine-Tuning para Tool Calling: Cómo construir agentes de IA confiables con modelos pequeños — Salida estructurada aplicada específicamente a esquemas de tool-calling en pipelines de agentes.
- Cómo ajustar un LLM — Tutorial completo del proceso de fine-tuning desde la preparación del dataset hasta el despliegue.
Ship AI that runs on your users' devices.
Early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.
Keep reading

Fine-Tuning Phi-4: Microsoft's Best Small Model for Enterprise Tasks
Phi-4 14B outperforms GPT-4 on math benchmarks while running 15x faster on local hardware. Here's how to fine-tune it for classification, extraction, and structured output tasks.

Fine-Tuning Qwen 2.5 for Multilingual Applications
Qwen 2.5 covers 29 languages with 18 trillion training tokens. Here's how to fine-tune it for multilingual classification, support, and content generation without separate models per language.

Fine-Tuning Gemma 3: Google's Lightweight Model for On-Device Deployment
Gemma 3 is optimized for on-device inference — phones, tablets, edge hardware. Here's how to fine-tune it for mobile AI features and IoT applications that run without a server.