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

Presentación del módulo: modos de fallo y resiliencia para IA

Por qué este módulo existe aquí

Hazte una pregunta incómoda sobre todo lo que construiste en los cuatro módulos anteriores: ¿qué pasa cuando el modelo no responde? En el módulo 1 ubicaste el LLM tras una frontera; en el 2 le pusiste presupuesto de latencia y costo; en el 3 le pusiste una compuerta de eval; en el 4 custodiaste su frontera de confianza. Todo ese trabajo, sin decirlo, asumía que el componente de IA está ahí y contesta. Pero un LLM vive detrás de una API externa que puede estar caída, saturada, lenta, o negándote el servicio porque agotaste tu cuota. Y aún cuando responde, puede contestar con un dato que inventó —con la misma gramática segura y el mismo tono profesional que usa cuando acierta—. Un componente clásico no hace nada de esto: una función suma un carrito y te da el total, o lanza una excepción que atrapas. El LLM abre una familia de fallos nueva, y algunos de esos fallos son invisibles: no lanzan un error, no encienden una alarma, simplemente entregan basura que parece oro.

Este módulo instala la tesis que gobierna el diseño de aquí en adelante: un sistema AI-native debe estar diseñado para que el fallo del componente de IA NO se convierta en la caída del sistema entero, y para que un fallo silencioso no pase por respuesta correcta. El componente de IA es la pieza más frágil del sistema —la más lenta, la que depende de un tercero, la que a veces miente—, y la arquitectura tiene que contener esa fragilidad, no confiar en que no aparezca. La forma de contenerla es un conjunto de piezas que quizás ya conoces de los sistemas distribuidos —fallback, circuit breaker, timeout, degradación— aplicadas específicamente al componente de IA y a sus fallos propios.

Aquí conviene marcar la frontera dura de este módulo antes de seguir, porque define qué vas a aprender y qué no. La mecánica de los patrones de resiliencia —cómo se implementa un circuit breaker de verdad con sus contadores y ventanas, la matemática del backoff con jitter, cómo se hace un timeout con hilos o con async— se enseña en la guía resilience-and-reliability-patterns-guide (M2 timeouts, M5 circuit breakers, M7 degradación). Este módulo no re-enseña esa mecánica: la aplica al componente de IA y remite a esa guía para el detalle. Lo que sí es propio de aquí, y no está en la guía de resiliencia, son los fallos específicos de la IA —la alucinación como modo de fallo, la dependencia de una API lenta con cuotas, el drift silencioso— y cómo esas piezas conocidas se acomodan alrededor de una pieza que además de fallar, a veces miente sin avisar.

El caso, como en toda la guía, es Mercado, con dos features en el centro:

  • La búsqueda semántica: el usuario escribe "audífonos para correr bajo la lluvia" y el LLM entiende la intención. Cuando el modelo está caído, no queremos que la caja de búsqueda de Mercado deje de funcionar; queremos que degrade a una búsqueda por keywords —peor, sin entender la intención, pero devuelve productos relevantes—. El usuario recibe resultados, no un error.
  • El agente de soporte: responde preguntas de clientes. Cuando no puede verificar un dato (¿este pedido existe?, ¿cuál es su estado real?) o cuando el modelo falla repetidamente, no queremos que invente una respuesta ni que se caiga; queremos que escale a un humano. El agente que no sabe, escala; no adivina.

Conexión con el módulo. Esta es la lección-mapa. No entramos a fondo en ninguna técnica todavía; instalamos la tesis (el fallo del modelo no debe tumbar el sistema; el fallo silencioso no debe pasar por correcto), el vocabulario (alucinación, caído/lento/rate-limited, drift, fallback, circuit breaker, timeout, degradación) y el mapa de cómo cada lección construye una parte. La lección 2 presenta la taxonomía de los fallos nuevos y por qué el silencioso es el peligroso. La 3 trata la alucinación y su contención por verificación. La 4 trata el modelo caído/lento/rate-limited y el timeout. La 5 es la mitigación central: fallback y degradación. La 6 pone el circuit breaker sobre el modelo. La 7 trata el drift y su monitoreo. Y la 8 te pone a construir la cáscara de resiliencia completa de una feature real de Mercado, ejecutada. La frontera con la guía de resiliencia es DURA: aquí aplicamos los patrones al componente de IA; su mecánica a fondo está en resilience-and-reliability-patterns-guide.

