Cómo tipar las respuestas de una LLM con Zod + TypeScript
Tiempo estimado de lectura: 4 min
- Valida en runtime: TypeScript desaparece en runtime; usa Zod para convertir datos inciertos en contratos verificables.
- Esquema primero: diseña el esquema Zod como fuente única, extrae tipos con z.infer<> y serializa el esquema en el prompt.
- Elige parse vs safeParse: .parse() para fallos severos, .safeParse() para autocorrección y reintentos.
- Producción robusta: logging contextual, reintentos limitados, métricas y SLOs para gestionar errores LLM.
Saber cómo tipar las respuestas de una LLM con Zod + TypeScript aparece en las primeras líneas porque es lo que evita que un agente, workflow o microservicio se rompa en producción. El modelo devuelve JSON, a menudo; nunca con la forma exacta que esperabas. .parse() y z.infer<> no son trucos: son la defensa que convierte datos inciertos en contratos verificables.
Resumen rápido (lectores con prisa)
Qué: Usa Zod para validar y transformar respuestas de LLM en runtime.
Cuándo: Siempre que vayas a persistir datos o ejecutar acciones críticas basadas en la salida de una LLM.
Por qué importa: TypeScript no valida en runtime; sin validación, datos malformados pueden romper sistemas en producción.
Cómo funciona: Diseña el esquema Zod como fuente única, extrae tipos con z.infer<>, limpia la salida si es necesario y valida con .parse() o .safeParse().
Por qué JSON.parse() + as es una trampa (y qué rompe)
El patrón clásico:
const obj = JSON.parse(response) as MyType;
Es práctico. Es una mentira. TypeScript desaparece en runtime; as no valida nada. Los fallos típicos de LLMs:
- JSON envuelto en bloques Markdown (“`json … “`).
- Strings donde esperabas números (“15” vs 15).
- Campos faltantes, claves renombradas o truncamiento por token limit.
- Estructuras válidas pero semánticamente inválidas (amount: -5).
Si confías en casts, esos problemas llegan a tu base de datos o a la lógica que ejecuta acciones. Validación en runtime es obligatoria.
El patrón sólido: esquema primero, prompt segundo
- Diseña el esquema Zod como la única fuente de verdad.
- Extrae el tipo estático con
z.infer<>. - Incluye una versión legible del esquema en el prompt.
- Valida la respuesta con
.safeParse()/.parse()antes de usarla.
Este patrón convierte la salida estocástica de la LLM en una entrada controlada para tu sistema.
Ejemplo práctico mínimo
import { z } from 'zod';
const TaskSchema = z.object({
title: z.string().min(5),
priority: z.enum(['low','medium','high']),
estimatedHours: z.number().positive().optional(),
});
type Task = z.infer;
Al pedir al modelo que devuelva JSON, serializa el esquema (o su forma) en el prompt para guiar la salida.
Limpieza defensiva y parse()
Los LLMs suelen añadir markdown. Limpia antes de parsear:
function cleanJson(raw: string) {
return raw.replace(/```json\n?|\n?```/g, '').trim();
}
const parsed = JSON.parse(cleanJson(rawOutput));
const valid = TaskSchema.parse(parsed); // lanza ZodError si falla
Usa .parse() cuando quieras detener el flujo y tratar la anomalía como error severo (útil en endpoints HTTP que deben devolver 4xx/5xx claros).
.safeParse() y auto‑corrección de prompts
En agentes autónomos o workflows escalables (n8n, agentes con herramientas), es preferible no lanzar. .safeParse() devuelve { success, data?, error? } para manejar fallos:
const result = TaskSchema.safeParse(parsed);
if (!result.success) {
// registrar, emitir métricas y reintentar con autocorrección
const zodErrors = result.error.flatten();
// reintentar: enviar a la LLM el error y pedir JSON corregido
}
Patrón de autocorrección: incluye los errores de Zod en un nuevo prompt — el LLM suele corregir la estructura en el siguiente intento.
Structured Outputs no elimina Zod
Structured Outputs de OpenAI ayuda a reducir errores de formato (Structured Outputs). Aun así:
- No controla cortes por token o fallos de red.
- No valida la semántica (p. ej., números positivos).
- No sustituye la necesidad de validar localmente antes de persistir o ejecutar acciones.
Zod sigue siendo la última línea de defensa.
Operativa en producción: logging, reintentos y SLOs
- Registra errores de validación con contexto (prompt, response, zod.flatten()).
- Implementa reintentos limitados con backoff y un máximo de autocorrecciones.
- Mide métricas: tasa de validación fallida por modelo y prompt, latencia de corrección.
- Decide SLOs: p. ej., si tras 2 reintentos sigue fallando, encolar para revisión humana.
Esto transforma un fallo LLM en un incidente manejable, no en una caída silenciosa.
Consejos prácticos y anti‑patrones
- No uses
aspara confiar en la salida del modelo. Siempre valida. - Mantén los esquemas Zod como fuente única; evita duplicar interfaces manualmente.
- Evita validaciones laxas (p. ej.,
z.any()) en bordes críticos. - Serializa el esquema de forma legible en el prompt, no como dump técnico que el modelo no entenderá.
- Usa discriminated unions para estados exclusivos; obligan a la LLM a responder con casos válidos.
Conclusión técnica
Tipar las respuestas de una LLM con Zod + TypeScript no es una cuestión de estilo: es ingeniería de fiabilidad. .parse() y z.infer<> convierten la salida no determinista de una IA en datos verificables. Si construyes agentes, pipelines en n8n o features que actúan sobre sistemas críticos, aplicar este patrón es la diferencia entre sistemas que escalan y sistemas que requieren vigilancia humana constante. Implementa el esquema primero, valida siempre y deja que la LLM vuelva a intentarlo cuando falle — pero que falle donde tú lo controles.
Para equipos que implementan flujos autónomos y pipelines de IA, una práctica útil es centralizar patrones de validación y autocorrección; esto es exactamente el tipo de trabajo que se experimenta en Dominicode Labs, donde se documentan plantillas y prácticas para agentes y workflows.
FAQ
¿Qué es Zod y por qué usarlo?
Zod es una librería de validación y parsing para JavaScript/TypeScript que permite definir esquemas y validar datos en runtime. Se usa para asegurar que los datos provenientes de fuentes no confiables (como LLMs) cumplen un contrato antes de ser consumidos por la aplicación.
¿Por qué no usar JSON.parse() + as?
Porque as es solo una aserción en TypeScript y no realiza validación en runtime. JSON.parse() + as asume que la estructura es correcta; si no lo es, introducirás datos inválidos en tu sistema.
¿Cuándo usar .parse() vs .safeParse()?
Usa .parse() cuando quieras que una anomalía detenga el flujo y sea tratada como error severo (por ejemplo, en endpoints HTTP que deben devolver 4xx/5xx). Usa .safeParse() cuando prefieras manejar el fallo (registrar, reintentar con autocorrección) sin lanzar una excepción.
¿Cómo incluir el esquema en el prompt?
Serializa una versión legible del esquema en el prompt (por ejemplo, un objeto JSON con tipos esperados o una tabla de campos requeridos/formatos). Evita dumps técnicos largos; prioriza ejemplos y restricciones clave que la LLM entienda fácilmente.
¿Los Structured Outputs de OpenAI reemplazan a Zod?
No. Structured Outputs reduce errores de formato, pero no controla cortes por token, fallos de red ni valida semántica (p. ej., números positivos). Zod sigue siendo necesario para validación en runtime.
¿Qué debo registrar cuando falla la validación?
Registra el prompt, la respuesta cruda, el resultado de result.error.flatten() o el stack de la excepción, e información contextual (modelo, versión del prompt, intento actual). Estos datos facilitan autocorrección y análisis de causa.

Leave a Reply