Madness Driven Design: Don Quijote, Sancho Panza y tu copiloto IA

| Última modificación: 4 de mayo de 2026 | Tiempo de Lectura: 8 minutos
Premios Blog KeepCoding 2025

Co-Fundador de KeepCoding

TL;DR: Un LLM es Don Quijote — no puedes curarlo, es estocástico por naturaleza. La solución no es corregir al loco sino poner un Sancho Panza determinista al lado. MDD tiene dos capas: primero estudias los errores que comete para diseñar herramientas que los absorban, y luego lo sueltas con esas herramientas para verificar que no has dejado huecos. Diseña para la locura, no contra ella.


Llevaba semanas auditando logs. 165 sesiones de un agente IA interactuando con una CLI para gestionar tareas. Más de 500 errores. 370 reintentos. Patrones que se repetían una y otra vez: el agente usaba --status cuando el flag se llamaba --state. Escribía Todo cuando la API esperaba unstarted. Pasaba urgent como prioridad cuando el sistema solo aceptaba números.

Y lo fascinante era que cada error tenía sentido. No eran errores aleatorios. Eran errores plausibles. Exactamente los errores que cometerías tú si conocieras el dominio «más o menos» pero nunca hubieras leído la documentación con atención.

En algún momento de esa auditoría, mirando el enésimo --status Done que debería haber sido --state completed, me di cuenta de que estaba viendo un patrón literario. Uno que tiene 400 años.

Don Quijote es un LLM

Piénsalo un segundo. Don Quijote ve molinos de viento y dice «son gigantes». No es estúpido — es un tío culto, ha leído mucho, conoce las historias de caballería al dedillo. El problema es que su modelo del mundo está contaminado por datos de entrenamiento ficticios. Ha leído tantas novelas de caballería que cuando ve algo ambiguo, lo interpreta según su training data. Molinos → gigantes. Rebaños → ejércitos. Ventas → castillos.

Un LLM hace exactamente lo mismo. Ha visto miles de APIs en su entrenamiento. Cuando le pides que use una que no conoce bien, no dice «no sé». Adivina. Y adivina bien. Casi siempre. Lo suficiente para que te fíes. Y cuando falla, el fallo es plausible.

--status en vez de --state. Porque en el 60% de las CLIs que ha visto, el flag se llama --status.

Todo en vez de unstarted. Porque en la interfaz gráfica de la herramienta, la columna dice «Todo». El LLM ha visto capturas de pantalla en documentación. Ha leído blogs. Ha inferido que si la UI dice «Todo», el API acepta «Todo». Suena lógico. Es falso.

urgent en vez de 1. Porque en la mayoría de sistemas de prioridades, urgent es un valor válido. ¿Quién diseña una API donde la prioridad es un número del 1 al 4 sin etiquetas?

Cada alucinación es una inferencia razonable sobre datos incompletos. Don Quijote no es tonto. Está loco. Y no puedes curar a un loco.

Lo que Cervantes ya sabía

Cervantes no intenta curar a Don Quijote. Lo que hace es poner a Sancho Panza a su lado.

Sancho no es brillante. No ha leído los libros. No tiene visiones grandiosas. Pero es determinista. Cuando Don Quijote dice «mira esos gigantes», Sancho dice «señor, que son molinos». No siempre le hace caso, pero la información está ahí. El sistema tiene dos capas: una estocástica que genera hipótesis (Don Quijote) y una determinista que las contrasta con la realidad (Sancho).

Esa es la arquitectura que necesitas cuando trabajas con un LLM. No vas a conseguir que deje de alucinar. Es su naturaleza. Lo que sí puedes hacer es poner capas deterministas que atrapen las alucinaciones antes de que causen daño.

Y aquí es donde entra la metodología.

MDD: Madness Driven Design

MDD tiene dos capas, y el orden importa.

Capa 1: Arqueología a priori

Antes de escribir una línea de código, estudias la locura. No la imaginas — la observas. Recopilas datos reales de cómo el LLM interactúa con herramientas existentes y catalogas los errores.

En mi caso, analicé 165 sesiones de un agente IA usando una CLI para gestionar tareas de un equipo de desarrollo. Los números:

Categoría de error Ocurrencias Reintentos generados
Flags inventados o prohibidos 275 ~150
Escapado JSON/GraphQL roto 25 80+
Confusión de nomenclatura 40+ 50+
Operaciones imposibles por la CLI 60+ 90+
Output verbose desperdiciando tokens N/A N/A

Con esos datos, diseñas la herramienta nueva para absorber los errores en vez de rechazarlos. En román paladino: el cuerdo se adapta al loco, no al revés.

Ejemplos concretos de absorción:

Error del LLM              → Diseño de la herramienta
─────────────────────────────────────────────────────
--status Done              → --status es alias de --state
                             "Done" se normaliza a "completed"