Y la promesa que se cumple en todo el módulo: nada se afirma "de memoria", todo se ejecuta. Cada simulación corre en Python, con el LLM simulado por un stub determinista que puede fallar —caído, lento, rate-limited, alucinando—, nunca una API real, sin claves ni red, y datos fijos, así que la salida que ves en cada bloque "Qué esperar" es la salida literal de correr el código. Puedes copiarlo y reproducirlo idéntico.

Tres analogías: el elevador, el GPS y el empleado

El elevador que si falla te deja usar las escaleras. Un edificio bien diseñado no depende de que el elevador funcione siempre. Cuando el elevador falla —y todos los elevadores fallan alguna vez—, no te quedas atrapado entre pisos ni el edificio deja de funcionar: usas las escaleras. Son más lentas, más incómodas, no llegan al piso 40 con la misma comodidad; pero te llevan a donde vas. El edificio se diseñó para degradar: cuando la ruta óptima (el elevador) no está, hay una ruta peor pero disponible (las escaleras) que mantiene el edificio en pie. Un edificio sin escaleras —donde el elevador es la única forma de subir— es un edificio que se cae entero cada vez que se cae el elevador. Eso es un sistema de IA sin fallback: cuando el modelo cae, todo cae.

El GPS que si pierde señal te da la última ruta conocida. Vas manejando con el GPS y entras a un túnel: se pierde la señal. Un GPS bien hecho no se apaga ni te deja a ciegas en mitad del túnel; sigue mostrándote la última ruta conocida y una estimación de dónde estás, y cuando recuperas señal, se recalibra. Te da una respuesta degradada —menos precisa, basada en datos viejos— en vez de ninguna respuesta. Un GPS que se apagara por completo al perder señal sería inútil justo cuando más lo necesitas. Eso es una respuesta cacheada como fallback: cuando el modelo no está, sirves la última respuesta buena que tenías, marcándola como lo que es.

El empleado que cuando no sabe, escala al supervisor en vez de inventar. Un buen empleado de atención al cliente, cuando le preguntan algo que no sabe con certeza —"¿cuándo llega exactamente mi pedido?", "¿me pueden reembolsar este cargo?"—, no se inventa una respuesta para quedar bien. Dice "déjame verificar" o escala al supervisor. Un mal empleado inventa: te da una fecha que sonó bien, te promete un reembolso que no puede autorizar, y crea un problema mayor que el de admitir que no sabía. La diferencia entre los dos no es inteligencia; es que el bueno sabe cuándo no confiar en su propia respuesta y tiene a dónde escalar. Eso es exactamente lo que hay que enseñarle a un componente de IA: cuando no puede verificar un dato, escala o degrada, no alucina.

Aquí está el punto que une las tres: un sistema resiliente tiene siempre una ruta de salida cuando la ruta preferida falla, y esa ruta de salida es peor pero funciona. El elevador tiene las escaleras; el GPS tiene la última ruta; el empleado tiene al supervisor. Tu sistema de IA tiene el fallback: cuando el modelo cae, está lento, o no puede verificar lo que dice, hay una ruta determinista —keywords en vez de semántica, cache en vez de modelo, humano en vez de adivinar— que mantiene el sistema en pie. La resiliencia no es que el modelo nunca falle (va a fallar); es que cuando falle, el sistema no.

Ejemplo trabajado: el modelo cae, el sistema no

No vamos a decir que un fallback mantiene el sistema en pie: lo vamos a ejecutar y a medir. Modelamos la búsqueda semántica de Mercado con la cáscara de resiliencia completa —timeout + circuit breaker + fallback— y simulamos una caída del modelo en mitad de un flujo de 50 requests. Comparamos dos diseños:

  • Sin fallback: el sistema depende directo del modelo. Si el modelo falla, el request se cae.
  • Con fallback + timeout + circuit breaker: cuando el modelo falla, el sistema degrada a una ruta determinista (búsqueda por keywords en memoria) que no depende del modelo y por eso nunca falla.

