Módulo 5: Modos de fallo y resiliencia para IA

Los nuevos modos de fallo de un componente de IA

Descripción

Cuando integras un componente nuevo en un sistema, la primera pregunta de diseño no es "¿qué hace cuando funciona?" sino "¿cómo falla?". Porque de eso depende todo lo demás: cómo lo pruebas, cómo lo monitoreas, qué defensas le pones alrededor. Y aquí está el giro que hace de un LLM un componente distinto de casi cualquier otro que hayas conectado: falla de formas que un componente clásico no tiene, y la peor de esas formas no lanza ningún error. Una función determinista tiene un catálogo de fallos corto y conocido: o te da el resultado correcto, o lanza una excepción que atrapas (ValueError, KeyError, TimeoutError). Cuando falla, lo sabes: se enciende una alarma que puedes capturar y manejar. Un LLM hereda todos esos fallos —puede estar caído, lento, rate-limited, y esos lanzan una excepción— pero agrega uno nuevo y peligroso: la alucinación, una salida que parece perfectamente válida, no lanza ninguna excepción, y está mal. Esta lección instala la taxonomía: los fallos ruidosos (que una excepción atrapa) y el fallo silencioso (que solo una validación ve).

En la lección 1 viste el módulo en miniatura: un modelo caído y un sistema que no se cae. Aquí bajamos a la pregunta que lo antecede: ¿de cuántas formas falla el componente de IA, y en qué se distinguen de las de un componente normal? Vas a ver, ejecutado, el contraste directo entre un componente clásico —un cálculo de impuesto, determinista— y un componente de IA —un clasificador de tickets de soporte—, contando exactamente qué tipos de fallo produce cada uno. El clásico tendrá cero fallos silenciosos; el de IA tendrá una familia entera de ellos.

Conexión con el módulo. La lección 1 mostró la cáscara de resiliencia funcionando; esta lección instala el mapa de amenazas que esa cáscara defiende —qué modos de fallo existen y cuál es cuál—. Es la base de todo el módulo: las lecciones 3 a 7 toman cada modo de fallo de este mapa y lo desarrollan con su defensa. La 3 toma la alucinación (el fallo silencioso); la 4 los fallos de disponibilidad (caído/lento/rate-limited); la 5 y la 6 las mitigaciones (fallback, breaker); la 7 el drift (un fallo silencioso en el tiempo). La frontera con la guía de resiliencia se mantiene: aquí catalogamos los fallos propios de la IA; la mecánica de las defensas está allá.

Una analogía: el cajero automático y el adivino elocuente

Imagina dos formas de pedir una cifra. La primera es un cajero automático. Le pides tu saldo y pasa una de dos cosas: o te muestra el número correcto, o te da un mensaje de error claro —"servicio no disponible", "tarjeta no reconocida", "intente más tarde"—. Nunca te muestra un saldo inventado. El cajero, cuando no puede darte el dato bueno, te lo dice; su fallo es ruidoso, honesto, inconfundible. Sabes exactamente cuándo confiar en él: cuando no dio error.

La segunda forma es preguntarle a un adivino elocuente. Le pides tu saldo y siempre te da un número, dicho con una seguridad absoluta y una voz que inspira confianza. La mayoría de las veces acierta —es bueno—, pero de vez en cuando el número es inventado, y lo dice con exactamente la misma seguridad que cuando acierta. No hay ninguna diferencia en su tono, en su gramática, en su aplomo, entre la respuesta correcta y la inventada. El adivino nunca dice "no sé"; siempre dice algo, y ese algo suena siempre igual de convincente. Su fallo es silencioso: para saber si acertó, tendrías que verificar el número por otro lado.

