Saltar al contenido principal

Desafío 05: Funciona localmente, falla en producción a escala

Resumen del escenario

Industria: Retail / E-Commerce | Contexto regulatorio: Obligaciones de SLA, NIST AI RMF MANAGE 1.3
Tiempo estimado: 90 minutos | Costo de Azure: ~$5–10


Qué está en juego

TechMart lanzó un asistente de compras con IA para Black Friday. Funcionó perfectamente en desarrollo con 5 usuarios concurrentes. Con 500 usuarios concurrentes en producción, 40% de las solicitudes agotan el tiempo de espera y el otro 60% devuelve recomendaciones de productos desactualizadas.

"Estamos perdiendo $15,000/minuto en carritos abandonados. El agente está ahí, pero no responde. Tenemos 4 horas para corregirlo antes de reiniciar la campaña."

Debes diagnosticar y corregir una falla de Hosted Agent en producción bajo presión de tiempo.


Habilidades practicadas

  • Leer logs de contenedor de Hosted Agent y métricas de Micro-VM
  • Diagnosticar problemas de arranque en frío vs. escalado
  • Configurar ajustes de concurrencia y timeout
  • Implementar el patrón circuit breaker para fallas de APIs descendentes
  • Usar Azure Monitor para identificar cuellos de botella en tiempo real

Marco diagnóstico

Cuando un Hosted Agent falla a escala, revisa en este orden:

1. Is the agent running? → Azure Monitor: agent health metrics
2. Is it receiving requests? → Application Insights: request rate
3. Is it timing out? → Check timeout config + downstream latency
4. Is it hitting resource limits? → Micro-VM CPU/memory metrics
5. Are downstream tools failing? → Tool call success rate in traces
6. Is there a VNet routing issue? → NSG flow logs + private endpoint health

🧰 Antes de empezar — Configuración del entorno

Este desafío es un ejercicio de resiliencia en producción: un agente que funciona en desarrollo colapsa a escala. Tu configuración necesita observabilidad y una forma de generar carga concurrente para reproducir y corregir la falla.

Prerrequisitos

RequisitoPor qué lo necesitasCómo verificar
Suscripción de Azure + un Hosted Agent desplegadoLo que está bajo cargaAzure portal
Azure Monitor / Application InsightsLeer métricas de salud, tasa de solicitudes, trazasAzure portal
Azure CLIInspeccionar configuración del agente, timeouts, escaladoaz version
Herramienta de generación de carga — Azure Load Testing (o locust (tercero))Reproducir la concurrencia que lo rompeAzure portal / pip show locust
Acceso a las herramientas/APIs descendentes del agenteLas correcciones de circuit breaker y latencia viven aquíconfiguración de app

Paso 0 — Mira el sistema primero (5 min) — el "a dónde voy"

Antes de cambiar algo, abre los paneles. Bajo presión, el instinto es adivinar — resístelo y lee las métricas.

  1. az login.
  2. Abre el recurso Application Insights de tu Hosted Agent → Live metrics y las secciones Failures + Performance (App Insights overview). Si tu agente no tiene App Insights adjunto, agrega uno desde el recurso del agente → Monitoring.
  3. Anota tres números base con el tráfico actual: tasa de solicitudes, tasa de fallas y latencia p95 de dependencias.

Listo cuando puedas ver tasa de solicitudes en vivo, tasa de fallas y latencia de dependencias para el agente — esta es la instrumentación de la que dependen las Tareas 1–3.

Paso 1 — Reproducir la falla con carga (10 min)

No puedes corregir lo que no puedes reproducir. Crea un recurso de Azure Load Testing y aumenta la concurrencia (p. ej., 5 → 100 → 500) para encontrar dónde empiezan los timeouts — ese punto de inflexión es tu objetivo. Sigue el create-and-run quickstart; apunta la prueba al endpoint de tu agente.

Listo cuando una prueba de carga reproduzca la falla (la tasa de error sube con mayor concurrencia) y hayas registrado el nivel de concurrencia donde empieza — ese número enmarca cada corrección.

🟦 Nota Microsoft-first: diagnóstico y carga son nativos de Microsoft — Azure Monitor, Application Insights, métricas de Micro-VM de Hosted Agent y Azure Load Testing. locust se lista solo como alternativa local de terceros para generación rápida de carga.