El modelo está simulado por un stub que lanza un error durante la caída (índices 20 a 24 de 50 requests, es decir, el modelo está arriba el 90% del tiempo). El fallback es una función determinista local. El circuit breaker deja de llamar al modelo cuando lo ve fallar tres veces seguidas, para no seguir pagando el timeout de una API que ya sabemos que está caída.

# Leccion 1 (intro M5): un sistema llama al LLM con TIMEOUT + CIRCUIT BREAKER
# + FALLBACK. Se SIMULA el modelo caido (sin red, sin API, sin claves).
# Medimos la disponibilidad del sistema CON vs SIN fallback, y vemos el
# breaker abrirse durante una caida del modelo.


class ModelError(Exception):
    pass


# El modelo esta CAIDO durante una ventana (indices 20..24): una caida real,
# concentrada, no fallos sueltos. 5 de 50 requests -> modelo 90% arriba.
OUTAGE = set(range(20, 25))
TIMEOUT_COST_MS = 800   # lo que "cuesta" esperar un timeout antes de rendirse


def call_model(i):
    # Stub determinista del LLM. Durante la caida cuelga y agota el timeout
    # (lanza ModelError). Fuera de la caida responde sano.
    if i in OUTAGE:
        raise ModelError("model timeout")
    return f"respuesta-semantica-{i}"


# Fallback determinista: NO depende del modelo (busqueda por keywords en
# memoria). Por eso nunca falla: la disponibilidad del sistema se despega de
# la del modelo.
def fallback(i):
    return f"respuesta-keywords-{i}"


class CircuitBreaker:
    # Mecanica a fondo: resilience-and-reliability-patterns-guide M5.
    def __init__(self, fail_threshold=3, cooldown=3):
        self.fail_threshold = fail_threshold
        self.cooldown = cooldown
        self.fails = 0
        self.state = "CLOSED"
        self.opened_at = None

    def allow(self, now):
        if self.state == "OPEN":
            if now - self.opened_at >= self.cooldown:
                self.state = "HALF_OPEN"   # deja pasar UN intento de prueba
                return True
            return False                    # OPEN: no llamamos al modelo
        return True

    def on_success(self):
        self.fails = 0
        self.state = "CLOSED"

    def on_failure(self, now):
        self.fails += 1
        if self.fails >= self.fail_threshold:
            self.state = "OPEN"
            self.opened_at = now


N = 50

# --- Modo A: SIN fallback (el sistema depende directo del modelo) ---
responded_A = 0
for i in range(N):
    try:
        call_model(i)
        responded_A += 1
    except ModelError:
        pass   # sin fallback: el request se cae
avail_A = responded_A / N * 100

# --- Modo B: CON timeout + breaker + fallback ---
cb = CircuitBreaker(fail_threshold=3, cooldown=3)
responded_B = model_calls = timeouts_paid = skipped_by_breaker = 0
trace = []
for i in range(N):
    if cb.allow(i):
        decision = cb.state
        try:
            call_model(i)
            cb.on_success()
            served = "modelo"
            model_calls += 1
        except ModelError:
            timeouts_paid += 1        # pagamos el timeout antes de rendirnos
            cb.on_failure(i)
            fallback(i)
            served = "fallback (modelo fallo)"
    else:
        decision = "OPEN"
        skipped_by_breaker += 1       # breaker OPEN: ni llamamos al modelo
        fallback(i)
        served = "fallback (breaker OPEN)"
    responded_B += 1
    if 19 <= i <= 26:
        trace.append((i, decision, served))
avail_B = responded_B / N * 100

print("Traza del incidente (modelo caido en indices 20..24):")
print(f"  {'req':<5}{'breaker':<11}servido por")
print("  " + "-" * 45)
for i, st, served in trace:
    print(f"  {i:<5}{st:<11}{served}")