Aquí está el punto: un componente clásico es el cajero; un LLM es el adivino elocuente. El cajero falla ruidoso —te avisa cuándo no puede—; el LLM puede fallar silencioso —te da una respuesta inventada con la misma cara que una correcta—. Y esto cambia radicalmente cómo debes diseñar alrededor de cada uno. Del cajero te fías cuando no dio error; el error es la señal. Del adivino no te puedes fiar por su tono, porque el tono es idéntico acierte o no; la única defensa es verificar. Un sistema que trata al adivino como si fuera un cajero —que confía en su respuesta porque "no dio error"— va a servir números inventados creyendo que son saldos reales. En Mercado, el clasificador de tickets, el agente de soporte, el generador de descripciones: todos son el adivino elocuente, y el módulo entero es aprender a construir el sistema de verificación que el cajero no necesitaba.

Ejemplo trabajado: el componente clásico vs el de IA

Vamos a contar los fallos de cada tipo de componente. El componente clásico es tax_component: calcula un impuesto. Es determinista —mismo input, mismo output— y falla fuerte: con una entrada inválida (un subtotal negativo) lanza ValueError. El componente de IA es ai_component: clasifica el tema de un ticket de soporte en una de un conjunto cerrado de categorías (billing, shipping, returns, refund, account). El stub simula los modos de fallo propios de un LLM: a veces responde bien (ok), a veces está caído/lento/rate-limited (lanza una excepción), y a veces alucina —devuelve una categoría que no existe (teleport, refund9999, premium_tier), con la misma naturalidad que una válida, sin lanzar nada—.

Fíjate en la asimetría del stub de IA: los modos down, slow y rate_limited lanzan excepción; los modos ok y hallucination devuelven un string —y desde fuera, sin verificar, no se distinguen—. Esa es toda la lección.

# Leccion 2: los modos de fallo NUEVOS de un componente de IA.
# Un componente clasico falla FUERTE (excepcion que atrapas) o no falla.
# Un LLM puede fallar de formas nuevas, incluida la SILENCIOSA (alucinacion):
# una respuesta que parece correcta y esta mal, sin lanzar ningun error.
# LLM simulado por stub determinista; sin red.


# --- Componente CLASICO: calculo de impuesto. Determinista. Falla FUERTE. ---
def tax_component(subtotal):
    if not isinstance(subtotal, (int, float)) or subtotal < 0:
        raise ValueError("subtotal invalido")   # fallo fuerte, atrapable
    return round(subtotal * 0.16, 2)             # correcto si no lanza


# --- Componente de IA: clasifica el tema de un ticket de soporte. ---
class ModelDown(Exception): pass
class ModelSlow(Exception): pass
class ModelRateLimited(Exception): pass


# El stub simula los modos de fallo PROPIOS de un LLM. 'ok' y 'hallucination'
# devuelven texto: NINGUNO lanza excepcion. Ese es el punto.
AI_OUTCOMES = [
    ("ok",            "billing"),
    ("hallucination", "teleport"),     # SILENCIOSO: categoria inventada
    ("ok",            "shipping"),
    ("down",          None),           # el modelo esta caido
    ("ok",            "returns"),
    ("slow",          None),           # tardo demasiado (timeout)
    ("hallucination", "refund9999"),   # SILENCIOSO: valor inventado
    ("rate_limited",  None),           # cuota agotada
    ("ok",            "account"),
    ("ok",            "shipping"),
    ("hallucination", "premium_tier"), # SILENCIOSO: categoria que no existe
    ("ok",            "billing"),
]

VALID_CATEGORIES = {"billing", "shipping", "returns", "refund", "account"}


def ai_component(i):
    kind, value = AI_OUTCOMES[i % len(AI_OUTCOMES)]
    if kind == "down":         raise ModelDown()
    if kind == "slow":         raise ModelSlow()
    if kind == "rate_limited": raise ModelRateLimited()
    return value


# --- Componente clasico: 12 llamadas, una con entrada invalida ---
CLASSIC_INPUTS = [100, 250, -5, 0, 1000, 42, 7.5, 300, 15, 999, 1, 50]
classic_ok = classic_err = 0
for x in CLASSIC_INPUTS:
    try:
        tax_component(x)
        classic_ok += 1
    except ValueError:
        classic_err += 1   # fallo LOUD: excepcion atrapada

print("=== Componente CLASICO (calculo de impuesto) ===")
print(f"  exitos                 : {classic_ok}")
print(f"  errores (excepcion)    : {classic_err}")
print(f"  fallos SILENCIOSOS     : 0   <- un componente determinista no los tiene")
print()