Correcciones comunes: la prueba de carga no alcanza el agente → revisa la URL del endpoint + cualquier header de autenticación requerido. Sin métricas de Micro-VM → confirma que el objetivo es Hosted Agent (no una ejecución local) y que las métricas están habilitadas en Azure Monitor.

El recorrido por este desafío

  1. Marco diagnóstico — recorre el checklist de 6 pasos en orden.
  2. Tarea 1 — leer logs + métricas de Micro-VM para encontrar el cuello de botella.
  3. Tarea 2 — corregir configuración de concurrencia/timeout.
  4. Tarea 3 — agregar un circuit breaker para fallas descendentes.
  5. Criterios de éxito — menos de 5% de errores con 100 usuarios concurrentes.
  6. Adáptalo a tu negocio — endurecer tu agente para carga pico.

⏱️ Presupuesto de tiempo: ~90 minutos. Sigue el orden diagnóstico — saltar a una solución antes de encontrar el cuello de botella es cómo se agota el reloj de 4 horas.


Tus tareas

Tarea 1: Habilitar monitoreo integral

import os
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential

client = AIProjectClient(
endpoint=os.environ["PROJECT_ENDPOINT"],
credential=DefaultAzureCredential()
)

# Enable telemetry with Application Insights connection string
connection_string = client.telemetry.get_connection_string()
print(f"App Insights connection string: {connection_string}")

# Enable automatic tracing
client.telemetry.enable()

# Now ALL agent operations are automatically traced
# Query agent performance metrics in Azure Monitor
az monitor metrics list \
--resource /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.MachineLearningServices/workspaces/<project> \
--metric "AgentRequestCount,AgentRequestLatency,AgentErrorRate" \
--interval PT1M \
--output table

Tarea 2: Implementar reintento + circuit breaker

import asyncio
import time
from typing import Optional

class AgentCircuitBreaker:
"""Prevent cascade failures when the agent is overwhelmed."""

def __init__(self, failure_threshold=5, recovery_timeout=30):
self.failure_count = 0
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.last_failure_time: Optional[float] = None
self.state = "CLOSED" # CLOSED=normal, OPEN=failing, HALF_OPEN=testing

def call(self, func, *args, **kwargs):
if self.state == "OPEN":
if time.time() - self.last_failure_time > self.recovery_timeout:
self.state = "HALF_OPEN"
else:
raise Exception("Circuit breaker OPEN — service unavailable. Try again in 30s.")

try:
result = func(*args, **kwargs)
if self.state == "HALF_OPEN":
self.state = "CLOSED"
self.failure_count = 0
return result
except Exception as e:
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = "OPEN"
raise e

# Usage
breaker = AgentCircuitBreaker(failure_threshold=5, recovery_timeout=30)

def get_product_recommendation(user_query: str) -> str:
return breaker.call(
lambda: client.agents.runs.create_and_process(
thread_id=thread.id,
agent_id=agent.id,
timeout=10 # 10 second hard timeout per request
)
)

Tarea 3: Implementar connection pooling + procesamiento asíncrono

import asyncio
from concurrent.futures import ThreadPoolExecutor
from azure.ai.projects.aio import AIProjectClient as AsyncAIProjectClient

async def process_user_request(client: AsyncAIProjectClient, user_query: str, session_id: str):
"""Process a single user request asynchronously."""
thread = await client.agents.threads.create()

await client.agents.messages.create(
thread_id=thread.id,
role="user",
content=user_query
)

run = await client.agents.runs.create_and_process(
thread_id=thread.id,
agent_id=os.environ["AGENT_ID"],
)

messages = await client.agents.messages.list(thread_id=thread.id)
return {"session_id": session_id, "response": messages.data[0].content[0].text.value}

async def process_batch(queries: list[dict]) -> list[dict]:
"""Process up to 50 concurrent requests."""
async with AsyncAIProjectClient(
endpoint=os.environ["PROJECT_ENDPOINT"],
credential=DefaultAzureCredential()
) as client:
# Limit concurrency to avoid overwhelming the agent
semaphore = asyncio.Semaphore(50)

async def bounded_call(query):
async with semaphore:
return await process_user_request(client, query["text"], query["session_id"])

return await asyncio.gather(*[bounded_call(q) for q in queries], return_exceptions=True)

Tarea 4: Script de health check de producción

