Módulo 6: Decision Matrix para AI Engineers

Cápsula 05: Análisis de Costos y ROI

🎯 Objetivo de la cápsula

Dominar el análisis financiero real de vector databases: calcular TCO (Total Cost of Ownership) a diferentes escalas, identificar costos ocultos, modelar break-even de migración y predecir cuándo el free tier se acaba y los costos se disparan.

Al finalizar esta cápsula:

  • ✅ Calcularás el TCO real (no solo el sticker price) de cada opción
  • ✅ Identificarás los 5 costos ocultos que nadie menciona en pricing pages
  • ✅ Modelarás break-even de migración self-hosted → managed (y viceversa)
  • ✅ Proyectarás costos a 3 escalas: 10K, 100K y 1M vectores

Tiempo estimado: 25-35 minutos


Descripción de la cápsula

El error financiero más costoso al elegir una vector database no es pagar de más — es no saber cuánto estás pagando realmente. Las páginas de pricing muestran el costo del servicio, pero eso representa apenas el 30-40% del costo total. El 60-70% restante se esconde en horas de ingeniería, egress de datos, incidentes, costo de oportunidad y el salto imprevisto cuando cruzas un umbral de pricing.

Esta cápsula te da las herramientas para calcular el Total Cost of Ownership (TCO) real en cuatro dimensiones: infraestructura, operación, riesgo e integración. Modelarás costos a tres escalas (10K, 100K y 1M vectores) para cada modalidad (managed vs self-hosted), identificarás los "cliff edges" donde el costo se dispara, y construirás un modelo de break-even para decidir si migrar vale la inversión.

La meta no es encontrar "lo más barato". Es encontrar la opción que maximiza valor por dólar en tu contexto. A veces eso significa pagar más por managed; a veces significa invertir en ops para self-hosted. Los números te dirán cuál.


La fórmula de TCO completa

La mayoría de equipos comparan solo el precio del servicio. El TCO real tiene cuatro dimensiones:

def calculate_tco(
    infra_cost: float,
    ops_hours: float,
    engineer_rate: float,
    incident_probability: float,
    incident_cost: float,
    integration_hours: float = 0,
    integration_rate: float = 0
) -> dict:
    """
    Calcula el Total Cost of Ownership mensual.

    Args:
        infra_cost: Costo mensual del servicio/infraestructura
        ops_hours: Horas mensuales de operación/mantenimiento
        engineer_rate: Tarifa por hora del ingeniero (USD)
        incident_probability: Probabilidad mensual de incidente (0-1)
        incident_cost: Costo estimado por incidente (USD)
        integration_hours: Horas de integración inicial (amortizar en 12 meses)
        integration_rate: Tarifa del ingeniero para integración
    """
    ops_cost = ops_hours * engineer_rate
    risk_cost = incident_probability * incident_cost
    integration_amortized = (integration_hours * integration_rate) / 12

    tco = infra_cost + ops_cost + risk_cost + integration_amortized

    return {
        "infra": round(infra_cost, 2),
        "ops": round(ops_cost, 2),
        "risk": round(risk_cost, 2),
        "integration_amortized": round(integration_amortized, 2),
        "total_monthly": round(tco, 2),
        "total_annual": round(tco * 12, 2),
        "breakdown_pct": {
            "infra": round((infra_cost / tco) * 100, 1) if tco > 0 else 0,
            "ops": round((ops_cost / tco) * 100, 1) if tco > 0 else 0,
            "risk": round((risk_cost / tco) * 100, 1) if tco > 0 else 0,
            "integration": round((integration_amortized / tco) * 100, 1) if tco > 0 else 0,
        }
    }

Ejemplo: managed vs self-hosted

# Escenario: 200K vectores, 1536 dimensiones, equipo mid-level

managed_tco = calculate_tco(
    infra_cost=200,          # Tier managed para 200K vectors
    ops_hours=2,             # Solo monitoring de integración
    engineer_rate=70,        # Mid-level engineer
    incident_probability=0.05, # 5% chance de incidente/mes
    incident_cost=500,       # Costo estimado por incidente
    integration_hours=16,    # 2 días de integración inicial
    integration_rate=70
)

self_hosted_tco = calculate_tco(
    infra_cost=80,           # VM t3.large + storage
    ops_hours=12,            # Monitoring, backups, updates, debugging
    engineer_rate=70,
    incident_probability=0.15, # 15% — más incidentes sin managed
    incident_cost=800,       # Más costoso: debugging + recovery
    integration_hours=40,    # 5 días de setup + config
    integration_rate=70
)