--priority urgent          → "urgent" se normaliza a 1
                             "high" → 2, "medium" → 3, "low" → 4

--no-pager                 → Flag ignorado silenciosamente
                             (la herramienta nunca usa pager)

Escapado roto de comillas  → Input por fichero o stdin
en descripciones             Nunca inline. Serde se encarga.

Cada fila de esa tabla es una decisión de diseño que viene de un error real observado. No de una especulación sobre «qué podría fallar», sino de un log que dice «esto falló 40 veces en 165 sesiones».

La diferencia con el diseño convencional es sutil pero importante. En diseño normal, defines la interfaz correcta y rechazas lo que no encaja. En MDD, defines la interfaz correcta y todas las interfaces incorrectas que tu usuario va a intentar, y las absorbes.

Es como diseñar una puerta que se abre tanto empujando como tirando. La puerta «correcta» solo se abre en una dirección. La puerta buena se abre en las dos, porque has observado que el 40% de la gente empuja cuando debería tirar.

Capa 2: Verificación a posteriori

Construyes la herramienta con las defensas de la Capa 1 y luego la sueltas. Le das la herramienta nueva al LLM y observas qué errores nuevos comete.

Si la Capa 1 fue exhaustiva, los errores nuevos deberían ser mínimos. Si aparecen errores nuevos, has encontrado huecos en tu diseño. Cada error nuevo es un test de penetración involuntario.

Cuando hice esto con mi CLI, el LLM inventó cosas que no había visto en la auditoría original:

  • Un enum de ordenación que no existía. La API permite ordenar por createdAt y updatedAt. El LLM inventó un valor priority para el enum de ordenación. Suena perfectamente lógico — ¿por qué no podrías ordenar por prioridad? Pero ese valor no existe en el schema GraphQL.

  • Un operador de filtrado que no existía. Para filtrar por estado, la API acepta state.type.in. El LLM generó state.id.or. Sintaxis coherente, patrón razonable, completamente inventado.

  • Una función de file locking de otro lenguaje. En un proyecto Rust, el LLM sugirió fcntl.flock para bloquear ficheros. Eso es Python. En Rust usas el crate fs2.

Cada uno de esos errores fue plausible. Ninguno fue estúpido. Y cada uno reveló un hueco: la herramienta no validaba los valores del enum de ordenación, no rechazaba operadores de filtrado inventados, y la documentación del crate de file locking no estaba en el contexto del agente.

La Capa 2 cierra el bucle. No confías en que tu diseño sea correcto — lo verificas soltando al agente más creativo (y más propenso a alucinar) que tienes.

El Sancho Panza Stack

La metáfora de Don Quijote y Sancho Panza no es solo una analogía bonita. Es una arquitectura. En la práctica, el «Sancho Panza» no es una sola pieza — es un stack de capas deterministas, cada una atrapando una categoría de locura diferente:

┌──────────────────────────────────────┐
│         LLM (Don Quijote)            │  Genera comandos plausibles
│         Estocástico, creativo        │  pero potencialmente falsos
└──────────────┬───────────────────────┘
               │ "--status Done --priority urgent"
┌──────────────▼───────────────────────┐
│  1. CLI Parser (clap)                │  Rechaza flags que no existen
│     Acepta aliases: --status→--state │  ni siquiera como alias
└──────────────┬───────────────────────┘
               │ "--state Done --priority urgent"
┌──────────────▼───────────────────────┐
│  2. Normalización                    │  "Done"→"completed"
│     state-aliases, priority-aliases  │  "urgent"→1
└──────────────┬───────────────────────┘
               │ "--state completed --priority 1"
┌──────────────▼───────────────────────┐
│  3. Validación                       │  ¿"completed" es un estado
│     Contra enums conocidos           │  válido? ¿1 está en rango?
└──────────────┬───────────────────────┘
               │ state=completed, priority=1
┌──────────────▼───────────────────────┐
│  4. Serialización (serde)            │  Escapado correcto by
│     Variables GraphQL, no strings    │  construction. Ni comillas
│     interpoladas                     │  rotas ni inyección posible
└──────────────┬───────────────────────┘
               │ {"state":"completed","priority":1}
┌──────────────▼───────────────────────┐
│  5. API + error handling             │  Si la API rechaza algo,
│     Retry con backoff, mensajes      │  el error es legible y
│     útiles                           │  accionable
└──────────────────────────────────────┘

Cinco capas. Cada una determinista. Cada una atrapa una categoría de error que el LLM va a cometer. El LLM no necesita ser correcto — solo necesita ser aproximadamente correcto, y el stack se encarga del resto.

Es como un embudo de purificación. Arriba entra agua sucia (input estocástico del LLM) y abajo sale agua potable (query GraphQL válida). Cada capa filtra un tipo de impureza. Ninguna capa sola es suficiente. Todas juntas lo son.