print()
print("Disponibilidad del sistema (respondio / total):")
print(f"  SIN fallback : {responded_A}/{N} = {avail_A:.1f}%")
print(f"  CON fallback : {responded_B}/{N} = {avail_B:.1f}%")
print()
print("Costo del breaker durante la caida:")
print(f"  llamadas al modelo hechas   : {model_calls}")
print(f"  timeouts pagados            : {timeouts_paid}  "
      f"({timeouts_paid * TIMEOUT_COST_MS} ms esperando)")
print(f"  llamadas evitadas (breaker) : {skipped_by_breaker}  "
      f"({skipped_by_breaker * TIMEOUT_COST_MS} ms de espera AHORRADOS)")

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

Traza del incidente (modelo caido en indices 20..24):
  req  breaker    servido por
  ---------------------------------------------
  19   CLOSED     modelo
  20   CLOSED     fallback (modelo fallo)
  21   CLOSED     fallback (modelo fallo)
  22   CLOSED     fallback (modelo fallo)
  23   OPEN       fallback (breaker OPEN)
  24   OPEN       fallback (breaker OPEN)
  25   HALF_OPEN  modelo
  26   CLOSED     modelo

Disponibilidad del sistema (respondio / total):
  SIN fallback : 45/50 = 90.0%
  CON fallback : 50/50 = 100.0%

Costo del breaker durante la caida:
  llamadas al modelo hechas   : 45
  timeouts pagados            : 3  (2400 ms esperando)
  llamadas evitadas (breaker) : 2  (1600 ms de espera AHORRADOS)

Lee la salida con calma, porque ahí está el módulo entero en miniatura.

La disponibilidad es el número que importa. Sin fallback, el sistema respondió 45 de 50 requests = 90.0% —exactamente la disponibilidad del modelo, porque el sistema es el modelo: cuando el modelo cae, el request cae con él—. Con fallback, el sistema respondió 50 de 50 = 100.0%. Graba esa comparación: el modelo tiene la misma disponibilidad en los dos casos (90%); lo que cambió es que en el segundo, el sistema dejó de depender del modelo para responder. La ruta determinista —el fallback por keywords— siempre está, y siempre responde. Por eso el sistema es más disponible que su dependencia más frágil. Este es el resultado central de todo el módulo, hecho número: un modelo 90% disponible, dentro de un sistema con fallback, produce un sistema que responde el 100% de las veces.

La traza muestra el circuit breaker haciendo su trabajo. Mira los requests 20 a 24, que es donde el modelo está caído. En 20, 21 y 22, el breaker aún está CLOSED —no sabía que el modelo estaba caído—, así que llamó al modelo, esperó, y pagó el timeout (por eso "fallback (modelo fallo)": intentó el modelo, falló, cayó al fallback). Tres fallos seguidos y el breaker se abre. En 23 y 24 el breaker ya está OPEN: ni siquiera intenta el modelo, va directo al fallback ("fallback (breaker OPEN)"). Fíjate en el ahorro: pagó el timeout solo 3 veces (2400 ms), y el breaker le evitó 2 llamadas más a un modelo que ya sabía caído (1600 ms de espera ahorrados). En el request 25, tras el enfriamiento, el breaker pasa a HALF_OPEN y deja pasar un intento de prueba —el modelo ya se recuperó, así que responde—, y el breaker vuelve a CLOSED. El sistema se recuperó solo.

Junta las dos lecturas y tienes la arquitectura completa: el fallback mantiene la disponibilidad en 100% (nadie se quedó sin respuesta), el timeout evitó que cada fallo bloqueara al sistema para siempre (pagó un costo acotado, no infinito), y el circuit breaker dejó de golpear al modelo caído en cuanto lo detectó (ahorró latencia y protegió al modelo de más carga inútil). Ninguna de las tres piezas por sí sola es suficiente: sin fallback, el breaker solo te da un error más rápido; sin timeout, un cuelgue bloquea el sistema aunque tengas fallback; sin breaker, sigues pagando el timeout completo en cada request durante toda la caída. Juntas, el modelo cae y el sistema no.

Las ideas que instala este módulo, y dónde vive cada una

Ese ejemplo tocó, sin desarrollarlas del todo, las ideas del módulo. Vale la pena verlas explícitas, porque son la columna vertebral de las lecciones que siguen.