# --- Componente de IA: 12 llamadas ---
ai_ok = ai_down = ai_slow = ai_rate = ai_silent_wrong = 0
print("=== Componente de IA (clasificador de tickets) ===")
print(f"  {'req':<5}{'modo':<15}{'detectado por':<22}resultado")
print("  " + "-" * 60)
for i in range(12):
    try:
        out = ai_component(i)
        # No lanzo excepcion: puede ser 'ok' o una ALUCINACION.
        if out in VALID_CATEGORIES:
            ai_ok += 1
            print(f"  {i:<5}{'ok':<15}{'-':<22}{out}")
        else:
            ai_silent_wrong += 1   # texto que parece valido pero es inventado
            print(f"  {i:<5}{'hallucination':<15}{'validacion de schema':<22}{out} (INVENTADA)")
    except ModelDown:
        ai_down += 1
        print(f"  {i:<5}{'down':<15}{'excepcion':<22}fallback")
    except ModelSlow:
        ai_slow += 1
        print(f"  {i:<5}{'slow':<15}{'excepcion (timeout)':<22}fallback")
    except ModelRateLimited:
        ai_rate += 1
        print(f"  {i:<5}{'rate_limited':<15}{'excepcion':<22}fallback")

print("  " + "-" * 60)
print(f"  ok={ai_ok}  down={ai_down}  slow={ai_slow}  rate_limited={ai_rate}  "
      f"alucinaciones={ai_silent_wrong}")
print(f"  fallos LOUD  (excepcion)          : {ai_down + ai_slow + ai_rate}")
print(f"  fallos SILENCIOSOS (alucinacion)  : {ai_silent_wrong}  "
      f"<- NO lanzan excepcion; solo una validacion los ve")

Qué esperar. Al correr el archivo, la salida es exactamente esta:

=== Componente CLASICO (calculo de impuesto) ===
  exitos                 : 11
  errores (excepcion)    : 1
  fallos SILENCIOSOS     : 0   <- un componente determinista no los tiene

=== Componente de IA (clasificador de tickets) ===
  req  modo           detectado por         resultado
  ------------------------------------------------------------
  0    ok             -                     billing
  1    hallucination  validacion de schema  teleport (INVENTADA)
  2    ok             -                     shipping
  3    down           excepcion             fallback
  4    ok             -                     returns
  5    slow           excepcion (timeout)   fallback
  6    hallucination  validacion de schema  refund9999 (INVENTADA)
  7    rate_limited   excepcion             fallback
  8    ok             -                     account
  9    ok             -                     shipping
  10   hallucination  validacion de schema  premium_tier (INVENTADA)
  11   ok             -                     billing
  ------------------------------------------------------------
  ok=6  down=1  slow=1  rate_limited=1  alucinaciones=3
  fallos LOUD  (excepcion)          : 3
  fallos SILENCIOSOS (alucinacion)  : 3  <- NO lanzan excepcion; solo una validacion los ve

Lee las dos secciones en contraste, porque ahí está toda la lección.

El componente clásico tiene un catálogo de resultados de dos entradas: 11 éxitos y 1 error (la entrada -5, que lanzó ValueError). Y una línea que es el corazón del contraste: fallos silenciosos: 0. Un componente determinista no tiene esa categoría. Cuando tax_component no puede darte un resultado válido, lo sabe y lo dice —lanza una excepción—. Nunca te devuelve un impuesto inventado que parezca correcto. Su fallo es siempre ruidoso, y por eso confiar en él es fácil: si no lanzó, acertó.