MDD vs. fuzz testing: la diferencia que importa

Si conoces el fuzz testing, estarás pensando «esto es lo mismo». No lo es.

Fuzz testing MDD
Input Aleatorio, malformado Plausible, coherente, bien escrito
Objetivo Encontrar crashes, segfault Encontrar errores semánticos
El input parece válido No Sí — ese es el problema
Ejemplo \x00\xff\xfe como nombre --priority urgent como flag

Un fuzzer genera basura y ve si tu programa crashea. MDD genera input que parece correcto pero es factualmente falso. --priority urgent no es basura — es exactamente lo que escribiría un humano que conoce el dominio pero no la API. Un fuzzer nunca generaría eso porque es demasiado coherente.

Lo mismo aplica al mutation testing y al chaos engineering. Mutan tu código o rompen tu infraestructura para ver si los tests lo detectan. MDD no rompe nada — genera input correcto según otro modelo del mundo. Es la diferencia entre un ataque de fuerza bruta y un ataque de ingeniería social. Uno intenta todas las combinaciones; el otro te convence de que le abras la puerta.

La idea accionable

No necesitas construir una CLI en Rust para aplicar MDD. El patrón funciona con cualquier herramienta que un LLM vaya a usar:

Paso 1: Observa la locura. Antes de diseñar (o rediseñar) una herramienta, pon al LLM a usar la versión actual y registra cada error. No 5 sesiones — 50. Los patrones aparecen con volumen.

Paso 2: Clasifica los errores. ¿Son de nomenclatura? ¿De formato? ¿De semántica? Cada categoría implica una defensa diferente.

Paso 3: Diseña para absorber. No rechaces --status con un error críptico. Acepta --status como alias de --state. No rechaces urgent como prioridad. Normalízalo a 1. El usuario que más vas a tener es un agente que conoce el dominio al 80%. Diseña para ese 80%.

Paso 4: Suelta y verifica. Dale la herramienta nueva al LLM sin instrucciones especiales. Cada error nuevo es un hueco en tu Capa 1. Parchea y repite.

Si tu herramienta la van a usar humanos y LLMs, las defensas de MDD mejoran la experiencia para ambos. Porque los humanos cometen los mismos errores que los LLMs — solo que menos veces y con más vergüenza.

El que diseña el Sancho es el arquitecto

Hay una confusión frecuente que quiero despejar. El LLM no diseña el Sancho Panza Stack. El LLM es Don Quijote. Tú eres Cervantes.

Tú eres quien observa los patrones de locura. Tú eres quien decide qué normalizar y qué rechazar. Tú eres quien construye las capas deterministas. El LLM puede ayudarte a implementarlas — es buen picando código — pero las decisiones de diseño son tuyas.

Es la diferencia entre «le pedí a mi IA que arreglara sus propios errores» (no funciona — volverá a cometerlos) y «observé los errores de mi IA y construí un sistema que los absorbe» (funciona — el sistema es determinista).

Ni en pedo confíes en que el LLM se auto-corrija. Su naturaleza estocástica garantiza que repetirá los mismos errores con variaciones creativas. Lo que necesitas no es un LLM mejor — es un Sancho mejor.

Lo que realmente importa

MDD no es una metodología de testing. Es una metodología de diseño de herramientas. La pregunta no es «¿cómo detecto que el LLM se equivoca?» sino «¿cómo diseño para que equivocarse no tenga consecuencias?».

Es la misma filosofía que los guardrails en carreteras de montaña. No evitas que la gente gire mal — pones una barrera para que girar mal no te mate. No corriges al conductor — proteges el camino.

Cervantes lo entendió hace cuatro siglos. No intentó curar a Don Quijote. Le puso un Sancho al lado y dejó que la historia funcionara.

Tu CLI, tu API, tu SDK — lo que sea que un LLM vaya a tocar — necesita su propio Sancho. Determinista, testarudo, incapaz de alucinar. No brillante. No creativo. Solo correcto.

Diseña para la locura. El cuerdo se adapta al loco.


Read this article in English.

Noticias recientes del mundo tech


¡CONVOCATORIA ABIERTA!

Desarrollo de apps móviles ios & Android

Full Stack Bootcamp

Clases en Directo | Profesores en Activo | Temario 100% actualizado

Descárgate también el informe de tendencias en el mercado laboral 2026.

Fórmate con planes adaptados a tus objetivos y logra resultados en tiempo récord.
KeepCoding Bootcamps
Resumen de privacidad

Esta web utiliza cookies para que podamos ofrecerte la mejor experiencia de usuario posible. La información de las cookies se almacena en tu navegador y realiza funciones tales como reconocerte cuando vuelves a nuestra web o ayudar a nuestro equipo a comprender qué secciones de la web encuentras más interesantes y útiles.