1. Los nuevos modos de fallo (lección 2). Un componente de IA falla de formas que un componente clásico no tiene: caído, lento, rate-limited (fallos ruidosos, que lanzan una excepción) y —la más peligrosa— la alucinación (fallo silencioso, que no lanza nada). La lección 2 ejecuta el contraste: un componente determinista falla fuerte o no falla; el de IA tiene una familia de fallos silenciosos que try/except no atrapa.

2. La alucinación como modo de fallo (lección 3). El modelo inventa un dato con confianza. La contención: verificar el claim contra una fuente de verdad determinista, y degradar (escalar, decir "no sé") cuando no se puede verificar. La lección 3 ejecuta un agente que cita datos de pedidos —los verificados se sirven, los inventados se bloquean—.

3. El modelo caído/lento/rate-limited y el timeout (lección 4). La dependencia de una API externa con cuotas. El timeout para no esperar para siempre. La lección 4 mide cómo un timeout de 800 ms recorta la espera de un cuelgue de 30 s.

4. Fallback y degradación (lección 5). La mitigación central: degradar a una ruta más simple en vez de caerse. La cascada (semántica → cache → keywords) y la respuesta degradada pero válida. La lección 5 mide la disponibilidad subiendo de 70% a 100%.

5. El circuit breaker sobre el modelo (lección 6). Dejar de llamar a un modelo que viene fallando. El ángulo de IA: cada llamada fallida cuesta latencia y dinero, y reintentar un modelo rate-limited lo empeora. La lección 6 mide las llamadas y el costo ahorrados.

6. El drift (lección 7). La degradación silenciosa en el tiempo. La defensa: medir con el eval de forma continua. La lección 7 dispara una alerta de drift dos semanas antes de que los usuarios se quejen.

Guarda este mapa; es la ruta del módulo:

Idea                                     Leccion   Concepto clave
───────────────────────────────────────  ────────  ──────────────────────────────
Los nuevos modos de fallo de la IA       L2        ruidoso (excepcion) vs
                                                    silencioso (alucinacion)
La alucinacion como modo de fallo        L3        verificar vs fuente de verdad;
                                                    degradar si no se puede
Caido / lento / rate-limited + timeout   L4        no esperar para siempre;
                                                    remite a resilience M2
Fallback y degradacion                   L5        ruta peor pero valida;
                                                    remite a resilience M7
El circuit breaker sobre el modelo       L6        dejar de golpear al modelo;
                                                    remite a resilience M5
El drift: la degradacion silenciosa      L7        medir en el tiempo; alerta
───────────────────────────────────────  ────────  ──────────────────────────────
Hacer resiliente una feature de Mercado  L8        el mini-proyecto, ejecutado

El mapa: dónde está este módulo en la guía y en el ecosistema

Este módulo es la quinta pieza de la cáscara determinista que rodea al componente de IA. Así se conecta con el resto de la guía:

flowchart TD
    M1["M1 · Ubicar el componente<br/>(contrato, frontera, nucleo/cascara)"]
    M2["M2 · Latencia y costo como arquitectura"]
    M3["M3 · El eval como fitness function"]
    M4["M4 · Guardrails y la frontera de confianza"]
    M5["M5 · Modos de fallo y resiliencia para IA"]
    M6["M6 · La cascara determinista"]
    M7["M7 · El lazo de datos y de retroalimentacion"]
    M8["M8 · Proyecto: arquitecta una feature de IA"]
    M1 --> M2 --> M3 --> M4 --> M5 --> M6 --> M7 --> M8

Léelo así: en M1 ubicaste el componente; en M2 le pusiste presupuesto; en M3 le pusiste una compuerta de calidad; en M4 le custodiaste la frontera de confianza; aquí (M5) lo haces resiliente a sus fallos propios: aceptas que va a alucinar, a caerse y a driftear, y diseñas para que ninguno de esos fallos tumbe el sistema. En M6 verás la cáscara determinista a fondo —el modelo propone, el sistema dispone— de la que la resiliencia de este módulo es una parte central; y en M7 cerrarás el lazo de datos, del que el monitoreo del drift de la lección 7 es la primera puntada.

