Módulo 8: Proyecto — arquitecta una feature de IA en Mercado
Resiliencia y fallback
Descripción
Los pasos 2, 3 y 4 protegieron el costo, la calidad y la seguridad del agente de soporte —todos con el modelo funcionando—. El paso 5 protege la disponibilidad, que es lo que pasa cuando el modelo no funciona: cae, se pone lento, o se rate-limita. Esta lección llena el campo fallback de la hoja con las mitigaciones del módulo 5: una cascada de fallback (modelo → plantilla FAQ → escalar a humano) y un circuit breaker sobre el modelo. Vas a ver, ejecutado, cómo una caída del modelo no tumba la feature —la disponibilidad se sostiene en 100% con la cascada frente a caer sin ella—, y cómo el breaker deja de golpear un modelo rate-limited.
Y hay una decisión de contención que esta lección hace explícita y que es propia de una feature de tolerancia 3: el último recurso del fallback nunca auto-aprueba un reembolso. Cuando el modelo cae y llega un ticket que pide dinero, el sistema no inventa una respuesta ni aprueba a ciegas —escala a un humano—. Degradar para el agente de soporte no significa "responder peor a como sea"; significa "responder peor sin dejar de ser seguro". Una búsqueda que cae puede degradar a keywords sin riesgo; un agente que toca dinero debe degradar a un humano cuando el modelo no está, porque la alternativa —auto-aprobar sin el modelo ni el humano— es exactamente lo que la cáscara existe para impedir.
Conexión con el módulo. Esta lección protege todo lo que las anteriores construyeron: de nada sirve un agente barato (M2), de calidad probada (M3) y bien blindado (M4) si se cae cuando el proveedor del modelo tiene una interrupción. La cascada de fallback usa la caché de la lección 3 (que ahora cumple doble función como ruta degradada) y prepara la lección 7, donde la cáscara determinista valida las propuestas que sí llegan. La frontera con la guía de resiliencia es DURA: la mecánica de los patrones —los estados del circuit breaker con precisión, las ventanas de conteo, la calibración— se enseña en resilience-and-reliability-patterns-guide; aquí los aplicamos al componente de IA y remitimos allá para la implementación seria.
Una analogía: el hospital cuando se va la luz
Un hospital no puede permitirse dejar de funcionar cuando se va la luz. Por eso tiene una cascada de respaldo, ordenada de mejor a peor. La fuente preferida es la red eléctrica: barata, ilimitada, siempre disponible… hasta que no lo está. Cuando la red cae, arrancan los generadores diésel: más caros y ruidosos, pero mantienen los quirófanos funcionando durante horas. Y para los equipos que no pueden parpadear ni un segundo —un respirador, una máquina de soporte vital—, hay baterías que cubren el instante entre que se va la red y arrancan los generadores. Cada nivel es peor que el anterior (más caro, más limitado) pero infinitamente mejor que "nada": un quirófano con generador es peor que con la red, pero mucho mejor que un quirófano a oscuras a mitad de una operación.
Ahora fíjate en el detalle que hace especial al hospital: hay procedimientos que, si no hay energía confiable, simplemente no se hacen. No se inicia una cirugía electiva no urgente durante un apagón con solo generadores; se pospone o se escala a un centro con energía plena. El hospital degrada su capacidad, pero no baja sus estándares de seguridad: prefiere posponer un procedimiento a hacerlo sin las garantías. Degradar no es "operar como sea"; es "operar solo lo que se puede hacer con seguridad, y para el resto, escalar".
El agente de soporte es ese hospital. La cascada de fallback es red → generador → batería: el modelo es la red (la mejor respuesta, pero puede caer), la plantilla FAQ es el generador (responde lo común sin el modelo), y el humano es la batería para lo crítico (siempre disponible, resuelve cualquier cosa). Y la regla del hospital se traduce directo: cuando el modelo cae y llega un ticket que pide un reembolso —el "procedimiento crítico"—, el sistema no auto-aprueba (no "opera sin energía confiable"); escala a un humano. Degrada su capacidad de respuesta, pero no baja el estándar de seguridad. Esta lección monta esa cascada para el agente de soporte y mide que la feature no se caiga.
Ejemplo trabajado: la cascada aguanta una caída del modelo
Vamos a simular una caída del modelo y medir dos cosas: que la disponibilidad se sostenga en 100% con la cascada de fallback, y que el circuit breaker deje de golpear un modelo caído. El modelo cae del request 3 al 11 (nueve requests seguidos fallando, como una ventana de rate limit). Cada ticket intenta el modelo (gobernado por el breaker); si el modelo falla, cae a la plantilla FAQ para preguntas comunes, o escala a un humano para el resto. El humano nunca auto-aprueba.
# M8 Leccion 6 — RESILIENCIA (M5) del agente de soporte: cuando el modelo cae,
# el sistema NO se cae. Cascada de fallback (modelo -> plantilla FAQ -> escalar
# a humano, que nunca auto-aprueba) + circuit breaker sobre el modelo. El humano
# NUNCA auto-aprueba un reembolso: escalar es degradar seguro. STUB del modelo;
# sin red ni APIs. Mecanica a fondo: resilience-and-reliability-patterns-guide.
MODEL_COST = 0.002 # $ por intento de llamada al modelo
TIMEOUT_MS = 800 # ms perdidos cuando un fallo agota el timeout
class ModelError(Exception):
pass
N = 16
OUTAGE = set(range(3, 12)) # el modelo cae del request 3 al 11 (rate-limited)
# Tickets entrantes. Algunos son preguntas comunes (la plantilla FAQ los cubre);
# otros son casos que solo un humano puede resolver.
COMMON = {"tracking", "devolucion", "envio"}
QUERIES = ["tracking", "devolucion", "envio", "tracking", "envio",
"reembolso_complejo", "devolucion", "tracking", "envio",
"reembolso_complejo", "tracking", "devolucion", "envio",
"tracking", "reembolso_complejo", "devolucion"]
def call_model(i):
if i in OUTAGE:
raise ModelError("caido / rate-limited")
return "respuesta del modelo"
def fallback(query):
# Cascada de fallback: plantilla FAQ para lo comun; escalar a humano el resto.
# El humano es la red final: nunca falla y NUNCA auto-aprueba un reembolso.
if query in COMMON:
return "template"
return "human"
class CircuitBreaker:
def __init__(self, fail_threshold=3, cooldown=4):
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"
return True
return False
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
# --- Modo A: SIN fallback (solo modelo). Durante la caida, el ticket se cae. ---
responded_naive = 0
for i, q in enumerate(QUERIES):
try:
call_model(i)
responded_naive += 1
except ModelError:
pass # sin fallback: el cliente ve un error
# --- Modo B: cascada de fallback + circuit breaker. ---
cb = CircuitBreaker(fail_threshold=3, cooldown=4)
tiers = {"model": 0, "template": 0, "human": 0}
model_calls = 0
print(f"{'req':<5}{'query':<20}{'breaker':<11}{'tier servido':<14}calidad")
print("-" * 66)
for i, q in enumerate(QUERIES):
if cb.allow(i):
state = cb.state
try:
call_model(i)
cb.on_success()
model_calls += 1
tiers["model"] += 1
print(f"{i:<5}{q:<20}{state:<11}{'model':<14}optimo")
continue
except ModelError:
model_calls += 1
cb.on_failure(i)
else:
state = "OPEN"
tier = fallback(q)
tiers[tier] += 1
print(f"{i:<5}{q:<20}{state:<11}{tier:<14}degradado")
print("-" * 66)
avail_naive = responded_naive / N * 100
print(f"Disponibilidad: SIN fallback {responded_naive}/{N} = {avail_naive:.0f}%"
f" | CON cascada {N}/{N} = 100%")
print(f"Tiers servidos: model={tiers['model']} template={tiers['template']}"
f" human={tiers['human']}")
print(f"Llamadas al modelo (con breaker): {model_calls} "
f"(sin breaker habrian sido {N}: el breaker evito {N - model_calls})")
print("El sistema respondio los 16 tickets durante la caida; el breaker dejo de")
print("golpear un modelo rate-limited; el humano cubrio lo que la plantilla no.")
Qué esperar. Al correr el archivo, la salida es exactamente esta:
req query breaker tier servido calidad
------------------------------------------------------------------
0 tracking CLOSED model optimo
1 devolucion CLOSED model optimo
2 envio CLOSED model optimo
3 tracking CLOSED template degradado
4 envio CLOSED template degradado
5 reembolso_complejo CLOSED human degradado
6 devolucion OPEN template degradado
7 tracking OPEN template degradado
8 envio OPEN template degradado
9 reembolso_complejo HALF_OPEN human degradado
10 tracking OPEN template degradado
11 devolucion OPEN template degradado
12 envio OPEN template degradado
13 tracking HALF_OPEN model optimo
14 reembolso_complejo CLOSED model optimo
15 devolucion CLOSED model optimo
------------------------------------------------------------------
Disponibilidad: SIN fallback 7/16 = 44% | CON cascada 16/16 = 100%
Tiers servidos: model=6 template=8 human=2
Llamadas al modelo (con breaker): 10 (sin breaker habrian sido 16: el breaker evito 6)
El sistema respondio los 16 tickets durante la caida; el breaker dejo de
golpear un modelo rate-limited; el humano cubrio lo que la plantilla no.
Lee la traza y luego las métricas, porque juntas muestran la cascada y el breaker trabajando.
Fuera de la caída, todo va por el modelo. Los requests 0-2 y 13-15 se sirvieron por model, en calidad optimo: el modelo estaba sano, la cascada no cobró ningún costo. La resiliencia no le quita calidad al camino feliz; solo entra en acción cuando el modelo falla.
Durante la caída, la cascada baja de nivel según el ticket. Mira los requests 3-12. Los que preguntan cosas comunes —tracking, devolución, envío— caen a la plantilla FAQ (template): una respuesta determinista, sin modelo, que cubre el grueso del volumen. Los que piden algo que solo un humano puede resolver —reembolso_complejo— escalan a un humano (human): más lento y caro, pero seguro. Fíjate en la decisión de contención en los requests 5 y 9: son reembolsos complejos que llegaron durante la caída del modelo, y el sistema no los auto-aprobó —los escaló a un humano—. Esa es la regla del hospital: cuando falta la energía confiable (el modelo), el procedimiento crítico (el reembolso) no se hace a ciegas; se escala. El humano es la única garantía para una acción que toca dinero cuando el modelo no está.
La disponibilidad: de 44% a 100%. Sin fallback, el sistema respondió 7 de 16 —falló en los 9 requests de la caída— = 44%. Con la cascada, respondió 16 de 16 = 100%. El modelo tuvo la misma disponibilidad en ambos casos; lo que cambió es que el sistema dejó de depender del modelo para responder. Y el desglose por tier cuenta la historia: 6 óptimos, 8 por plantilla, 2 por humano —10 respuestas degradadas pero válidas que, sin la cascada, habrían sido errores—. Degradar es cualitativamente distinto de caerse: un sistema que se cae le dice al cliente "no puedo ayudarte"; uno que degrada le dice "puedo ayudarte un poco peor" —o, para el reembolso, "un humano te va a atender"—.
El circuit breaker: 10 llamadas en vez de 16. Sigue la columna breaker. En los requests 3-5 el breaker está CLOSED —aún no sabía de la caída—, así que llama al modelo, falla, y cuenta fallos (1, 2, 3). Al tercer fallo, se abre (OPEN): en los requests 6-8 no llama al modelo, va directo al fallback —ahí está el ahorro, tres llamadas evitadas a un modelo que ya sabíamos caído—. En el 9 (HALF_OPEN) deja pasar un intento de prueba, que falla (el modelo sigue caído), y se reabre. En el 13 (HALF_OPEN) el intento funciona (el modelo se recuperó) y se cierra. Total: 10 llamadas al modelo en vez de 16 —el breaker evitó 6—. Y aquí está lo propio de la IA: cada llamada evitada no es solo latencia ahorrada; es dinero ahorrado (cada llamada cuesta tokens) y carga que no le pusiste a un modelo que quizás está caído precisamente porque está saturado de llamadas. El breaker corta ese círculo vicioso.
Profundización: las reglas de una buena cascada aplicadas al soporte
El último nivel debe ser el más robusto, y para el soporte ese nivel es humano. La regla de oro del módulo 5: el nivel más bajo de la cascada debe ser el más robusto de todos, porque es el que atrapa lo que los de arriba dejan caer. En la búsqueda semántica, ese nivel es keyword —determinista, local, siempre devuelve algo—. En el agente de soporte es el humano, y la diferencia importa. La búsqueda puede tener un último recurso determinista porque un orden de resultados un poco peor no le hace daño a nadie; el soporte toca dinero y reclamos, donde un humano es la única garantía universal —puede atender cualquier ticket, incluso los que ninguna plantilla previó, y es la única "autoridad" que puede aprobar un reembolso cuando el modelo no está—. El último recurso se dimensiona por lo que la feature necesita: determinista para lo tolerante, humano para lo intolerante.
El fallback no debe compartir la dependencia frágil. Otra regla del módulo 5: el nivel final de la cascada no puede depender de lo mismo que la ruta preferida. Si tu fallback fuera "si el modelo grande falla, usa el modelo pequeño del mismo proveedor", el día que el proveedor entero cae, ambos modelos caen juntos y el fallback no sirve. Por eso la cascada del agente termina en dos rutas que no dependen del modelo: la plantilla FAQ (determinista, corre en tu proceso) y el humano (una persona, no una API). Las dos sobreviven a la caída que se supone que cubren. Un fallback que depende de lo mismo que la ruta preferida es un fallback de mentira.
Degradar honestamente: marca lo degradado. En la traza, cada respuesta lleva su calidad (optimo o degradado). Ese marcado tiene dos usos. Uno, para el monitoreo (la observabilidad de la lección 7): si de repente el 80% de las respuestas son degradadas, tienes una caída del modelo en curso y quieres saberlo. Dos, para el usuario, cuando aplica: un cliente cuyo ticket se escaló a un humano merece saber "un agente te va a atender en breve", no una respuesta inventada que finge ser del sistema normal. Lo que nunca haces es servir una respuesta degradada fingiendo que es óptima. Y para el soporte hay un tercer uso, propio de una feature que toca dinero: el marcado human en un reembolso complejo es la traza de auditoría de que ese reembolso lo aprobó una persona, no el modelo caído.
El breaker sobre un modelo tiene una motivación extra. Un circuit breaker clásico ahorra latencia y protege hilos. Un breaker sobre un modelo ahorra además dinero (cada llamada cuesta tokens) y evita empeorar un rate limit (si el modelo está caído por estar saturado, seguir llamándolo prolonga su saturación). Reintentar un 429 sigue consumiendo cuota; abrir el breaker no. Por eso, para el rate limit, el breaker no es solo una optimización de latencia: es la diferencia entre recuperarte en un minuto o quedarte bloqueado diez. La mecánica de los estados y su calibración vive en resilience-and-reliability-patterns-guide; aquí basta ver que el breaker, aplicado a un modelo, protege el presupuesto de la lección 3 durante una caída.
Errores comunes
No tener fallback y caer todo cuando el modelo cae. Qué pasa: el agente es una llamada directa al modelo sin ruta alterna; el día de una caída del proveedor, el soporte de Mercado deja de funcionar por completo, justo cuando más clientes escriben (una caída del modelo suele coincidir con incidentes que generan tickets). Por qué pasa: se diseñó el camino feliz, el fallo nunca se vio en desarrollo. Cómo detectarlo: traza qué pasa cuando la llamada al modelo lanza una excepción; si la respuesta es "el ticket falla", no tienes fallback. Cómo corregirlo: pon una cascada con un nivel final robusto —para el soporte, el humano—. El ejemplo lo mide: sin fallback 44%, con cascada 100%.
Auto-aprobar durante la degradación "para no molestar al humano". Qué pasa: el equipo, buscando que la feature "siga funcionando" durante una caída, hace que el fallback apruebe automáticamente los reembolsos pequeños sin el modelo ni el humano —"total, son montos chicos"—. Un atacante (o un bug) que sabe que durante las caídas hay auto-aprobación explota justamente ese momento, y salen reembolsos sin ninguna validación real. Por qué pasa: se confunde "degradar la respuesta" con "degradar la seguridad", y auto-aprobar parece más cómodo que escalar. Cómo detectarlo: tu ruta de fallback tiene un camino que ejecuta una acción que toca dinero sin el modelo ni un humano. Cómo corregirlo: degradar la calidad de respuesta, sí; degradar la seguridad, nunca. El último recurso para una acción que toca dinero es escalar a un humano, no auto-aprobar. Es la regla del hospital: se pospone el procedimiento crítico, no se hace sin garantías.
Un fallback que comparte la dependencia frágil. Qué pasa: el equipo pone como fallback "si el modelo falla, usa un modelo más simple del mismo proveedor"; el día que el proveedor entero cae, ambos caen juntos y el fallback no sirve. Por qué pasa: se pensó el fallback como "otra forma de hacer IA" sin ver que comparte la dependencia frágil (la red al proveedor). Cómo detectarlo: tu último nivel de fallback llama a una API externa o al mismo proveedor que la ruta preferida. Cómo corregirlo: el nivel final debe ser local o humano, sin la dependencia frágil —la plantilla FAQ corre en tu proceso, el humano es una persona—. El fallback debe sobrevivir a la caída que se supone que cubre.
Ejercicios
Ejercicio 1 — Por qué el reembolso complejo escala a humano y no a plantilla. En la cascada, las preguntas comunes caen a la plantilla FAQ, pero el reembolso_complejo escala a un humano. Explica por qué no podría caer a una plantilla, y qué principio de la feature intolerante (tolerancia 3) lo justifica.
Ver solución
Un reembolso complejo no puede caer a una plantilla porque una plantilla no puede tomar la decisión que el reembolso requiere. Las plantillas FAQ funcionan para preguntas cuya respuesta es fija e igual para todos ("¿cuánto tarda el envío?" → "de 3 a 5 días"). Pero un reembolso complejo —"me llegó roto, pagué con dos tarjetas, quiero reembolso parcial a una y el resto a saldo"— exige entender varias condiciones y decidir una acción que toca dinero, con lógica específica de ese caso. Ninguna plantilla previó esa combinación, y una plantilla que "adivinara" una respuesta a un reembolso sería peor que no responder —podría prometer algo incorrecto o, peor, aprobar un monto equivocado—.
El principio de la feature intolerante (tolerancia 3) que lo justifica: cuando el modelo no está, una acción que toca dinero solo la puede autorizar un humano, nunca una regla automática degradada. La tolerancia 3 significa que cada propuesta de reembolso debe validarse con todo el rigor —y durante una caída del modelo, el rigor que falta (la comprensión del caso más la validación de la cáscara) solo lo aporta un humano—. Escalar no es un lujo; es la única forma de mantener la feature disponible sin bajar su estándar de seguridad. Para una feature tolerante (la búsqueda), el último recurso puede ser determinista porque no hay una decisión de dinero que tomar; para el agente de reembolsos, el último recurso es humano porque sí la hay. La cascada se dimensiona por la tolerancia, igual que la cáscara.
Ejercicio 2 — El tradeoff del enfriamiento del breaker. Mira el request 12 de la traza: el breaker está OPEN y sirve template, aunque el modelo ya se recuperó en el request 11. Explica por qué el breaker sirvió un fallback "de más", qué tradeoff representa, y por qué casi siempre vale la pena.
Ver solución
En el request 12 el breaker sirvió un fallback aunque el modelo ya funcionaba porque el breaker no lo sabía todavía. La caída terminó en el request 11, pero el breaker estaba en OPEN en su enfriamiento (se reabrió en el request 9 tras el intento de prueba fallido, y su cooldown de 4 no se había cumplido: 12 − 9 = 3 < 4), así que no probó el modelo en el 12 —fue directo al fallback—. El breaker no prueba hasta el request 13, donde detecta la recuperación y se cierra.
El tradeoff que representa: durante el enfriamiento, el breaker sirve algún fallback de más —respuestas degradadas cuando el modelo ya estaba disponible— a cambio de no golpear el modelo caído una y otra vez durante la caída real. Un enfriamiento corto detecta la recuperación rápido (menos fallbacks de más) pero prueba más seguido (más llamadas de prueba, más riesgo de reabrir en falso); uno largo protege más pero tarda en notar la recuperación. Es un cambio deliberado: "alguna respuesta degradada de más" por "muchas llamadas inútiles menos".
Por qué casi siempre vale la pena: durante una caída, el número de llamadas inútiles evitadas (y el dinero, y la carga que no le pones al modelo rate-limited) supera con creces el costo de unas pocas respuestas degradadas de más al final del enfriamiento. En el ejemplo, el breaker evitó 6 llamadas a cambio de servir una respuesta degradada de más (el request 12). Ese intercambio es favorable en casi cualquier escenario real, donde las caídas duran mucho más que el enfriamiento. La calibración exacta —cuánto enfriamiento— vive en resilience-and-reliability-patterns-guide.
Ejercicio 3 — Degradación parcial de una respuesta del agente. El agente de soporte, además de responder el ticket, muestra en su respuesta: (a) el estado del pedido (de la base de datos), (b) una respuesta redactada por el modelo, y (c) una sugerencia de "productos relacionados" generada por IA. Si el modelo cae, ¿qué parte degradas y cómo, y qué parte no se toca? ¿Por qué es mejor que caer toda la respuesta?
Ver solución
Con el modelo caído, degradas solo lo que depende de la IA y dejas intacto lo que no:
- (a) Estado del pedido → NO se toca. Viene de la base de datos determinista, no del modelo. La caída del modelo no lo afecta; se muestra normal. El cliente sigue viendo dónde está su pedido.
- (b) Respuesta redactada por el modelo → degradar. Cae a una plantilla FAQ si la pregunta es común ("¿cómo devuelvo esto?" → la plantilla de devoluciones), o a un mensaje de escalamiento ("un agente te atenderá en breve") si no lo es. Peor redactada, pero informativa y honesta.
- (c) Sugerencia de productos relacionados por IA → degradar u ocultar. Cae a recomendaciones no personalizadas (los más vendidos, calculados sin IA) o simplemente se oculta la sección —una respuesta de soporte sin "productos relacionados" sigue siendo perfectamente útil—.
Por qué es mejor que caer toda la respuesta: la mayor parte de lo que el cliente necesita no depende del modelo. El estado de su pedido —la información más importante en un ticket de soporte— viene de la base de datos y está disponible aunque el modelo caiga. Tirar toda la respuesta por un fallo que solo afecta a la parte redactada y a una sugerencia secundaria sería negarle al cliente la información crítica (dónde está su pedido) por un problema en las partes accesorias. La degradación parcial aísla el fallo a las partes afectadas y mantiene funcional el núcleo. Es la degradación elegante en su forma más fina: cada parte degrada a su propio fallback, y lo que no depende del modelo ni se entera. La mecánica de este patrón vive en resilience-and-reliability-patterns-guide.
Resumen y siguiente paso
En esta lección llenaste el campo fallback de la hoja: la resiliencia del agente de soporte. Con el hospital y su cascada de energía (red → generador → batería, y los procedimientos críticos que se posponen en vez de hacerse sin garantías), viste que degradar no es bajar el estándar de seguridad. Y lo ejecutaste: una caída del modelo de 9 requests, y la cascada de fallback (modelo → plantilla FAQ → escalar a humano) sostuvo la disponibilidad en 100% frente al 44% sin fallback —6 óptimos, 8 por plantilla, 2 escalados a humano—. El circuit breaker recortó las llamadas de 16 a 10, dejando de golpear un modelo rate-limited. Y viste la decisión de contención propia de una feature de tolerancia 3: cuando el modelo cae y llega un reembolso, el sistema no auto-aprueba —escala a un humano—, porque el último recurso para una acción que toca dinero es humano, no una regla automática. Degradar honestamente en vez de caerse, sin bajar el estándar de seguridad.
Antes de avanzar deberías poder: diseñar una cascada de fallback con un último recurso robusto (humano para el soporte); explicar por qué el fallback no debe compartir la dependencia frágil; argumentar por qué degradar es mejor que caer y por qué no se degrada la seguridad; y reconocer las motivaciones extra de un circuit breaker sobre un modelo (dinero, rate limit).
La lección 7 monta los dos campos que cierran la contención: la cáscara determinista y el lazo de datos. Vas a ver la cáscara medida en dinero —el modelo propone, el sistema dispone, y una capa determinista protege $5,190 de propuestas peligrosas— y vas a cerrar el lazo de datos: la observabilidad de calidad que un log de servidor no ve, y el feedback loop que convierte los thumbs_down en vivo en casos nuevos del eval-set —cerrando el círculo hacia la lección 4—. La misma cáscara que protege el dinero alimenta la calidad.
Recursos
resilience-and-reliability-patterns-guide(este ecosistema) — la referencia central para la mecánica de la degradación, los circuit breakers y el load shedding que aquí solo aplicamos: los estados con precisión, las ventanas de conteo, la calibración del enfriamiento, la degradación parcial por prioridad. En español.- Martin Fowler y Bharani Subramaniam, "Emerging Patterns in Building GenAI Apps" — martinfowler.com/articles/gen-ai-patterns. Trata los fallbacks y las rutas degradadas alrededor de un componente de IA como patrones de primera clase. En inglés.
- Anthropic, documentación de Claude — docs.anthropic.com. Las páginas de rate limits describen las cuotas y el comportamiento del 429 —el modo de fallo que hace del breaker sobre un modelo algo más que una optimización de latencia—. Conceptual, sin fijar versión. En inglés.
architecture-for-ai-native-systems-guide, Módulo 5 (este ecosistema) — el tratamiento a fondo del fallback, el circuit breaker, el timeout, la degradación y el drift. Esta lección aplica al agente de soporte lo que el M5 desarrolló. En español.