import httpx
import asyncio

async def check_agent_health():
"""Run every 30 seconds in production."""
checks = {
"endpoint_reachable": False,
"agent_responds": False,
"latency_ms": None,
"error": None,
}

start = time.time()
try:
# Quick smoke test
thread = client.agents.threads.create()
client.agents.messages.create(thread_id=thread.id, role="user", content="ping")
run = client.agents.runs.create_and_process(thread_id=thread.id, agent_id=os.environ["AGENT_ID"])

checks["endpoint_reachable"] = True
checks["agent_responds"] = run.status == "completed"
checks["latency_ms"] = int((time.time() - start) * 1000)

# Clean up health check thread
client.agents.threads.delete(thread.id)

except Exception as e:
checks["error"] = str(e)

# Alert if latency > 5s or agent not responding
if checks["latency_ms"] and checks["latency_ms"] > 5000:
print(f"⚠️ HIGH LATENCY: {checks['latency_ms']}ms — investigate scaling")
if not checks["agent_responds"]:
print(f"🚨 AGENT NOT RESPONDING: {checks['error']}")

return checks

Tarea 5: Pruebas de escala con simulación de carga

# Install hey (HTTP load tester)
# Run 500 concurrent requests to your agent endpoint
hey -n 1000 -c 500 -t 30 \
-H "Authorization: ****** account get-access-token --query accessToken -o tsv)" \
-m POST \
-T "application/json" \
-d '{"query": "Show me gaming laptops under $1000"}' \
https://<your-endpoint>/process-query

# Expected output shows:
# - Response time distribution
# - Error rate (target: <1%) - check for 429 rate limit errors too
# - Throughput (requests/second)

Criterios de éxito

  • Application Insights muestra trazas para todas las solicitudes del agente (no solo errores)
  • El circuit breaker bloquea correctamente solicitudes cuando el conteo de fallas supera el umbral
  • El procesamiento batch asíncrono maneja 50 solicitudes concurrentes sin timeouts
  • El script de health check identifica correctamente un agente detenido o lento
  • La prueba de carga muestra <5% de tasa de error con 100 usuarios concurrentes

🔁 Adáptalo a tu propio negocio

El escenario es un asistente de compras de Black Friday, pero cualquier agente enfrenta la brecha entre "funciona en la demo" y "sobrevive tráfico real". El enfoque diagnosticar-en-orden + patrones de resiliencia aplica a todo agente de producción.

Paso 1 — Encuentra tu momento de carga pico

IndustriaEvento picoQué falla primero
Retail / e-commerceBlack Friday / venta relámpagoTimeouts, recomendaciones obsoletas
Servicios financierosApertura de mercado, temporada fiscalSaturación de APIs descendentes
SaludPeriodos de inscripciónRecuperación lenta bajo concurrencia
Viajes / hospitalidadPicos de reservas en festivosArranques en frío, rate limits
Sector públicoFechas límite de presentaciónColas acumuladas, solicitudes descartadas

Paso 2 — Mapea los bloques de construcción a tu stack (Microsoft-first)

En este desafíoEn tu proyecto — usa
Métricas de salud + solicitudesAzure Monitor + Application Insights
Métricas de contenedor / Micro-VMMétricas de Hosted Agent en Azure Monitor
Generación de cargaAzure Load Testing (locust como opción local de terceros)
Ajuste de concurrencia / timeoutConfiguración de escalado + timeout de Hosted Agent
Circuit breakerResiliencia en código (p. ej., Polly para .NET) para llamadas descendentes
Cache de datos calientesAzure Cache for Redis para recomendaciones/consultas

Paso 3 — Checklist de implementación de 5 preguntas

  1. ¿Puedes ver en vivo tasa de solicitudes, tasa de fallas y latencia? Si no → instrumenta primero, corrige después.
  2. ¿Puedes reproducir la falla con carga? Si no → ejecuta Azure Load Testing antes de cambiar algo.
  3. ¿Es arranque en frío o saturación? Lee métricas de Micro-VM — la corrección es completamente distinta.
  4. ¿Las fallas descendentes se propagan en cascada? Si sí → agrega circuit breaker + timeouts.
  5. ¿Los datos calientes se recalculan en cada solicitud? Si sí → cachea (Azure Cache for Redis).

Paso 4 — Plan de despliegue de 1 semana