print("=== TCO Mensual: Managed vs Self-hosted ===\n")
for name, tco in [("Managed", managed_tco), ("Self-hosted", self_hosted_tco)]:
    print(f"{name}:")
    print(f"  Infraestructura:  ${tco['infra']:>8.2f}  ({tco['breakdown_pct']['infra']}%)")
    print(f"  Operación:        ${tco['ops']:>8.2f}  ({tco['breakdown_pct']['ops']}%)")
    print(f"  Riesgo:           ${tco['risk']:>8.2f}  ({tco['breakdown_pct']['risk']}%)")
    print(f"  Integración (12m):${tco['integration_amortized']:>8.2f}  ({tco['breakdown_pct']['integration']}%)")
    print(f"  ---")
    print(f"  TOTAL MENSUAL:    ${tco['total_monthly']:>8.2f}")
    print(f"  TOTAL ANUAL:      ${tco['total_annual']:>8.2f}")
    print()

saving = self_hosted_tco["total_monthly"] - managed_tco["total_monthly"]
print(f"Diferencia mensual: ${saving:.2f}/mes a favor de {'managed' if saving > 0 else 'self-hosted'}")
print(f"Diferencia anual: ${saving * 12:.2f}/año")

Salida típica:

Managed:
  Infraestructura:  $  200.00  (58.0%)
  Operación:        $  140.00  (40.6%)
  Riesgo:           $   25.00  (7.3%)
  Integración (12m):$   93.33  (27.1%)
  ---
  TOTAL MENSUAL:    $  458.33
  TOTAL ANUAL:      $5,500.00

Self-hosted:
  Infraestructura:  $   80.00  (7.3%)
  Operación:        $  840.00  (76.9%)
  Riesgo:           $  120.00  (11.0%)
  Integración (12m):$  233.33  (21.3%)
  ---
  TOTAL MENSUAL:    $1,273.33
  TOTAL ANUAL:      $15,280.00

Conclusión: El managed "caro" de $200/mes es $815/mes más barato que el self-hosted "barato" de $80/mes cuando cuentas las horas de tu equipo.


Los 5 costos ocultos que nadie menciona

hidden_costs = {
    "1_egress": {
        "name": "Network egress (transferencia de datos)",
        "description": "Cada query envía datos del servidor al cliente. "
                       "Si tu vector DB está en una región diferente a tu app, pagas egress.",
        "typical_cost": "$0.05-0.12 por GB",
        "trap": "5K queries/día × 10KB respuesta = 50MB/día = 1.5GB/mes. "
                "Parece poco, pero si haces re-embedding masivo, puedes transferir 100GB+.",
        "mitigation": "Coloca vector DB en la misma región/VPC que tu app. "
                      "Evita cross-region queries."
    },
    "2_embedding_generation": {
        "name": "Costo de generación de embeddings",
        "description": "No es costo de la vector DB, pero es inseparable. "
                       "Cada documento nuevo necesita embedding antes de ingestar.",
        "typical_cost": "OpenAI text-embedding-3-small: $0.02/1M tokens",
        "trap": "Re-embedding por cambio de modelo: 200K docs × 1000 tokens = "
                "200M tokens = $4. Parece barato, pero ×4 veces al año = $16 solo en re-embed.",
        "mitigation": "Almacena embeddings para no re-generar. "
                      "Elige modelo de embedding estable (no cambies cada trimestre)."
    },
    "3_overprovisioning": {
        "name": "Sobre-provisionamiento por miedo a latencia",
        "description": "Contratas un tier más alto 'por si acaso' y pagas 2-3x lo necesario.",
        "typical_cost": "Diferencia entre tiers: $50-300/mes",
        "trap": "Tier para 1M vectores cuando tienes 100K → pagas $300/mes por capacidad que usas al 10%.",
        "mitigation": "Empieza en el tier más bajo. Escala cuando p95 > umbral, no preventivamente."
    },
    "4_migration_cost": {
        "name": "Costo de migración futura",
        "description": "Si eliges mal, migrar después cuesta mucho más que la diferencia de pricing.",
        "typical_cost": "$5,000-15,000 (ingeniería + downtime + re-embedding)",
        "trap": "Elegir por precio hoy → migrar en 12 meses → el 'ahorro' de $100/mes se pierde en 1 semana de migración.",
        "mitigation": "Invierte en la evaluación inicial (este módulo). "
                      "Una buena decisión ahora ahorra $10K+ después."
    },
    "5_opportunity_cost": {
        "name": "Costo de oportunidad",
        "description": "Horas que tu equipo dedica a ops de vector DB = horas que NO dedica a features de producto.",
        "typical_cost": "Difícil de cuantificar pero real",
        "trap": "12h/mes de ops × 12 meses = 144 horas = ~1 mes de trabajo de un developer. "
                "¿Qué feature habrías construido con ese mes?",
        "mitigation": "Incluye costo de oportunidad en el TCO. "
                      "Si tu equipo es pequeño, el impacto es mayor."
    }
}

print("=== 5 Costos Ocultos de Vector Databases ===\n")
for key, cost in hidden_costs.items():
    print(f"💰 {cost['name']}")
    print(f"   {cost['description']}")
    print(f"   Costo típico: {cost['typical_cost']}")
    print(f"   ⚠️ Trampa: {cost['trap']}")
    print(f"   ✅ Mitigación: {cost['mitigation']}")
    print()

