Módulo 8: Proyecto Integrador — Deployed AI System

7. Performance Baseline — Establecer Métricas de Referencia

Descripción

En esta cápsula vas a establecer baselines de performance para tu sistema AI desplegado: latencia de inferencia, costes mensuales, error rates, y uptime. Un baseline es el "normal" de tu sistema — sin él, no sabes si algo está mejorando, degradándose, o roto. Al terminar, tendrás targets documentados y herramientas gratuitas para monitorearlos.

Contexto: Tu sistema está desplegado y validado. Ahora necesitas definir qué es "normal" para poder detectar anomalías. Este es el puente entre este módulo (deployment) y la guía #18 (Monitoring & Observability): aquí estableces los baselines que la guía #18 te enseña a monitorear con herramientas avanzadas.


Por Qué Necesitas Baselines

Sin baseline vs con baseline

SIN BASELINE:
- "La app parece lenta" → ¿Comparada con qué?
- "Los costes subieron" → ¿Desde cuándo? ¿Cuánto es normal?
- "Hay muchos errores" → ¿Qué es "muchos"?

CON BASELINE:
- "Latencia p95 está en 4.2s, el baseline es 2.5s" → 68% degradado, investigar
- "Costes este mes: $45, baseline: $20" → 125% incremento, verificar tráfico
- "Error rate: 3.5%, baseline: 0.5%" → 7x incremento, algo está roto

Los baselines convierten percepciones subjetivas ("parece lento") en datos objetivos ("está 68% más lento que lo normal").

Las 4 métricas esenciales

┌─────────────────────────────────────────────────────────┐
│                 PERFORMANCE BASELINES                     │
│                                                          │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌─────────┐ │
│  │ LATENCIA │  │  COSTES  │  │  ERRORS  │  │ UPTIME  │ │
│  │          │  │          │  │          │  │         │ │
│  │ p50, p95 │  │ $/mes    │  │ % rate   │  │ % disp. │ │
│  │ p99      │  │ desglose │  │ por tipo │  │ minutos │ │
│  └──────────┘  └──────────┘  └──────────┘  └─────────┘ │
│                                                          │
│  Target: <3s    Target:<$50  Target:<1%   Target:>99%    │
└─────────────────────────────────────────────────────────┘

Baseline 1: Latencia

Cómo medir latencia de tu sistema AI

# scripts/measure_latency.py
"""
Mide latencia del sistema AI en producción.
Ejecutar: python scripts/measure_latency.py https://tu-app.railway.app 20
"""
import sys
import time
import json
import urllib.request
import statistics