El componente de IA tiene un catálogo mucho más rico. De 12 llamadas: 6 correctas, 3 fallos ruidosos (1 caído, 1 lento, 1 rate-limited —cada uno lanzó su excepción, y el sistema pudo caer al fallback—), y —aquí está lo nuevo— 3 alucinaciones. Mira los requests 1, 6 y 10: el modelo devolvió teleport, refund9999 y premium_tier. Ninguno lanzó excepción. Los tres son strings, como los seis buenos. Desde fuera, un try/except los deja pasar todos por igual —el except nunca se activa porque no hubo excepción—. La única razón por la que este código los detectó es la línea if out in VALID_CATEGORIES: una validación de schema que compara la salida contra el conjunto cerrado de categorías reales. Sin esa validación, teleport se habría usado como si fuera una categoría de verdad, con las consecuencias que eso arrastre aguas abajo (un ticket enrutado a un equipo que no existe, un dato basura en un reporte).

Fíjate en la implicación arquitectónica: el try/except protege contra los fallos ruidosos, y no hace absolutamente nada contra los silenciosos. Los tres fallos ruidosos cayeron al fallback limpiamente. Las tres alucinaciones habrían pasado como buenas si no hubiera una validación explícita del contenido de la salida. Son dos frentes de defensa distintos: uno atrapa excepciones (para el modelo caído/lento/rate-limited), otro valida contenido (para la alucinación). El módulo cubre los dos, y esta lección te muestra por qué necesitas ambos: ninguno cubre lo del otro.

Profundización: el mapa de los modos de fallo de la IA

Vale la pena poner el mapa completo, porque es la brújula del módulo. Los modos de fallo de un componente de IA se agrupan en tres familias, y cada lección toma una:

                    MODOS DE FALLO DE UN COMPONENTE DE IA
                                    │
        ┌───────────────────────────┼───────────────────────────┐
        ▼                           ▼                           ▼
  DISPONIBILIDAD               CONTENIDO                   TEMPORAL
  (ruidoso)                    (silencioso)                (silencioso, lento)
        │                           │                           │
  ┌─────┼─────┐                     │                           │
  ▼     ▼     ▼                     ▼                           ▼
caido lento rate-              alucinacion                    drift
        (timeout) limited      (inventa con                (degradacion
                                confianza)                  en el tiempo)
        │                           │                           │
  lanza excepcion            NO lanza nada;               NO lanza nada;
  -> timeout/breaker/        parece valida                aparece de a poco
     fallback (L4,L5,L6)      -> verificar (L3)            -> monitorear (L7)

Familia 1: fallos de disponibilidad (ruidosos). El modelo caído (la API no responde, HTTP 5xx), lento (tarda más que tu presupuesto, y sin un timeout te deja esperando), y rate-limited (agotaste la cuota, HTTP 429). Los tres son ruidosos: la API te devuelve un error o el timeout lo convierte en uno. Se defienden con las piezas clásicas de resiliencia —timeout (L4), fallback (L5), circuit breaker (L6)— aplicadas al modelo. Son los "fáciles" en el sentido de que al menos sabes cuándo pasan.

Familia 2: fallo de contenido (silencioso). La alucinación: el modelo responde —sin error— con un dato inventado que suena bien. Es el fallo propio de la IA, el que no existe en ningún componente clásico. No lo atrapa ningún try/except; solo lo ve una verificación del contenido contra una fuente de verdad (L3). Es el más peligroso porque es invisible: el sistema cree que todo va bien.

Familia 3: fallo temporal (silencioso, lento). El drift: el modelo o los datos cambian con el tiempo y la calidad se degrada poco a poco, sin ninguna caída ni excepción. Hoy el sistema acierta el 94%, en un mes el 79%, y nadie lo notó porque nunca hubo un momento de "se rompió". Solo lo ve un monitoreo que mide la calidad en el tiempo (L7). Es una alucinación distribuida en semanas.

La distinción clave: ruidoso vs silencioso. Un fallo ruidoso se anuncia —lanza una excepción, devuelve un código de error, agota un timeout—; tu código lo puede capturar en el momento y reaccionar. Un fallo silencioso no se anuncia —entrega una salida que pasa por buena—; tu código no lo capturará nunca a menos que verifiques activamente que la salida es correcta. Esta es la razón profunda por la que la salida del LLM se trata como no confiable (la tesis del módulo 4): no porque el modelo sea malo, sino porque no puedes distinguir su acierto de su fallo por la forma de la respuesta. El adivino suena igual acierte o no.