Modelo de costos por escala

Los costos no escalan linealmente. Aquí tienes un modelo para tres escalas:

def cost_model_by_scale(provider: str, vectors: int, dimensions: int = 1536) -> dict:
    """
    Modelo simplificado de costo por escala.
    Basado en pricing público (sujeto a cambios).
    """
    models = {
        "pinecone": {
            "tiers": [
                {"max_vectors": 100_000, "monthly": 0, "name": "Free"},
                {"max_vectors": 1_000_000, "monthly": 70, "name": "Starter"},
                {"max_vectors": 5_000_000, "monthly": 200, "name": "Standard"},
                {"max_vectors": 50_000_000, "monthly": 800, "name": "Enterprise"},
            ],
            "ops_hours": 2,
        },
        "qdrant_cloud": {
            "tiers": [
                {"max_vectors": 100_000, "monthly": 0, "name": "Free"},
                {"max_vectors": 500_000, "monthly": 50, "name": "Starter"},
                {"max_vectors": 2_000_000, "monthly": 150, "name": "Business"},
                {"max_vectors": 20_000_000, "monthly": 500, "name": "Enterprise"},
            ],
            "ops_hours": 3,
        },
        "weaviate_cloud": {
            "tiers": [
                {"max_vectors": 50_000, "monthly": 0, "name": "Sandbox"},
                {"max_vectors": 500_000, "monthly": 75, "name": "Starter"},
                {"max_vectors": 5_000_000, "monthly": 250, "name": "Business"},
                {"max_vectors": 50_000_000, "monthly": 900, "name": "Enterprise"},
            ],
            "ops_hours": 3,
        },
        "chromadb_self": {
            "tiers": [
                {"max_vectors": 100_000, "monthly": 20, "name": "t3.small"},
                {"max_vectors": 500_000, "monthly": 60, "name": "t3.medium"},
                {"max_vectors": 2_000_000, "monthly": 150, "name": "m5.large"},
                {"max_vectors": 10_000_000, "monthly": 400, "name": "m5.xlarge"},
            ],
            "ops_hours": 12,
        },
        "milvus_zilliz": {
            "tiers": [
                {"max_vectors": 100_000, "monthly": 0, "name": "Free"},
                {"max_vectors": 1_000_000, "monthly": 65, "name": "Starter"},
                {"max_vectors": 10_000_000, "monthly": 200, "name": "Standard"},
                {"max_vectors": 100_000_000, "monthly": 600, "name": "Enterprise"},
            ],
            "ops_hours": 5,
        }
    }

    if provider not in models:
        return {"error": f"Proveedor '{provider}' no encontrado"}

    model = models[provider]
    tier = None
    for t in model["tiers"]:
        if vectors <= t["max_vectors"]:
            tier = t
            break

    if tier is None:
        tier = model["tiers"][-1]
        tier["monthly"] *= 1.5  # estimación para escala superior

    return {
        "provider": provider,
        "vectors": vectors,
        "tier": tier["name"],
        "service_cost": tier["monthly"],
        "ops_hours": model["ops_hours"],
    }


# Comparación a 3 escalas
scales = [10_000, 100_000, 1_000_000]
providers = ["pinecone", "qdrant_cloud", "weaviate_cloud", "chromadb_self", "milvus_zilliz"]
engineer_rate = 70

print("=== Costo por escala (servicio + ops, USD/mes) ===\n")
header = f"{'Proveedor':<20}"
for scale in scales:
    header += f" {scale//1000}K vectors   "
print(header)
print("-" * 80)

for provider in providers:
    row = f"{provider:<20}"
    for scale in scales:
        model = cost_model_by_scale(provider, scale)
        total = model["service_cost"] + (model["ops_hours"] * engineer_rate)
        row += f" ${total:>7,.0f} ({model['tier']:<10})"
    print(row)

El "Cliff Edge": cuando el free tier se acaba