def measure_endpoint(base_url: str, num_requests: int = 20):
    """Mide latencia de inferencia con múltiples requests."""

    url = f"{base_url}/api/inference"
    latencies = []
    errors = 0

    print(f"Measuring latency: {num_requests} requests to {url}")
    print()

    for i in range(num_requests):
        payload = json.dumps({
            "prompt": f"Respond briefly: what is the number {i+1}?"
        }).encode()

        headers = {"Content-Type": "application/json"}
        req = urllib.request.Request(url, data=payload, headers=headers)

        start = time.time()
        try:
            response = urllib.request.urlopen(req, timeout=30)
            elapsed_ms = (time.time() - start) * 1000
            latencies.append(elapsed_ms)

            status = "OK" if response.status == 200 else f"HTTP {response.status}"
            print(f"  Request {i+1:3d}: {elapsed_ms:7.0f}ms — {status}")
        except Exception as e:
            elapsed_ms = (time.time() - start) * 1000
            errors += 1
            print(f"  Request {i+1:3d}: {elapsed_ms:7.0f}ms — ERROR: {e}")

        time.sleep(0.5)

    if not latencies:
        print("No successful requests")
        return

    latencies.sort()
    n = len(latencies)

    results = {
        "total_requests": num_requests,
        "successful": n,
        "errors": errors,
        "error_rate": f"{(errors/num_requests)*100:.1f}%",
        "latency_ms": {
            "min": round(latencies[0]),
            "max": round(latencies[-1]),
            "mean": round(statistics.mean(latencies)),
            "median_p50": round(latencies[n // 2]),
            "p90": round(latencies[int(n * 0.9)]),
            "p95": round(latencies[int(n * 0.95)]),
            "p99": round(latencies[int(n * 0.99)]) if n >= 100 else "N/A (need 100+ samples)",
            "std_dev": round(statistics.stdev(latencies)) if n > 1 else 0,
        },
    }

    print()
    print("=" * 50)
    print("LATENCY BASELINE RESULTS")
    print("=" * 50)
    print(f"  Requests: {n} successful / {num_requests} total")
    print(f"  Error rate: {results['error_rate']}")
    print(f"  Latency (ms):")
    print(f"    Min:    {results['latency_ms']['min']}ms")
    print(f"    p50:    {results['latency_ms']['median_p50']}ms")
    print(f"    p90:    {results['latency_ms']['p90']}ms")
    print(f"    p95:    {results['latency_ms']['p95']}ms")
    print(f"    Max:    {results['latency_ms']['max']}ms")
    print(f"    StdDev: {results['latency_ms']['std_dev']}ms")
    print()

    p95 = results["latency_ms"]["p95"]
    if p95 < 3000:
        print(f"  STATUS: GOOD — p95 ({p95}ms) < 3000ms target")
    elif p95 < 5000:
        print(f"  STATUS: WARNING — p95 ({p95}ms) > 3000ms but < 5000ms")
    else:
        print(f"  STATUS: CRITICAL — p95 ({p95}ms) > 5000ms")

    return results

if __name__ == "__main__":
    base_url = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:8000"
    num_req = int(sys.argv[2]) if len(sys.argv) > 2 else 20
    measure_endpoint(base_url, num_req)

Output esperado

Measuring latency: 20 requests to https://mi-app.railway.app/api/inference

  Request   1:    3245ms — OK
  Request   2:    1890ms — OK
  Request   3:    2100ms — OK
  ...
  Request  20:    1945ms — OK

==================================================
LATENCY BASELINE RESULTS
==================================================
  Requests: 20 successful / 20 total
  Error rate: 0.0%
  Latency (ms):
    Min:    1654ms
    p50:    2100ms
    p90:    2890ms
    p95:    3100ms
    Max:    3245ms
    StdDev: 420ms

  STATUS: WARNING — p95 (3100ms) > 3000ms but < 5000ms

Definir targets de latencia

latency_targets = {
    "health_check": {
        "target_ms": 200,
        "acceptable_ms": 500,
        "critical_ms": 1000,
    },
    "inference": {
        "target_ms": 2000,
        "acceptable_ms": 3000,
        "critical_ms": 5000,
    },
    "notes": [
        "Inferencia incluye: network + preprocessing + LLM API call + postprocessing",
        "La mayor parte del tiempo (~70-80%) es la llamada al LLM",
        "Cold starts en free tier pueden agregar 5-15s al primer request",
        "p95 es la métrica principal — no p50 ni media",
    ],
}

Baseline 2: Costes

Desglose de costes mensuales

# scripts/cost_baseline.py
"""Calcula y documenta el baseline de costes."""

def calculate_monthly_costs(
    daily_requests: int,
    avg_input_tokens: int = 500,
    avg_output_tokens: int = 300,
    platform_cost: float = 0,
) -> dict:
    """Estima costes mensuales para un sistema AI."""
    monthly_requests = daily_requests * 30

    gpt4o_mini_input = 0.15 / 1_000_000
    gpt4o_mini_output = 0.60 / 1_000_000

    llm_input_cost = monthly_requests * avg_input_tokens * gpt4o_mini_input
    llm_output_cost = monthly_requests * avg_output_tokens * gpt4o_mini_output
    llm_total = llm_input_cost + llm_output_cost

    embedding_cost_per_request = 0.02 / 1_000_000 * 500
    embedding_total = monthly_requests * embedding_cost_per_request

    total = llm_total + embedding_total + platform_cost

    return {
        "monthly_requests": monthly_requests,
        "llm_cost": round(llm_total, 2),
        "embedding_cost": round(embedding_total, 2),
        "platform_cost": platform_cost,
        "total_monthly": round(total, 2),
        "cost_per_request": round(total / monthly_requests * 1000, 4),
    }

# Escenarios
scenarios = {
    "Current (150 users)": calculate_monthly_costs(
        daily_requests=500,
        avg_input_tokens=600,
        avg_output_tokens=300,
        platform_cost=0,  # free tier
    ),
    "Growth (500 users)": calculate_monthly_costs(
        daily_requests=2000,
        avg_input_tokens=600,
        avg_output_tokens=300,
        platform_cost=5,  # Railway Pro
    ),
    "Scale (2000 users)": calculate_monthly_costs(
        daily_requests=8000,
        avg_input_tokens=600,
        avg_output_tokens=300,
        platform_cost=20,  # Railway Pro + more resources
    ),
}

print("COST BASELINE")
print("=" * 60)
for name, costs in scenarios.items():
    print(f"\n{name}:")
    print(f"  Monthly requests: {costs['monthly_requests']:,}")
    print(f"  LLM cost:        ${costs['llm_cost']:.2f}/mes")
    print(f"  Embedding cost:  ${costs['embedding_cost']:.2f}/mes")
    print(f"  Platform cost:   ${costs['platform_cost']:.2f}/mes")
    print(f"  TOTAL:           ${costs['total_monthly']:.2f}/mes")
    print(f"  Per 1K requests: ${costs['cost_per_request']:.4f}")

Definir targets de costes

cost_targets = {
    "monthly_budget": 50,  # $50/mes máximo
    "alert_threshold": 40,  # Alertar al 80% del presupuesto
    "breakdown_expected": {
        "llm_api": "60-70% del total",
        "platform": "20-30% del total",
        "other": "<10% del total",
    },
    "monitoring": {
        "openai": "platform.openai.com → Usage → set spending limit",
        "platform": "Dashboard → Billing → set alerts",
        "review": "Semanal: verificar que el gasto va en línea con proyección",
    },
}

Configurar alertas de costes

OpenAI:
  1. platform.openai.com → Settings → Limits
  2. Set monthly budget: $30 (deja margen para infra)
  3. Set email alert at: $20

Railway:
  1. Dashboard → Settings → Usage Alerts
  2. O monitorear manualmente el Usage meter

Fly.io:
  1. Dashboard → Billing → set spending limit

AWS:
  1. AWS Budgets → Create budget → Monthly cost budget
  2. Alert at 80% and 100% of budget

Baseline 3: Error Rate

Medir error rate actual

# scripts/error_baseline.py
"""Mide error rate del sistema con requests variados."""
import sys
import json
import urllib.request
import urllib.error

def measure_error_rate(base_url: str, num_requests: int = 50):
    """Envía requests variados y mide la tasa de error."""

    test_cases = [
        {"prompt": "What is machine learning?", "expect": 200},
        {"prompt": "Explain Docker in one sentence", "expect": 200},
        {"prompt": "What is 2+2?", "expect": 200},
        {"prompt": "Summarize the benefits of CI/CD", "expect": 200},
        {"prompt": "Hello", "expect": 200},
    ]

    results = {"total": 0, "success": 0, "client_error": 0,
               "server_error": 0, "timeout": 0, "other": 0}
    error_details = []

    print(f"Measuring error rate: {num_requests} requests to {base_url}")

    for i in range(num_requests):
        test = test_cases[i % len(test_cases)]
        url = f"{base_url}/api/inference"
        payload = json.dumps({"prompt": test["prompt"]}).encode()
        headers = {"Content-Type": "application/json"}
        req = urllib.request.Request(url, data=payload, headers=headers)

        results["total"] += 1
        try:
            response = urllib.request.urlopen(req, timeout=30)
            if response.status == test["expect"]:
                results["success"] += 1
            else:
                results["client_error"] += 1
                error_details.append(f"Request {i+1}: expected {test['expect']}, got {response.status}")
        except urllib.error.HTTPError as e:
            if 400 <= e.code < 500:
                results["client_error"] += 1
            else:
                results["server_error"] += 1
            error_details.append(f"Request {i+1}: HTTP {e.code}")
        except TimeoutError:
            results["timeout"] += 1
            error_details.append(f"Request {i+1}: timeout")
        except Exception as e:
            results["other"] += 1
            error_details.append(f"Request {i+1}: {type(e).__name__}")

    total = results["total"]
    error_count = total - results["success"]
    error_rate = (error_count / total) * 100 if total > 0 else 0

    print()
    print("ERROR RATE BASELINE")
    print("=" * 50)
    print(f"  Total requests: {total}")
    print(f"  Successful:     {results['success']} ({(results['success']/total)*100:.1f}%)")
    print(f"  Client errors:  {results['client_error']}")
    print(f"  Server errors:  {results['server_error']}")
    print(f"  Timeouts:       {results['timeout']}")
    print(f"  Other errors:   {results['other']}")
    print(f"  ERROR RATE:     {error_rate:.1f}%")
    print()

    if error_rate < 1:
        print(f"  STATUS: GOOD — error rate ({error_rate:.1f}%) < 1% target")
    elif error_rate < 5:
        print(f"  STATUS: WARNING — error rate ({error_rate:.1f}%) > 1% but < 5%")
    else:
        print(f"  STATUS: CRITICAL — error rate ({error_rate:.1f}%) > 5%")

    if error_details:
        print(f"\n  Error details ({len(error_details)}):")
        for detail in error_details[:10]:
            print(f"    - {detail}")

    return results

if __name__ == "__main__":
    url = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:8000"
    num = int(sys.argv[2]) if len(sys.argv) > 2 else 50
    measure_error_rate(url, num)

Definir targets de error rate

error_targets = {
    "overall_error_rate": {
        "target": "< 1%",
        "acceptable": "< 5%",
        "critical": "> 5%",
    },
    "by_type": {
        "server_errors_5xx": "< 0.1% — indica bugs en tu código",
        "timeouts": "< 0.5% — indica problemas de infra o LLM API",
        "client_errors_4xx": "No cuenta como 'error' — es uso incorrecto del cliente",
    },
    "notes": [
        "OpenAI API tiene ~0.1-0.5% error rate propio (rate limits, server errors)",
        "Free tier platforms tienen más errores por cold starts y resource limits",
        "Error rate = (server_errors + timeouts) / total_requests",
    ],
}

Baseline 4: Uptime

Medir y monitorear uptime

Uptime = tiempo que el servicio está accesible / tiempo total

Target    Downtime/mes   Downtime/año
99%       7.3 hrs        3.65 días
99.5%     3.65 hrs       1.83 días
99.9%     43.8 min       8.77 hrs
99.99%    4.38 min       52.6 min

Para un sistema AI en free tier:
  Target realista: 99% (7 hrs downtime/mes máximo)
  Incluye: cold starts, platform maintenance, deploys

Para un sistema AI en plan pago:
  Target realista: 99.5% (3.65 hrs downtime/mes)

Para producción enterprise:
  Target: 99.9%+ (requiere infra dedicada)

Herramientas gratuitas de uptime monitoring

UptimeRobot (recomendado):
  - 50 monitors gratis
  - Check cada 5 minutos
  - Status page público (opcional)
  - Alertas: email, Slack, webhook
  - Histórico de uptime (% mensual)

Configuración:
  Monitor 1: GET /health → cada 5 min
  Monitor 2: POST /api/inference con body → cada 15 min (verifica inferencia)

Better Stack:
  - 10 monitors gratis
  - Check cada 3 minutos
  - Status page incluido
  - Incident management básico

Freshping:
  - 50 monitors gratis
  - Check cada 1 minuto
  - Multi-location checks

Documento de Performance Baseline

Template completo

# Performance Baseline — [Nombre del Sistema AI]

**Fecha de baseline:** [Fecha]
**URL producción:** [URL]
**Plataforma:** [Railway/Render/Fly.io/AWS]
**Plan:** [Free/Pro/etc.]

## Latencia

| Métrica | Valor baseline | Target | Alerta si |
|---------|---------------|--------|-----------|
| p50 | [X]ms | <2000ms | >2500ms |
| p90 | [X]ms | <2500ms | >3000ms |
| p95 | [X]ms | <3000ms | >4000ms |
| Max | [X]ms | <5000ms | >8000ms |
| Health check | [X]ms | <200ms | >500ms |

**Medido con:** [N] requests el [fecha]
**Nota:** [Observaciones sobre cold starts, variabilidad, etc.]

## Costes

| Componente | Baseline | Budget | Alerta si |
|-----------|----------|--------|-----------|
| LLM API (OpenAI) | $[X]/mes | $[Y]/mes | >$[Z]/mes |
| Platform (infra) | $[X]/mes | $[Y]/mes | >$[Z]/mes |
| Total | $[X]/mes | $[Y]/mes | >$[Z]/mes |
| Per 1K requests | $[X] | - | >$[Y] |

**Spending alerts configuradas:** [Sí/No] [Dónde]

## Error Rate

| Métrica | Valor baseline | Target | Alerta si |
|---------|---------------|--------|-----------|
| Overall error rate | [X]% | <1% | >2% |
| Server errors (5xx) | [X]% | <0.1% | >0.5% |
| Timeouts | [X]% | <0.5% | >1% |

**Medido con:** [N] requests el [fecha]

## Uptime

| Métrica | Target | Monitoreo |
|---------|--------|-----------|
| Monthly uptime | >99% | UptimeRobot |
| Max downtime/incident | <30 min | Alertas configuradas |
| Max downtime/mes | <7 hrs | Review mensual |

**Monitoring configurado:** [Herramienta, URL del dashboard]
**Alertas configuradas:** [Email/Slack cuando downtime >5 min]

## Cuándo Escalar

| Trigger | Métrica actual | Threshold | Acción |
|---------|---------------|-----------|--------|
| Latencia sostenida | [X]ms p95 | >5000ms por >1 hora | Escalar recursos o optimizar |
| Error rate alto | [X]% | >5% por >30 min | Investigar + posible rollback |
| Costes excedidos | $[X]/mes | >$[Y]/mes | Review de tráfico y optimización |
| Memoria alta | [X]% | >90% sostenido | Escalar RAM o optimizar |

Cuándo Escalar

Señales de que necesitas más recursos

ESCALAR VERTICALMENTE (más CPU/RAM):
├── Latencia p95 sostenida > 2x baseline por > 1 hora
├── Memoria > 90% del límite disponible
├── CPU > 80% sostenido
└── Acción: Upgrade de plan en la plataforma

ESCALAR HORIZONTALMENTE (más instancias):
├── Requests concurrentes > capacidad de una instancia
├── Queue de requests creciendo
├── Latencia incrementa linealmente con tráfico
└── Acción: Agregar réplicas (si la plataforma lo permite)

OPTIMIZAR CÓDIGO (antes de escalar infra):
├── Caching de responses repetitivas
├── Reducir tokens en prompts
├── Cambiar a modelo más rápido (gpt-4o-mini vs gpt-4o)
├── Async processing para requests pesados
└── Acción: Optimizar ANTES de pagar más infra

Decision tree para escalar

¿Latencia alta?
├── ¿Es la API del LLM? (>70% del tiempo total)
│   ├── SÍ → Optimizar prompts, reducir tokens, o cambiar modelo
│   └── NO → ¿Es tu código?
│       ├── SÍ → Profiling, optimizar, caching
│       └── NO → ¿Es la plataforma?
│           ├── SÍ → Escalar recursos o cambiar plataforma
│           └── NO → Investigar networking
│
¿Costes altos?
├── ¿Es el LLM API? (revisar token usage)
│   ├── SÍ → Reducir tokens, caching, rate limiting
│   └── NO → ¿Es la infra?
│       ├── SÍ → Evaluar plan actual vs alternativas
│       └── NO → ¿Tráfico legítimo o abuso?
│           ├── Legítimo → Escalar (invertir en crecimiento)
│           └── Abuso → Rate limiting, firewall

Troubleshooting

Problema 1: "La latencia varía mucho entre requests"

Causa: Cold starts, variabilidad de la API del LLM, o garbage collection en Python.

Solución:

# 1. Excluir el primer request (cold start) del baseline
latencies = latencies[1:]  # Skip warm-up request

# 2. Reportar desviación estándar junto con p50/p95
# StdDev alto (>50% de la media) = alta variabilidad
# StdDev bajo (<20% de la media) = consistente

# 3. Implementar warm-up en el pipeline
# Después del deploy, enviar 2-3 requests de warm-up antes de smoke tests

Problema 2: "No puedo medir costes porque estoy en free tier"

Causa: Free tier no tiene billing visible.

Solución: Mide el uso, no el coste directo.

# Medir uso de OpenAI (que sí tiene billing)
# platform.openai.com → Usage → ver por día

# Estimar coste de infra si migras a plan pago
estimated_infra = {
    "Railway Pro": 5,        # $5/mes base
    "Render Starter": 7,     # $7/mes
    "Fly.io": 0,             # Free VMs, pago por uso adicional
    "AWS Lambda": 0,         # Free tier: 1M requests
}

Problema 3: "El error rate es 0% pero sé que a veces falla"

Causa: Estás midiendo con pocos requests o con prompts muy simples.

Solución:

# Usar prompts más variados y realistas
edge_case_prompts = [
    "A" * 5000,                    # Prompt muy largo
    "🎉 emoji test 中文 عربي",      # Unicode
    "Repeat the word 'test' 500 times",  # Request de muchos tokens
    "",                             # Prompt vacío (debería dar 422)
    "x" * 100000,                  # Prompt extremadamente largo
]
# Medir con al menos 50 requests para un baseline significativo

Problema 4: "No sé qué targets poner para latencia"

Solución:

La regla práctica para apps AI:
- Si es chat interactivo: p95 < 3s (los usuarios esperan respuesta rápida)
- Si es API backend: p95 < 5s (depende del consumer)
- Si es batch processing: p95 < 30s (no es interactivo)

Baseline primero, targets después:
1. Mide 20+ requests
2. Calcula p50 y p95
3. Target = p95 actual + 20% margen
4. Alerta = p95 actual + 50% margen
5. Crítico = p95 actual × 2

Ejercicios Prácticos

Ejercicio 1: Medir latencia baseline

Ejecuta el script de medición de latencia contra tu sistema desplegado y documenta los resultados.

Ver solución
# Ejecutar medición
python scripts/measure_latency.py https://tu-app.railway.app 20

# Resultado esperado:
# p50: ~2000ms (depende del modelo y plataforma)
# p95: ~3000ms
# StdDev: ~400-800ms

# Documentar en tu performance baseline:
# "Latencia medida el [fecha] con 20 requests.
#  p50: 2100ms, p95: 3050ms, max: 3500ms.
#  El 80% del tiempo es la llamada a OpenAI API.
#  Cold start del primer request: 12s (excluido del baseline)."

Si tu p95 es > 5s excluyendo cold starts, investiga: puede ser la plataforma (free tier con recursos limitados), el modelo (gpt-4o es más lento que gpt-4o-mini), o tu código (procesamiento excesivo).

Ejercicio 2: Calcular cost baseline

Ejecuta el script de costes con tus datos reales y configura alertas.

Ver solución
# Con tus datos:
my_costs = calculate_monthly_costs(
    daily_requests=500,     # Tu tráfico actual
    avg_input_tokens=600,   # Medir con tiktoken
    avg_output_tokens=300,  # Estimar o medir
    platform_cost=0,        # Free tier
)

print(f"Estimated monthly cost: ${my_costs['total_monthly']:.2f}")
# Ejemplo: $2.70/mes en free tier con 500 req/día

# Configurar alerta en OpenAI:
# platform.openai.com → Settings → Limits → $10/mes

El coste más difícil de predecir es el de la API del LLM, porque depende del largo de los prompts y respuestas. Mide tokens reales con tiktoken para tener un baseline preciso.

Ejercicio 3: Medir error rate

Ejecuta el script de error rate y documenta los resultados.

Ver solución
python scripts/error_baseline.py https://tu-app.railway.app 50

# Resultado esperado:
# Error rate: 0-2% (depende de la estabilidad de tu sistema)
# Server errors: 0% (ideal)
# Timeouts: 0-1% (puede ocurrir en free tier)

# Si error rate > 5%:
# 1. Revisa los detalles de errores
# 2. ¿Son todos del mismo tipo? (ej: todos timeout)
# 3. ¿Es la API de OpenAI? (rate limiting)
# 4. ¿Es tu código? (bug en el handler)

Ejercicio 4: Documento de performance baseline completo

Crea docs/performance-baseline.md usando el template de esta cápsula con tus datos reales.

Ver solución
# Performance Baseline — Mi AI System

**Fecha de baseline:** 2026-03-08
**URL producción:** https://mi-app.railway.app
**Plataforma:** Railway (Free tier)

## Latencia
| Métrica | Baseline | Target | Alerta |
|---------|----------|--------|--------|
| p50 | 2100ms | <2500ms | >3000ms |
| p95 | 3050ms | <3500ms | >4500ms |

Medido con 20 requests el 2026-03-08.
Nota: Cold start del primer request ~12s (excluido).

## Costes
| Componente | Baseline | Budget |
|-----------|----------|--------|
| OpenAI API | $2.70/mes | $10/mes |
| Railway | $0/mes (free) | $5/mes |
| Total | $2.70/mes | $15/mes |

## Error Rate
| Métrica | Baseline | Target |
|---------|----------|--------|
| Overall | 0.0% | <1% |
| Timeouts | 0.0% | <0.5% |

Medido con 50 requests el 2026-03-08.

## Uptime
Monitoring: UptimeRobot (cada 5 min)
Target: >99% mensual

Este documento se actualiza mensualmente o cuando hay cambios significativos en el sistema.


Resumen

  • Baselines convierten percepciones ("parece lento") en datos ("p95 está 68% sobre el target")
  • Las 4 métricas esenciales: latencia (p50/p95), costes ($/mes), error rate (%), uptime (%)
  • Mide antes de definir targets — los targets se basan en datos reales, no aspiraciones
  • Herramientas gratuitas cubren el monitoreo básico: UptimeRobot, OpenAI Usage dashboard, scripts propios
  • Cuándo escalar: primero optimiza código (caching, prompts más cortos), después escala infra
  • El documento de performance baseline es un artefacto vivo — actualízalo mensualmente
  • Este baseline es la entrada para la guía #18 (Monitoring & Observability), que profundiza en métricas avanzadas

Recursos Adicionales

  1. UptimeRobot — Monitoreo de uptime gratuito
  2. OpenAI Usage Dashboard — Monitoreo de uso de API
  3. Google SRE Book — Service Level Objectives — SLOs y SLIs
  4. Latency Numbers Every Programmer Should Know — Referencia de latencias
  5. tiktoken — OpenAI Tokenizer — Contar tokens para estimar costes
  6. Cloud Cost Handbook — Referencia de costes cloud