Por qué el silencioso es el caro. Un fallo ruidoso, por definición, te da la oportunidad de manejarlo: caes al fallback, escalas, reintentas. El daño es acotado —un request más lento, una respuesta degradada—. Un fallo silencioso llega hasta el usuario sin fricción, porque nada lo detuvo. Un impuesto mal calculado que lanzara una excepción se atraparía; un impuesto mal calculado que se devolviera como correcto llegaría a la factura del cliente. El componente clásico nunca hace lo segundo; el de IA sí. Por eso el módulo dedica una lección entera (la 3) a la alucinación y otra (la 7) al drift: son los dos fallos silenciosos, y contra el silencio la única defensa es mirar activamente.

Los fallos ruidosos también son nuevos en su origen, aunque no en su forma. Ojo con una simplificación: aunque un modelo caído se parece a cualquier otra dependencia caída, su origen tiene matices propios de la IA. El rate limit es más central que en un servicio clásico —el proveedor de modelos impone cuotas estrictas por tokens y por requests—; la lentitud es el caso normal, no la excepción (un LLM tarda cientos de ms a segundos, como viste en el módulo 2), así que el timeout no es un seguro contra rarezas sino una restricción de primera clase; y el costo por llamada convierte cada reintento en dinero. Por eso, aunque la forma de defenderse (timeout, breaker, fallback) sea la misma que en la guía de resiliencia, la motivación aquí tiene un peso extra. Las lecciones 4 y 6 lo desarrollan.

Errores comunes

Tratar al LLM como un cajero (confiar en que "si no dio error, acertó"). Qué pasa: el equipo envuelve la llamada al modelo en un try/except cuidadoso, maneja bien el modelo caído y el rate limit, y da por hecho que con eso está cubierto. Pero nunca valida el contenido de las respuestas que no lanzaron excepción, así que todas las alucinaciones pasan como buenas. Por qué pasa: se transfiere al LLM el modelo mental de un componente clásico, donde "no lanzó = acertó" es cierto. En el LLM es falso. Cómo detectarlo: tu manejo de fallos del modelo consiste solo en except, sin ninguna verificación del contenido de la salida exitosa. Cómo corregirlo: agrega el segundo frente —una validación del contenido contra un schema o una fuente de verdad— porque el try/except no ve las alucinaciones. La lección 3 lo ejecuta.

Contar solo los fallos ruidosos en las métricas. Qué pasa: el dashboard muestra "99.5% de las llamadas al modelo exitosas" y el equipo duerme tranquilo —porque cuenta como "exitosa" toda llamada que no lanzó excepción, incluyendo las que alucinaron—. La tasa de disponibilidad es excelente y la tasa de corrección es un desastre, pero solo se mide la primera. Por qué pasa: la disponibilidad es fácil de medir (¿lanzó o no?), la corrección es difícil (hay que verificar la salida). Cómo detectarlo: tus métricas de salud del modelo no incluyen ninguna medida de calidad de la salida, solo de disponibilidad. Cómo corregirlo: mide las dos —disponibilidad (fallos ruidosos) y calidad (fallos silenciosos, con el eval del módulo 3)—, porque un modelo puede estar 100% disponible y aun así servir basura. La lección 7 lo lleva al monitoreo continuo.

Confundir "raro" con "no pasa". Qué pasa: el equipo ve que las alucinaciones son el 5-10% y decide que es una tasa lo bastante baja como para ignorarla —"el modelo casi siempre acierta"—. A escala, ese 5-10% son miles de respuestas inventadas servidas al mes, cada una indistinguible de las buenas. Por qué pasa: se piensa la tasa como magnitud del problema, cuando el problema es que no sabes cuáles son las malas sin verificar. Cómo detectarlo: tu justificación para no verificar es la baja frecuencia del fallo. Cómo corregirlo: la frecuencia decide cuánto degradas o reintentas, no si verificas; la verificación va siempre, porque una sola alucinación servida en un dato crítico (un estado de pedido, un monto) puede costar caro. Es la misma lógica del "casi siempre acierta es la trampa" del módulo 4.