DíaAcciónPropietario
Día 1Agregar instrumentación completa de solicitudes/dependenciasSRE
Día 2Prueba de carga para encontrar el punto de inflexión de fallaSRE
Día 3Ajustar configuración de concurrencia + timeoutsDesarrollador backend
Día 4Agregar circuit breaker + timeouts descendentesDesarrollador backend
Día 5Agregar cache; repetir prueba de carga para confirmar menos de 5% de erroresSRE

Paso 5 — Demuestra el ROI

  • Tasa de error a carga objetivo — % de solicitudes fallidas a concurrencia pico (objetivo: bajo 5%).
  • Latencia P95 bajo carga — tiempo de respuesta en pico (objetivo: dentro del SLA).
  • Impacto de arranque en frío — % de solicitudes lentas atribuibles al arranque en frío (objetivo: cerca de 0).

💡 Regla práctica: "funciona localmente" prueba corrección; producción prueba concurrencia. Instrumenta, reproduce con carga y luego corrige en orden diagnóstico — adivinar bajo un reloj de pérdida de ingresos alarga las interrupciones.

Hacerlo en solitario (sin equipo, orientado a portafolio)

¿Sin equipo ni presupuesto? Un reporte de prueba de carga antes/después es uno de los artefactos más claros de "hago sistemas production-ready" que puedes mostrar. Ejecuta la semana en solitario:

  • Lun–Mar — instrumenta solicitudes/fallas/latencia y luego ejecuta una primera prueba de carga para encontrar el punto de inflexión.
  • Mié–Jue — ajusta concurrencia/timeouts, agrega un circuit breaker y cachea datos calientes.
  • Vie — repite la prueba de carga y captura el gráfico antes/después de tasa de error vs. concurrencia.

📦 Entrega este artefacto: un reporte de prueba de carga (antes/después) que muestre tasa de error bajo 5% en pico. Bullet para CV: "Endurecí un agente para tráfico pico — mantuve tasa de error bajo 5% y p95 dentro del SLA con 500 usuarios concurrentes."

🆓 Ruta free-tier: si no hay presupuesto para Azure Load Testing, ejecuta locust (tercero) localmente contra un endpoint de nivel gratuito — el reporte se ve igual.


📋 Mapeo regulatorio — SLA · EU AI Act · NIST
RequisitoRegulaciónImplementación
Compromisos de confiabilidad del servicioSLA / ContratoCircuit breaker + monitoreo de salud
Registro de incidentesEU AI Act Art. 20Todas las fallas registradas con contexto en Azure Monitor
Monitoreo de desempeñoNIST AI RMF MANAGE 1.3Métricas de latencia + alertas automatizadas
Degradación eleganteBuena práctica de ingenieríaCircuit breaker devuelve mensaje fallback útil

💡 Pistas
  1. Arranque en frío vs. carga sostenida: Si la PRIMERA solicitud agota el tiempo pero las siguientes tienen éxito, es arranque en frío. Si la degradación ocurre gradualmente, es límite de recursos o cuello de botella descendente.
  2. Tamaño del semáforo: Semaphore(50) limita a 50 llamadas concurrentes al agente. Ajusta según tu cuota de Foundry (el límite de tokens por minuto afecta más el throughput que la concurrencia bruta).
  3. Limpieza de threads: Cada client.agents.threads.create() crea un thread persistente. Elimina siempre los threads de health check. A 500 req/min, acumularás 30,000 threads/hora si no limpias.
  4. El cuello de botella real suele ser el modelo: A escala, la cuota de tokens por minuto (TPM) de GPT-4o suele ser el límite vinculante, no el runtime del agente. Revisa errores 429 Too Many Requests en tus trazas.

Comprobación de conocimientos

  1. ¿Cuál es la diferencia entre una falla de arranque en frío y una falla de carga sostenida en Hosted Agents?
  2. ¿Cuándo debe un circuit breaker transicionar de OPEN a HALF_OPEN?
  3. ¿Por qué el procesamiento asíncrono es más eficiente que threading para llamadas de agentes I/O-bound?
  4. ¿Sobre qué métrica de Azure Monitor alertarías para detectar degradación del agente antes de que los clientes lo noten?

Limpieza

# No persistent infrastructure beyond the agent itself
# Delete test threads via SDK if you created many