def find_cliff_edges(provider: str, growth_rate_monthly: float,
                     starting_vectors: int) -> list[dict]:
    """
    Identifica los puntos donde el costo salta al siguiente tier.
    """
    tiers_map = {
        "pinecone": [
            (100_000, 0, "Free"), (1_000_000, 70, "Starter"),
            (5_000_000, 200, "Standard"), (50_000_000, 800, "Enterprise")
        ],
        "qdrant_cloud": [
            (100_000, 0, "Free"), (500_000, 50, "Starter"),
            (2_000_000, 150, "Business"), (20_000_000, 500, "Enterprise")
        ],
        "weaviate_cloud": [
            (50_000, 0, "Sandbox"), (500_000, 75, "Starter"),
            (5_000_000, 250, "Business"), (50_000_000, 900, "Enterprise")
        ],
        "chromadb_self": [
            (100_000, 20, "t3.small"), (500_000, 60, "t3.medium"),
            (2_000_000, 150, "m5.large"), (10_000_000, 400, "m5.xlarge")
        ],
    }

    if provider not in tiers_map:
        return []

    tiers = tiers_map[provider]
    cliffs = []
    current_tier_idx = 0
    vectors = starting_vectors

    # Encontrar tier actual
    for i, (max_v, _, _) in enumerate(tiers):
        if vectors <= max_v:
            current_tier_idx = i
            break

    import math

    for next_idx in range(current_tier_idx + 1, len(tiers)):
        next_max = tiers[next_idx - 1][0] if next_idx > 0 else 0
        prev_cost = tiers[next_idx - 1][1]
        next_cost = tiers[next_idx][1]

        # ¿En cuántos meses llegas a ese tier?
        if growth_rate_monthly > 0 and vectors < next_max:
            months = math.log(next_max / vectors) / math.log(1 + growth_rate_monthly)
        else:
            months = float('inf')

        cost_jump = next_cost - prev_cost
        jump_pct = (cost_jump / prev_cost * 100) if prev_cost > 0 else float('inf')

        cliffs.append({
            "from_tier": tiers[next_idx - 1][2],
            "to_tier": tiers[next_idx][2],
            "at_vectors": next_max,
            "months_to_reach": round(months, 1) if months != float('inf') else "∞",
            "cost_jump": cost_jump,
            "cost_jump_pct": round(jump_pct, 0) if jump_pct != float('inf') else "∞",
            "new_monthly_cost": next_cost
        })

    return cliffs


# Ejemplo: startup con 50K vectores, crecimiento 15%/mes
print("=== Cliff Edges: ¿cuándo salta el costo? ===\n")
for provider in ["pinecone", "qdrant_cloud", "weaviate_cloud"]:
    cliffs = find_cliff_edges(provider, 0.15, 50_000)
    print(f"{provider}:")
    for cliff in cliffs:
        print(f"  📈 {cliff['from_tier']}{cliff['to_tier']}: "
              f"+${cliff['cost_jump']}/mes ({cliff['cost_jump_pct']}% más) "
              f"en ~{cliff['months_to_reach']} meses "
              f"(a {cliff['at_vectors']:,} vectores)")
    print()

Break-even de migración

¿Cuándo vale la pena migrar? Cuando el ahorro acumulado supera el costo de migración:

def migration_break_even(
    current_monthly_tco: float,
    new_monthly_tco: float,
    migration_cost: float,
    risk_buffer_pct: float = 0.2
) -> dict:
    """
    Calcula el break-even de una migración.

    Args:
        current_monthly_tco: TCO mensual actual
        new_monthly_tco: TCO mensual esperado después de migrar
        migration_cost: Costo total de la migración (una vez)
        risk_buffer_pct: Buffer de riesgo (20% por defecto)
    """
    monthly_savings = current_monthly_tco - new_monthly_tco

    if monthly_savings <= 0:
        return {
            "viable": False,
            "reason": "El nuevo proveedor es más caro o igual",
            "monthly_savings": round(monthly_savings, 2)
        }

    migration_with_buffer = migration_cost * (1 + risk_buffer_pct)
    payback_months = migration_with_buffer / monthly_savings
    year_1_net = (monthly_savings * 12) - migration_with_buffer
    year_2_net = (monthly_savings * 24) - migration_with_buffer

    return {
        "viable": True,
        "monthly_savings": round(monthly_savings, 2),
        "migration_cost": round(migration_cost, 2),
        "migration_with_buffer": round(migration_with_buffer, 2),
        "payback_months": round(payback_months, 1),
        "year_1_net_savings": round(year_1_net, 2),
        "year_2_net_savings": round(year_2_net, 2),
        "recommendation": (
            "✅ Migra ahora" if payback_months < 6 else
            "🟡 Planifica migración" if payback_months < 9 else
            "🟠 Evalúa trimestralmente" if payback_months < 12 else
            "🔴 No migres aún"
        )
    }


# Escenario 1: ChromaDB self-hosted → Qdrant Cloud
scenario_1 = migration_break_even(
    current_monthly_tco=1200,    # ChromaDB self-hosted con ops altas
    new_monthly_tco=350,         # Qdrant Cloud managed
    migration_cost=8000,         # 2 semanas de ingeniería + re-embedding
    risk_buffer_pct=0.25
)

# Escenario 2: Pinecone → Qdrant Cloud (ahorro menor)
scenario_2 = migration_break_even(
    current_monthly_tco=500,
    new_monthly_tco=350,
    migration_cost=6000,
    risk_buffer_pct=0.20
)

# Escenario 3: Managed → Self-hosted (¿vale la pena?)
scenario_3 = migration_break_even(
    current_monthly_tco=350,     # Managed actual
    new_monthly_tco=1200,        # Self-hosted con ops
    migration_cost=5000
)