Y la frontera con la guía de resiliencia, que hay que respetar y es DURA: la mecánica de timeouts, retries con backoff, circuit breakers, bulkheads y degradación no se enseña aquí a fondo. Eso vive en resilience-and-reliability-patterns-guide (M2 timeouts, M5 circuit breakers, M7 degradación), y esta guía la enlaza en cada lección donde aplica. Lo que sí tratamos es cómo esos patrones se aplican al componente de IA y —esto es lo propio de aquí— los fallos específicos de la IA que no existen en un componente clásico: la alucinación, la dependencia de una API de modelo con cuotas, y el drift. Cuando la lección 6 hable del circuit breaker, no te va a dar un curso de circuit breakers: te va a mostrar por qué un breaker sobre un modelo tiene una motivación extra —el costo por llamada y las cuotas del proveedor— que un breaker sobre un servicio clásico no tiene. La distinción es la misma que en todo el ecosistema: aquí tratamos la propiedad arquitectónica de la resiliencia aplicada a la IA, no la disciplina de resiliencia completa.

Errores comunes

Asumir que el LLM siempre responde. Qué pasa: el equipo escribe la búsqueda semántica como una llamada directa al modelo —"le paso la query, me devuelve resultados"— sin ninguna ruta alterna. El día que la API del modelo tiene una caída de veinte minutos, la caja de búsqueda de Mercado deja de funcionar por completo, y con ella una parte enorme del tráfico y de las ventas. Por qué pasa: en desarrollo el modelo siempre respondió, así que el fallo nunca se vio; se diseñó para el camino feliz. Cómo detectarlo: traza qué pasa en tu código cuando la llamada al modelo lanza una excepción o se cuelga; si la respuesta es "el request falla" o "se queda esperando", no tienes resiliencia. Cómo corregirlo: diseña asumiendo que el modelo va a fallar —caído, lento, rate-limited— y pon una ruta de fallback determinista, como el edificio pone escaleras junto al elevador. La lección 5 lo ejecuta: con fallback, la disponibilidad sube de 70% a 100%.

No tener fallback y tumbar todo el sistema cuando el modelo cae. Qué pasa: variante del anterior, pero peor: la feature de IA no está aislada, y su caída arrastra a otras partes del sistema —el hilo que esperaba al modelo se queda bloqueado, se acumulan requests, se agota el pool de conexiones, y un servicio que ni usaba IA se cae en cascada—. Por qué pasa: el componente de IA se trató como cualquier otra dependencia confiable, sin aislarlo ni acotarlo. Cómo detectarlo: un solo modelo caído puede degradar servicios que no dependen de él. Cómo corregirlo: aísla el componente de IA (la guía de resiliencia llama a esto bulkhead), ponle timeout y breaker, y dale un fallback para que su caída sea local y degradada, no global y total. Este módulo lo aplica; la mecánica del aislamiento a fondo está en la guía de resiliencia.

Confiar en una salida alucinada como si fuera cierta. Qué pasa: el agente de soporte le dice a un cliente "tu pedido llega el jueves y va con la guía TRK-4521", el cliente lo cree, y resulta que ese pedido no tiene esa guía —el modelo la inventó—. El fallo no lanzó ninguna excepción, no encendió ninguna alarma; el sistema entregó un dato falso con total naturalidad. Por qué pasa: la alucinación es un fallo silencioso; a diferencia de una caída, no se anuncia, y como la salida suena bien, nadie la revisa. Cómo detectarlo: tu sistema sirve datos que el modelo afirma sin verificarlos contra una fuente de verdad. Cómo corregirlo: trata la alucinación como un modo de fallo —verifica todo claim factual contra una fuente determinista antes de servirlo, y degrada (escala a humano, di "no puedo confirmarlo") cuando no puedas verificar—. La lección 3 lo ejecuta: los datos verificados se sirven, los inventados se bloquean.

Ejercicios

Ejercicio 1 — El elevador, el GPS y el empleado. Para cada una de las tres analogías del módulo, identifica (a) cuál es la ruta preferida que falla, (b) cuál es la ruta de fallback degradada, y (c) qué pieza arquitectónica de este módulo representa. Luego di, para la búsqueda semántica de Mercado, cuál es su ruta preferida y cuál su fallback.