Ejercicios

Ejercicio 1 — Clasifica el modo de fallo. Para cada situación, di a qué familia pertenece (disponibilidad / contenido / temporal), si es ruidoso o silencioso, y cuál es la defensa adecuada: (a) la API del modelo tarda 25 segundos y el request se queda colgado; (b) el modelo recomienda un producto que fue retirado del catálogo hace un mes; (c) el score del eval de la búsqueda semántica bajó de 0.91 a 0.82 en seis semanas; (d) la API devuelve HTTP 429.

Ver solución
  • (a) Tarda 25 s → DISPONIBILIDAD, ruidoso (con timeout). El cuelgue por sí solo no lanza nada, pero el timeout lo convierte en un fallo ruidoso atrapable. Defensa: timeout (corta a tu presupuesto) + fallback. Lección 4.
  • (b) Recomienda un producto retirado → CONTENIDO, silencioso. No es una excepción; el modelo devolvió un ID que parece válido pero apunta a algo que ya no debería mostrarse. Es una forma de alucinación/dato obsoleto. Defensa: verificar el ID contra la fuente de verdad (¿el producto existe y está activo?) antes de servirlo. Lección 3.
  • (c) El eval bajó 0.91 → 0.82 en seis semanas → TEMPORAL, silencioso. Drift: degradación lenta sin caída. Defensa: monitoreo continuo del eval con una alerta. Lección 7.
  • (d) HTTP 429 → DISPONIBILIDAD, ruidoso. Rate limit explícito. Defensa: circuit breaker (deja de llamar para no empeorarlo) + fallback. Lección 6.

Ejercicio 2 — El try/except que no basta. Un compañero muestra este código y dice que "ya maneja todos los fallos del modelo":

try:
    category = ai_component(ticket)
    route_to_team(category)   # enruta el ticket al equipo de esa categoria
except (ModelDown, ModelSlow, ModelRateLimited):
    route_to_team("general")  # fallback

Explica qué familia de fallos maneja bien y cuál deja pasar por completo, y da un ejemplo concreto de qué puede salir mal.

Ver solución

El código maneja bien la familia de disponibilidad (fallos ruidosos): si el modelo está caído, lento o rate-limited, lanza la excepción, el except la atrapa, y el ticket se enruta al equipo general como fallback. Eso está correcto.

Lo que deja pasar por completo es la familia de contenido (el fallo silencioso, la alucinación). Cuando ai_component devuelve teleport o premium_tier —una categoría inventada—, no lanza ninguna excepción, así que el except nunca se activa, y route_to_team("teleport") se ejecuta con una categoría que no existe. Ejemplo concreto de lo que sale mal: el ticket se enruta a un equipo "teleport" que no existe, así que el ticket se pierde —nadie lo recibe— o revienta el enrutador con un error mucho más adentro, lejos de la causa. El arreglo: agregar una validación del contenido antes de usar la categoría:

try:
    category = ai_component(ticket)
    if category not in VALID_CATEGORIES:   # atrapa la alucinacion
        category = "general"
    route_to_team(category)
except (ModelDown, ModelSlow, ModelRateLimited):
    route_to_team("general")

El try/except cubre el fallo ruidoso; el if ... not in VALID_CATEGORIES cubre el silencioso. Necesitas los dos.

Ejercicio 3 — El fallo que un componente clásico no puede tener. Explica, en tus propias palabras, por qué tax_component (el componente clásico del ejemplo) no puede tener un fallo silencioso equivalente a la alucinación, y qué propiedad del LLM hace que sí pueda tenerlo. Luego describe un caso en Mercado donde una alucinación sería especialmente cara.

Ver solución

tax_component no puede alucinar porque es determinista y cerrado: dado un subtotal válido, hay exactamente un resultado correcto (subtotal * 0.16), y el código lo calcula siempre. No hay margen para "inventar" un impuesto que parezca plausible pero esté mal; el cálculo es el cálculo. Cuando la entrada es inválida (un subtotal negativo), no devuelve un número dudoso: lanza una excepción. Su espacio de salidas es {resultado correcto, excepción}. No existe la tercera opción "resultado inventado que parece correcto".