for name, result in [
    ("ChromaDB Self → Qdrant Cloud", scenario_1),
    ("Pinecone → Qdrant Cloud", scenario_2),
    ("Managed → Self-hosted", scenario_3)
]:
    print(f"\n=== {name} ===")
    if result["viable"]:
        print(f"  Ahorro mensual: ${result['monthly_savings']}/mes")
        print(f"  Costo migración (+buffer): ${result['migration_with_buffer']}")
        print(f"  Payback: {result['payback_months']} meses")
        print(f"  Ahorro neto año 1: ${result['year_1_net_savings']}")
        print(f"  Ahorro neto año 2: ${result['year_2_net_savings']}")
        print(f"  → {result['recommendation']}")
    else:
        print(f"  ❌ {result['reason']}")
        print(f"  Diferencia mensual: ${result['monthly_savings']}/mes")

Proyección de costos a 12 meses

def project_costs_12m(
    starting_vectors: int,
    growth_rate_monthly: float,
    provider: str,
    engineer_rate: float = 70
) -> list[dict]:
    """Proyecta costos mensuales a 12 meses."""
    projections = []

    for month in range(1, 13):
        vectors = int(starting_vectors * (1 + growth_rate_monthly) ** month)
        model = cost_model_by_scale(provider, vectors)
        ops_cost = model["ops_hours"] * engineer_rate
        total = model["service_cost"] + ops_cost

        projections.append({
            "month": month,
            "vectors": vectors,
            "tier": model["tier"],
            "service_cost": model["service_cost"],
            "ops_cost": ops_cost,
            "total": total
        })

    return projections


# Comparar proyecciones de 3 proveedores
print("=== Proyección 12 meses (50K vectores, 15% crecimiento/mes) ===\n")

for provider in ["pinecone", "qdrant_cloud", "chromadb_self"]:
    proj = project_costs_12m(50_000, 0.15, provider)
    total_12m = sum(p["total"] for p in proj)

    print(f"{provider}:")
    for p in [proj[0], proj[2], proj[5], proj[8], proj[11]]:
        print(f"  Mes {p['month']:>2}: {p['vectors']:>10,} vectors | "
              f"Tier: {p['tier']:<10} | ${p['total']:>7,.0f}/mes")
    print(f"  TOTAL 12 meses: ${total_12m:,.0f}")
    print()

Modelo de escenarios: conservador, esperado, agresivo

No proyectes un solo número. Modela tres escenarios:

def three_scenario_projection(
    starting_vectors: int,
    provider: str,
    engineer_rate: float = 70
) -> dict:
    """
    Proyecta TCO a 12 meses en tres escenarios de crecimiento.
    """
    scenarios = {
        "conservative": {"growth": 0.08, "label": "Crecimiento 8%/mes"},
        "expected": {"growth": 0.15, "label": "Crecimiento 15%/mes"},
        "aggressive": {"growth": 0.25, "label": "Crecimiento 25%/mes"},
    }

    results = {}
    for scenario_name, config in scenarios.items():
        proj = project_costs_12m(starting_vectors, config["growth"], provider, engineer_rate)
        total_12m = sum(p["total"] for p in proj)
        final_vectors = proj[-1]["vectors"]
        final_tier = proj[-1]["tier"]
        final_monthly = proj[-1]["total"]

        results[scenario_name] = {
            "label": config["label"],
            "growth_rate": config["growth"],
            "total_12m": round(total_12m, 0),
            "final_vectors": final_vectors,
            "final_tier": final_tier,
            "final_monthly": round(final_monthly, 0),
            "avg_monthly": round(total_12m / 12, 0)
        }

    return results


print("=== 3 Escenarios: Qdrant Cloud (inicio: 50K vectores) ===\n")
scenarios = three_scenario_projection(50_000, "qdrant_cloud")

print(f"{'Escenario':<20} {'Vectores 12m':<15} {'Tier final':<12} "
      f"{'Último mes':<12} {'Total 12m':<12} {'Promedio/mes'}")
print("-" * 85)

for name, data in scenarios.items():
    print(f"{data['label']:<20} {data['final_vectors']:>12,} {data['final_tier']:<12} "
          f"${data['final_monthly']:>9,.0f} ${data['total_12m']:>9,.0f} ${data['avg_monthly']:>9,.0f}")

spread = scenarios["aggressive"]["total_12m"] - scenarios["conservative"]["total_12m"]
print(f"\nSpread conservador-agresivo: ${spread:,.0f}/año")
print("El spread te dice cuánta incertidumbre financiera tienes. Si es > 50% del presupuesto, planifica escenarios.")

Cuándo el "gratis" se vuelve caro