Ver solución
  • El elevador: (a) ruta preferida = el elevador (rápido, cómodo); (b) fallback = las escaleras (más lentas, pero te llevan); (c) representa el fallback / degradación —cuando la ruta óptima cae, hay una ruta peor pero disponible—. La lección clave: sin escaleras, el edificio se cae con el elevador.
  • El GPS: (a) ruta preferida = navegación con señal en vivo; (b) fallback = la última ruta conocida (datos viejos, menos precisos); (c) representa la respuesta cacheada como fallback —sirves lo último bueno que tenías en vez de nada—.
  • El empleado: (a) ruta preferida = responder con certeza; (b) fallback = escalar al supervisor / decir "déjame verificar"; (c) representa la degradación ante la alucinación —cuando no sabes con certeza, escalas en vez de inventar—.
  • La búsqueda semántica de Mercado: ruta preferida = búsqueda semántica con el LLM (entiende la intención de "audífonos para correr bajo la lluvia"); fallback = búsqueda por keywords (no entiende la intención, pero encuentra productos que contienen esas palabras). Peor, pero el usuario recibe resultados. Es exactamente el patrón del elevador/escaleras aplicado a IA.

Ejercicio 2 — El número de la disponibilidad. En el ejemplo trabajado, el modelo tiene 90% de disponibilidad y el sistema con fallback llegó a 100%. Explica por qué el sistema puede ser más disponible que su dependencia más frágil (el modelo), y qué propiedad debe cumplir el fallback para que eso funcione. Luego responde: si el fallback por keywords fallara el 1% de las veces (por ejemplo, un problema de la base de datos local), ¿cuál sería aproximadamente la disponibilidad del sistema?

Ver solución

El sistema puede ser más disponible que el modelo porque no depende solo del modelo para responder: cuando el modelo falla, hay una segunda ruta (el fallback) que atiende el request. La disponibilidad del sistema no es la del modelo; es la probabilidad de que al menos una de las rutas funcione. Para que el sistema llegue a 100%, el fallback debe cumplir una propiedad clave: no depender del modelo (ni de nada que caiga junto con el modelo). En el ejemplo, el fallback es una búsqueda por keywords en memoria local —sin red, sin API externa—, así que es independiente de la caída del modelo y responde siempre.

Si el fallback fallara el 1% de las veces, el sistema solo se caería cuando ambas rutas fallen a la vez. El modelo cae el 10% y, en ese 10%, el fallback cae el 1%: la caída conjunta es aproximadamente 0.10 × 0.01 = 0.001 = 0.1%. La disponibilidad del sistema sería ~99.9%. La lección general: la disponibilidad se multiplica a tu favor cuando las rutas son independientes. Por eso importa tanto que el fallback no comparta la dependencia frágil (el modelo) con la ruta preferida; si el fallback también llamara al modelo, no habría ganancia.

Ejercicio 3 — Ruidoso vs silencioso. Clasifica cada uno de estos fallos del componente de IA como ruidoso (lanza una excepción que puedes atrapar) o silencioso (entrega una salida que parece válida) y di, para cada uno, cuál es la defensa arquitectónica adecuada: (a) la API del modelo devuelve HTTP 429 (rate limit); (b) el modelo tarda 40 segundos en responder; (c) el modelo afirma que el pedido A-1002 fue entregado, cuando en realidad está en proceso; (d) la API del modelo devuelve un error 503 (servicio no disponible).