El LLM sí puede tenerla porque es probabilístico y abierto: no computa la respuesta correcta a partir de una fórmula; genera una salida plausible token a token a partir de patrones. Cuando no "sabe" la respuesta, no lo señala —no hay un mecanismo interno que diga "esto no lo sé"—; simplemente genera la continuación más plausible, que puede ser un dato inventado dicho con total fluidez. Su espacio de salidas incluye la tercera opción: "salida que parece correcta y no lo es".

Un caso caro en Mercado: el agente de soporte que inventa un estado de pedido o una guía de rastreo. Si el agente le dice a un cliente "tu pedido fue entregado ayer" cuando en realidad está perdido, o le da un número de guía inventado, el cliente actúa sobre un dato falso —deja de esperar, no reclama a tiempo, o va a buscar un paquete que no llegó—. El costo no es solo la respuesta mala: es la cadena de decisiones que el cliente toma confiando en ella. Por eso el agente debe verificar todo dato factual contra la fuente de verdad antes de servirlo, que es exactamente la lección 3.

Resumen y siguiente paso

En esta lección instalaste el mapa de amenazas del módulo: los modos de fallo de un componente de IA se agrupan en tres familias —disponibilidad (caído/lento/rate-limited, ruidosos), contenido (la alucinación, silenciosa) y temporal (el drift, silencioso y lento)— y la distinción que las organiza es ruidoso vs silencioso. Lo viste con el cajero (que te avisa cuándo no puede) y el adivino elocuente (que inventa con la misma cara que acierta), y lo mediste: el componente clásico tuvo 11 éxitos, 1 error y cero fallos silenciosos; el de IA tuvo 6 aciertos, 3 fallos ruidosos que el try/except atrapó, y 3 alucinaciones que solo una validación de contenido pudo ver. La lección arquitectónica: necesitas dos frentes de defensa —capturar excepciones para lo ruidoso, verificar contenido para lo silencioso—, y ninguno cubre lo del otro.

Antes de avanzar deberías poder: nombrar las tres familias de fallos y dar un ejemplo de cada una; distinguir un fallo ruidoso de uno silencioso y explicar por qué el silencioso es el caro; argumentar por qué un try/except no defiende contra una alucinación; y explicar qué propiedad del LLM (probabilístico, abierto) hace posible el fallo silencioso que un componente determinista no tiene.

La lección 3 toma el fallo más propio y peligroso de todos —la alucinación— y lo desarrolla a fondo. Vas a ver, ejecutado, al agente de soporte de Mercado citando estados y guías de pedidos, y una verificación contra la fuente de verdad que separa lo real de lo inventado: los datos que coinciden con el estado real de los pedidos se sirven, y los que el modelo inventó se bloquean antes de llegar al cliente, degradando a un "no puedo confirmarlo, te comunico con un agente". La forma de convertir "el modelo a veces miente" en una compuerta que atrapa la mentira.

Recursos

  • Chip Huyen, AI Engineering (O'Reilly, 2024). Los capítulos sobre confiabilidad y evaluación tratan la alucinación y los fallos de contenido como una clase de fallo distinta de los de disponibilidad, y por qué la verificación de la salida es parte del diseño. La referencia central de esta lección para la taxonomía. En inglés.
  • Anthropic, documentación de Claude — docs.anthropic.com. Las páginas sobre rate limits y errores de la API describen los modos de fallo de disponibilidad de un modelo servido por API (429, 5xx), a nivel conceptual y sin fijar versión. En inglés.
  • resilience-and-reliability-patterns-guide (este ecosistema), M1 "Por qué fallan los sistemas distribuidos" — el encuadre general de la falla parcial y los modos de fallo de una dependencia, que aquí especializamos al componente de IA. En español.
  • Martin Fowler y Bharani Subramaniam, "Emerging Patterns in Building GenAI Apps" — martinfowler.com/articles/gen-ai-patterns. Ubica la validación de salida y el manejo de fallos alrededor de un componente de IA. En inglés.