def free_tier_analysis(starting_vectors: int, growth_rate: float) -> dict:
    """Analiza cuándo cada proveedor cobra por primera vez."""
    import math

    free_limits = {
        "Pinecone": 100_000,
        "Qdrant Cloud": 100_000,
        "Weaviate Cloud": 50_000,
        "Milvus (Zilliz)": 100_000,
        "ChromaDB": float('inf'),  # Open source, siempre "gratis" (pero pagas VM)
    }

    first_paid_cost = {
        "Pinecone": 70,
        "Qdrant Cloud": 50,
        "Weaviate Cloud": 75,
        "Milvus (Zilliz)": 65,
        "ChromaDB": 20,  # Costo VM mínimo
    }

    results = {}
    for provider, limit in free_limits.items():
        if starting_vectors >= limit:
            months_free = 0
        elif limit == float('inf'):
            months_free = float('inf')
        else:
            months_free = math.log(limit / starting_vectors) / math.log(1 + growth_rate)

        results[provider] = {
            "free_limit": limit if limit != float('inf') else "∞",
            "months_free": round(months_free, 1) if months_free != float('inf') else "∞",
            "first_paid_cost": first_paid_cost[provider],
            "vectors_at_transition": (
                int(starting_vectors * (1 + growth_rate) ** months_free)
                if months_free != float('inf') and months_free > 0
                else starting_vectors
            )
        }

    return results


print("=== ¿Cuándo se acaba el free tier? (inicio: 30K vectors, 20%/mes) ===\n")
free_analysis = free_tier_analysis(30_000, 0.20)

print(f"{'Proveedor':<20} {'Límite free':<15} {'Meses gratis':<15} "
      f"{'Primer pago':<15}")
print("-" * 65)
for provider, data in free_analysis.items():
    limit = f"{data['free_limit']:,}" if isinstance(data["free_limit"], int) else data["free_limit"]
    months = data["months_free"]
    cost = f"${data['first_paid_cost']}/mes"
    print(f"{provider:<20} {limit:<15} {months:<15} {cost}")

print("\n⚠️ Planifica tu presupuesto ANTES de que se acabe el free tier.")
print("   El peor momento para negociar pricing es cuando ya estás en producción.")

Calculadora de ROI de decisión correcta

def roi_good_decision(
    bad_choice_monthly: float,
    good_choice_monthly: float,
    evaluation_hours: float,
    engineer_rate: float,
    months: int = 12
) -> dict:
    """
    Calcula el ROI de invertir tiempo en evaluar correctamente.
    
    Args:
        bad_choice_monthly: TCO mensual de elegir mal
        good_choice_monthly: TCO mensual de elegir bien
        evaluation_hours: Horas invertidas en evaluación
        engineer_rate: Tarifa del ingeniero
        months: Horizonte temporal
    """
    evaluation_cost = evaluation_hours * engineer_rate
    monthly_savings = bad_choice_monthly - good_choice_monthly
    total_savings = monthly_savings * months
    net_roi = total_savings - evaluation_cost
    roi_pct = (net_roi / evaluation_cost) * 100 if evaluation_cost > 0 else 0

    return {
        "evaluation_cost": round(evaluation_cost, 2),
        "monthly_savings": round(monthly_savings, 2),
        "total_savings_period": round(total_savings, 2),
        "net_roi": round(net_roi, 2),
        "roi_percentage": round(roi_pct, 0),
        "payback_hours": round(evaluation_cost / monthly_savings, 1) if monthly_savings > 0 else float('inf')
    }


roi = roi_good_decision(
    bad_choice_monthly=1200,   # ChromaDB self-hosted con ops altas
    good_choice_monthly=400,   # Managed bien elegido
    evaluation_hours=20,       # 2.5 días de evaluación (este módulo)
    engineer_rate=70,
    months=12
)

print("=== ROI de evaluar correctamente ===\n")
print(f"Costo de evaluación: ${roi['evaluation_cost']} ({20}h × $70/hr)")
print(f"Ahorro mensual: ${roi['monthly_savings']}/mes")
print(f"Ahorro total (12 meses): ${roi['total_savings_period']}")
print(f"ROI neto: ${roi['net_roi']}")
print(f"ROI %: {roi['roi_percentage']}%")
print(f"\nLas {20} horas de evaluación se pagan solas en "
      f"{roi['payback_hours']} meses de ahorro.")

🔧 Troubleshooting

Problema 1: "Managed parece más caro siempre al comparar sticker price"

Síntoma: Comparas $200/mes de managed vs $60/mes de VM y eliges self-hosted.

Solución: Nunca compares sticker price. Calcula TCO completo: $60 VM + 12h × $70/hr ops = $900/mes. El managed de $200 con 2h × $70 ops = $340/mes. Managed es 62% más barato en TCO.

Problema 2: "No sé cuánto vale una hora de mi equipo"

Síntoma: No tienes tarifa interna para calcular costo de ops.

Solución: Usa esta regla: salario anual / 2000 = tarifa hora aproximada. Para $120K/año → $60/hr. Si no conoces salarios, usa $50/hr como piso y $100/hr como techo. Corre el modelo con ambos — si la conclusión no cambia, la tarifa exacta no importa.

Problema 3: "El free tier es suficiente para siempre en mi PoC"

Síntoma: Crees que nunca pasarás de 100K vectores.

Solución: Proyecta tu crecimiento. A 15% mensual, 50K vectores llegan a 100K en 5 meses. Planifica el presupuesto para el primer tier pago ANTES de llegar al límite. El peor momento para negociar es cuando ya estás en producción con datos reales.