Ver solución
  • (a) HTTP 429 (rate limit) → RUIDOSO. La API devuelve un error explícito; puedes atraparlo con un except. Defensa: circuit breaker (deja de llamar al modelo mientras esté rate-limited, para no empeorarlo) + fallback (sirve la ruta determinista mientras tanto). La lección 6 lo trata.
  • (b) Tarda 40 segundos → RUIDOSO (con timeout). Por sí solo, un cuelgue no lanza nada —te deja esperando—, pero el timeout lo convierte en un fallo ruidoso: cortas a los 800 ms y lanzas un TimeoutError que atrapas. Defensa: timeout + fallback. La lección 4 lo trata.
  • (c) Afirma un estado falso del pedido → SILENCIOSO. Es una alucinación: no lanza ninguna excepción, entrega una respuesta que suena perfecta y está mal. try/except no lo atrapa. Defensa: verificar el claim contra la fuente de verdad (el estado real del pedido) y degradar si no coincide. La lección 3 lo trata. Es el fallo más peligroso justo porque es silencioso.
  • (d) HTTP 503 → RUIDOSO. Error explícito de servicio caído. Defensa: fallback (degrada a la ruta determinista) + circuit breaker (si se repite, deja de intentar). La lección 5 lo trata.

El patrón: (a), (b) y (d) son fallos de disponibilidad —ruidosos, atrapables— y se defienden con timeout/breaker/fallback; (c) es un fallo de contenido —silencioso— y se defiende con verificación. El módulo cubre los dos frentes.

Resumen y siguiente paso

En esta lección instalaste la tesis que sostiene el módulo: un sistema AI-native debe estar diseñado para que el fallo del componente de IA no tumbe el sistema entero, y para que un fallo silencioso no pase por respuesta correcta. Lo viste con tres analogías —el elevador con sus escaleras, el GPS con su última ruta, el empleado que escala en vez de inventar— y lo mediste: un sistema con timeout + circuit breaker + fallback ante un modelo 90% disponible respondió el 100% de los requests, mientras el mismo sistema sin fallback se quedó en el 90% del modelo. Viste al circuit breaker abrirse tras tres fallos, dejar de golpear al modelo caído, y recuperarse solo. Y viste la distinción que organiza todo el módulo: fallos ruidosos (caído, lento, rate-limited, que una excepción atrapa) y fallos silenciosos (la alucinación, que solo una verificación ve).

Antes de avanzar deberías poder: explicar por qué un sistema puede ser más disponible que su dependencia más frágil; nombrar las tres piezas de la cáscara de resiliencia (timeout, circuit breaker, fallback) y qué hace cada una; distinguir un fallo ruidoso de uno silencioso; y ubicar la frontera dura con la guía de resiliencia (aquí se aplica, allá se enseña la mecánica).

La lección 2 toma la primera idea y la desarrolla a fondo: los nuevos modos de fallo de un componente de IA. Vas a ver, ejecutado, el contraste entre un componente clásico —que falla fuerte (excepción atrapable) o no falla— y un componente de IA, que abre una familia de fallos nueva, incluida la más peligrosa: la alucinación, un fallo silencioso que no lanza ninguna excepción y que solo una validación detecta. Con código, para que "el LLM falla distinto" deje de ser una advertencia y sea una taxonomía que puedes ver y contar.

Recursos

  • resilience-and-reliability-patterns-guide (este mismo ecosistema) — la referencia central para la mecánica de todo lo que este módulo aplica: M2 timeouts, M3 reintentos con backoff, M5 circuit breakers, M7 degradación elegante y load shedding. Cuando quieras implementar un circuit breaker de verdad (contadores, ventanas deslizantes, estados) o un timeout robusto, esa guía es el destino. Aquí lo aplicamos al componente de IA; allá se enseña a fondo.
  • Anthropic, documentación de Claude — docs.anthropic.com. Consulta las páginas de rate limits y de errores de la API para entender, a nivel conceptual, los modos de fallo de disponibilidad de un modelo servido por API (429 por cuota, errores 5xx, reintentos) —sin fijarte en una versión de modelo específica—. En inglés.
  • Chip Huyen, AI Engineering (O'Reilly, 2024). Los capítulos sobre confiabilidad y monitoreo de aplicaciones con modelos de fundación tratan los fallos propios de la IA —incluida la alucinación y el drift— como propiedades de diseño que hay que medir y contener. En inglés.
  • Martin Fowler y Bharani Subramaniam, "Emerging Patterns in Building GenAI Apps" — martinfowler.com/articles/gen-ai-patterns. Ubica la resiliencia y los fallbacks alrededor de un componente de IA en el mapa arquitectónico completo. En inglés.