Problema 4: "Quiero migrar por performance pero no sé si el ROI justifica"

Síntoma: Tu latencia subió pero no has cuantificado el impacto en negocio.

Solución: Vincula latencia con métricas de negocio: ¿cuántos usuarios abandonan por lentitud? Si 100ms extra = 1% de abandono y tienes 10K queries/día, calcula el costo de esos abandonos vs el costo de migración.

Problema 5: "Los precios cambian cada trimestre"

Síntoma: Tu modelo de costos se desactualiza rápidamente.

Solución: No intentes predecir precios futuros. Modela con precios actuales y agrega un buffer de 15-20% para inflación. Reevalúa trimestralmente con precios reales. Si un proveedor baja precios significativamente, eso activa tu trigger de reevaluación.


🏋️ Ejercicios

Ejercicio 1: Calcula tu TCO real

Calcula el TCO mensual para tu proyecto actual (o el proyecto DocBot de la cápsula anterior) con 3 proveedores diferentes. Incluye las 4 dimensiones.

Solución
# DocBot: 200K vectores, equipo de 5, sin DevOps, $400/mes budget

providers_tco = {}
for provider_name, config in [
    ("Pinecone", {"infra": 200, "ops": 2, "incident_prob": 0.05, "incident_cost": 500, "integration": 16}),
    ("Qdrant Cloud", {"infra": 150, "ops": 3, "incident_prob": 0.08, "incident_cost": 600, "integration": 20}),
    ("ChromaDB Self", {"infra": 60, "ops": 12, "incident_prob": 0.15, "incident_cost": 800, "integration": 40}),
]:
    tco = calculate_tco(
        infra_cost=config["infra"],
        ops_hours=config["ops"],
        engineer_rate=70,
        incident_probability=config["incident_prob"],
        incident_cost=config["incident_cost"],
        integration_hours=config["integration"],
        integration_rate=70
    )
    providers_tco[provider_name] = tco
    print(f"{provider_name}: ${tco['total_monthly']}/mes (${tco['total_annual']}/año)")
    print(f"  Infra: ${tco['infra']} ({tco['breakdown_pct']['infra']}%)")
    print(f"  Ops:   ${tco['ops']} ({tco['breakdown_pct']['ops']}%)")
    print(f"  Risk:  ${tco['risk']} ({tco['breakdown_pct']['risk']}%)")
    print()

Ejercicio 2: Identifica cliff edges para tu proyecto

Con 80K vectores actuales y 20% de crecimiento mensual, ¿en cuántos meses llegas al primer cliff edge de cada proveedor? ¿Cuánto salta el costo?

Solución
print("Cliff edges desde 80K vectores, 20%/mes de crecimiento:\n")

for provider in ["pinecone", "qdrant_cloud", "weaviate_cloud"]:
    cliffs = find_cliff_edges(provider, 0.20, 80_000)
    print(f"{provider}:")
    for cliff in cliffs:
        print(f"  {cliff['from_tier']}{cliff['to_tier']}")
        print(f"    En ~{cliff['months_to_reach']} meses ({cliff['at_vectors']:,} vectors)")
        print(f"    Salto: +${cliff['cost_jump']}/mes")
    print()

# Ejemplo de resultado:
# Pinecone: Free → Starter en ~1.2 meses (+$70/mes)
# Weaviate: Sandbox → Starter muy pronto (50K límite, ya casi estás)
# Plan: tener presupuesto aprobado ANTES del cliff

Ejercicio 3: Break-even de migración

Tu equipo usa ChromaDB self-hosted (TCO: $1,100/mes). Estás considerando migrar a Qdrant Cloud (TCO estimado: $350/mes). El costo de migración es $7,500. ¿Vale la pena? ¿Cuál es el payback?

Solución
result = migration_break_even(
    current_monthly_tco=1100,
    new_monthly_tco=350,
    migration_cost=7500,
    risk_buffer_pct=0.25  # 25% buffer por imprevistos
)

print("=== Break-even: ChromaDB Self → Qdrant Cloud ===\n")
print(f"Ahorro mensual: ${result['monthly_savings']}/mes")
print(f"Migración con buffer (25%): ${result['migration_with_buffer']}")
print(f"Payback: {result['payback_months']} meses")
print(f"Ahorro neto año 1: ${result['year_1_net_savings']}")
print(f"Ahorro neto año 2: ${result['year_2_net_savings']}")
print(f"Recomendación: {result['recommendation']}")

# Resultado esperado:
# Ahorro mensual: $750/mes
# Payback: ~12.5 meses con buffer
# Año 1 net: ~-$375 (aún no recuperas)
# Año 2 net: ~$8,625
# Recomendación: 🟠 Evalúa trimestralmente
# El payback de 12.5 meses es largo. Válido solo si planeas
# quedarte 2+ años con el nuevo proveedor.

Ejercicio 4: Proyección de 3 escenarios

Proyecta el costo de Pinecone a 12 meses en tres escenarios de crecimiento (8%, 15%, 25% mensual) partiendo de 50K vectores. ¿En qué escenario superas tu budget de $400/mes?

Solución
scenarios = three_scenario_projection(50_000, "pinecone", engineer_rate=70)

print("=== Proyección Pinecone: 3 escenarios ===\n")
budget = 400

for name, data in scenarios.items():
    over_budget = data["final_monthly"] > budget
    status = "🔴 OVER BUDGET" if over_budget else "✅ Dentro de budget"
    print(f"{data['label']}:")
    print(f"  Vectores al mes 12: {data['final_vectors']:,}")
    print(f"  Tier final: {data['final_tier']}")
    print(f"  Costo mes 12: ${data['final_monthly']}/mes {status}")
    print(f"  Total 12 meses: ${data['total_12m']:,.0f}")
    print(f"  Promedio mensual: ${data['avg_monthly']}/mes")
    print()

# Identifica en qué mes de cada escenario superas $400/mes
for name, data in scenarios.items():
    proj = project_costs_12m(50_000, data["growth_rate"], "pinecone")
    for p in proj:
        if p["total"] > budget:
            print(f"{data['label']}: superas ${budget}/mes en el mes {p['month']}")
            break
    else:
        print(f"{data['label']}: no superas ${budget}/mes en 12 meses")

Ejercicio 5: ROI de este módulo

Calcula el ROI de haber invertido 8 horas en este módulo de evaluación, asumiendo que te ahorró elegir mal (ChromaDB self-hosted con $1,200/mes de TCO vs managed a $400/mes).

Solución
module_roi = roi_good_decision(
    bad_choice_monthly=1200,
    good_choice_monthly=400,
    evaluation_hours=8,        # Este módulo completo
    engineer_rate=70,
    months=12
)

print("=== ROI de invertir 8 horas en evaluación ===\n")
print(f"Costo de evaluación: ${module_roi['evaluation_cost']}")
print(f"Ahorro mensual: ${module_roi['monthly_savings']}")
print(f"Ahorro 12 meses: ${module_roi['total_savings_period']}")
print(f"ROI neto: ${module_roi['net_roi']}")
print(f"ROI %: {module_roi['roi_percentage']}%")
print(f"Payback: {module_roi['payback_hours']} meses")
print()
print(f"Cada hora invertida en evaluación te ahorra "
      f"${module_roi['net_roi'] / 8:.0f} en el primer año.")

# Resultado: ~$9,040 de ROI neto por 8 horas de trabajo.
# Eso es ~$1,130/hora de retorno.
# Conclusión: evaluar correctamente es una de las mejores
# inversiones de tiempo que puedes hacer.

🔗 Conexión con proyecto: Decision Questionnaire

En tu Decision Questionnaire, el análisis de costos es la sección que convierte tu recomendación técnica en una recomendación financiera. Tu cuestionario debe:

  1. Preguntar el presupuesto y la composición del equipo (para calcular tarifa hora)
  2. Calcular el TCO de las 2-3 opciones finalistas (no solo el sticker price)
  3. Proyectar a 12 meses con los tres escenarios de crecimiento
  4. Identificar cliff edges y alertar al usuario antes de que el costo salte
  5. Generar una sección de ROI que justifique la decisión ante stakeholders no-técnicos

Reutiliza calculate_tco(), cost_model_by_scale(), find_cliff_edges() y migration_break_even() directamente.


Resumen

  • El sticker price es el 30-40% del costo real. El TCO incluye ops, riesgo, integración y oportunidad.
  • El costo operativo domina en la mayoría de casos. 12h/mes × $70/hr = $840/mes de "invisible".
  • Los 5 costos ocultos: egress, embeddings, sobre-provisionamiento, migración futura, oportunidad.
  • Modela en 3 escalas (10K, 100K, 1M) para entender la curva de costos.
  • Los cliff edges son reales: el free tier de hoy es $70-200/mes en 3-6 meses.
  • Break-even de migración: solo migra si el payback es < 9 meses.
  • Proyecta en 3 escenarios (conservador, esperado, agresivo) para manejar incertidumbre.
  • El ROI de evaluar correctamente es masivo: 8 horas de análisis pueden ahorrar $9K+/año.

Recursos adicionales

  1. AWS Pricing Calculator — Estimador de costos de infraestructura AWS
  2. GCP Pricing Calculator — Estimador de Google Cloud
  3. Pinecone Pricing — Pricing y calculadora de Pinecone
  4. Qdrant Cloud Pricing — Tiers y precios de Qdrant managed
  5. Weaviate Pricing — Pricing de Weaviate Cloud Services
  6. OpenAI Embeddings Pricing — Costos de generación de embeddings
  7. FinOps Foundation — Framework para gestión de costos cloud
  8. Zilliz Cloud Pricing — Pricing de Milvus managed (Zilliz)

Tiempo estimado: 25-35 minutos Siguiente: 06-recomendacion-por-escenarios.md