El agente #12: cómo bajé mi factura de Claude de $50 a $5/mes vigilando a mis otros 11 agentes

Case study completo: construí un meta-agente cuyo único trabajo es leer las métricas de los demás y decirme qué optimizar. Hoy lo open-sourceo. Con código, números reales, y lo que aprendí.


Llevo 3 meses corriendo 11 agentes AI en producción. Sales, finance, dev, ops, creative, security, optimizer, email, marketing, coordinator, general. Cada uno con su rol, sus tools, su modelo. El sistema atiende mi Discord, mi WhatsApp, gestiona leads, publica en LinkedIn, escribe drafts de blog (incluyendo partes de éste).

Al segundo mes mi factura de Anthropic dijo: $50.13 USD.

No es una factura asesina, pero era 10× lo que esperaba para mi escala de uso. Y peor: no tenía idea de qué la estaba subiendo.

Esto es la historia de cómo lo resolví — y el código completo abierto al final.

El intento equivocado

Mi primer reflejo fue el de cualquiera: ir a logs y dashboards.

Probé:

Lo que necesitaba: una pregunta concreta con una respuesta concreta. “¿Cuál es el agente que más gasta y por qué?”. Eso no es un dashboard. Es un reporte ejecutivo.

La idea: un agente que lea a los otros

A las 3 AM se me ocurrió: ya tengo 11 agentes. ¿Por qué no construyo un agente número 12 que lea las métricas de los demás y me diga qué arreglar?

La instrucción para él sería literalmente:

“Eres el optimizador. Lee este resumen de uso. Devuélveme 3-5 cosas concretas que debería hacer para bajar el costo, priorizadas por ahorro estimado.”

No tendría que reasonar mucho. Solo procesar un resumen estructurado y formatear bullets accionables.

Sonaba prometedor hasta que pensé en el costo: cada análisis sería miles de tokens de input (las métricas), más output. ¿Iba a gastar $0.50 por análisis para ahorrarme $5?

El insight que cambió todo

Cuando empecé a estructurar el código, me di cuenta de algo obvio en retrospectiva:

El LLM no debe hacer matemáticas.

¿Por qué le voy a pasar 50,000 filas de token_usage a Claude para que calcule sumas y promedios? Postgres hace eso en milisegundos y gratis. El LLM solo aporta valor en la parte lingüística: priorizar, comparar, escribir una recomendación clara.

La separación correcta:

┌─ Postgres (gratis) ─────────────┐
│ Aggregations, ranking, joins,   │
│ ratios, percentiles, GROUP BY,  │
│ HAVING clauses, time windows.   │
└─────────────┬───────────────────┘
              │ JSON estructurado pequeño

┌─ LLM (caro) ────────────────────┐
│ Razonamiento textual: ¿qué      │
│ priorizar? ¿cómo explicarlo?    │
│ ¿qué emoji? ¿cuántos $$?        │
└─────────────────────────────────┘

Con esta separación, el LLM ve 1-2K tokens de input (un JSON compacto), genera ~300 tokens de output. Eso son fracciones de centavo por análisis con Haiku.

Las 6 heurísticas

Lo que el “agente #12” lee es el resultado de 6 queries SQL. Cada una responde una pregunta concreta que un humano se haría revisando spend de LLMs:

1. ¿Quién gasta y en qué modelo?

SELECT agent_name, model, COUNT(*) AS calls,
       SUM(input_tokens) AS in_t,
       SUM(output_tokens) AS out_t,
       /* cost_usd computed inline based on model */
FROM token_usage
WHERE created_at >= NOW() - INTERVAL '7 days'
GROUP BY agent_name, model
ORDER BY cost_usd DESC

Pregunta de humano: “Si tengo que cortar un solo agente, ¿cuál?“

2. ¿Quién tiene cache hit rate bajo?

SELECT agent_name,
       ROUND(100.0 * SUM(cache_read_tokens) /
             NULLIF(SUM(input_tokens) + SUM(cache_read_tokens), 0), 1)
         AS hit_rate_pct
FROM token_usage
WHERE created_at >= NOW() - INTERVAL '7 days'
GROUP BY agent_name
HAVING COUNT(*) >= 3
ORDER BY hit_rate_pct ASC

Si tu system prompt cachea bien, deberías ver 60-95% hit rate. Menos de 30% es una alarma: algo invalidando el cache silenciosamente. Causas comunes: timestamp en el system prompt, UUID al inicio, contexto dinámico que cambia entre calls.

3. ¿Qué tools están declaradas pero nunca se usan?

Cada tool en tu agent suma tokens al system prompt cada vez que llamas. Si declaras 10 tools y solo usas 3, estás pagando overhead del 70% en cada call.

SELECT DISTINCT jsonb_array_elements(tools_used)->>'name' AS tool
FROM agent_runs
WHERE agent = $1
  AND created_at >= NOW() - INTERVAL '30 days'

Comparas con la lista declarada → te dice qué quitar.

4. ¿Algún agente entra en loop?

SELECT agent,
       AVG(jsonb_array_length(tools_used)) AS avg_tool_calls,
       MAX(jsonb_array_length(tools_used)) AS max_tool_calls
FROM agent_runs
WHERE created_at >= NOW() - INTERVAL '7 days'
  AND jsonb_array_length(tools_used) >= 4
GROUP BY agent

Si un agente promedia 4+ tool calls por run, algo está mal en el prompt. Está pidiendo demasiado en una sola vuelta, o no tiene la tool correcta y está buscando. Cada tool call extra cuesta input + cache + tokens del schema.

5. ¿Algún agente está usando Opus para tareas de Haiku?

Esta es la heurística estrella. La defino así:

Si un agente usa Sonnet u Opus, y su avg output < 200 tokens AND avg tool calls < 2, casi seguro corre fino en Haiku.

SELECT ar.agent, tu.model,
       AVG(jsonb_array_length(tools_used)) AS avg_tools,
       AVG(tu.output_tokens) AS avg_output
FROM agent_runs ar
JOIN token_usage tu ON tu.agent_name = ar.agent
                    AND DATE(tu.created_at) = DATE(ar.created_at)
WHERE ar.status = 'ok'
  AND (tu.model LIKE '%sonnet%' OR tu.model LIKE '%opus%')
  AND ar.created_at >= NOW() - INTERVAL '14 days'
GROUP BY ar.agent, tu.model
HAVING AVG(jsonb_array_length(tools_used)) < 2
   AND AVG(tu.output_tokens) < 200
   AND COUNT(*) >= 5

Esta sola heurística me ahorró $20/mes cuando descubrí que un agente clasificador estaba usando Sonnet porque originalmente tenía dudas de si Haiku iba a alcanzar. Era trivial — sí alcanzaba.

6. ¿El extractor de memoria vale lo que cuesta?

Mi sistema tiene un sub-agente que extrae memorias persistentes de cada conversación importante. Es la única operación “automática” del sistema (no requiere user input).

SELECT COUNT(*) AS extractor_calls,
       /* cost_usd */
FROM token_usage
WHERE agent_name = 'memory_extractor'
  AND created_at >= NOW() - INTERVAL '7 days'

Si gasta $5/semana y genera 3 memorias útiles, es $1.67 por memoria. Caro. Probablemente el threshold (cuándo decidir extraer) está mal.

El briefing que ve el LLM

Todas las heurísticas se empacan en un JSON compacto. Esto es lo que Claude Haiku recibe (ejemplo real del demo del repo):

{
  "period_days": 7,
  "total_cost_usd": 1.292,
  "total_calls": 74,
  "top_spenders": [
    {"agent": "researcher", "model": "claude-opus-4-7",
     "calls": 23, "cost_usd": 0.7580}
  ],
  "low_cache_agents": [
    {"agent": "scraper", "hit_rate_pct": 1.1, "calls": 12}
  ],
  "downgrade_candidates": [
    {"agent": "researcher", "model": "claude-opus-4-7",
     "avg_output": 119, "avg_tool_calls": 1.0}
  ],
  "unused_tools": {
    "planner": {"declared": 5, "used": 2,
                "unused": ["create_calendar_event","list_files","run_search"]}
  },
  "high_loop_agents": [
    {"agent": "translator", "avg_tool_calls": 6, "max_tool_calls": 8}
  ]
}

Compactísimo. ~1,300 tokens cuando se serializa.

Lo que el agente devuelve

Con eso como input, Haiku produce este output exacto (de nuevo, copy-paste del demo real):

📉 Analysis last 7 days ($1.2920 spent, 74 calls)

🎯 Prioritized recommendations:

  1. 🔴 58% savings — Downgrade researcher from Opus to Sonnet: avg output 119 tokens, 1 tool call/run doesn’t justify Opus cost ($0.76 → ~$0.32)
  2. 🟡 97% upside — Fix scraper cache: 1.1% hit rate with 54K uncached tokens suggests stale context; audit system prompt for dynamic invalidators
  3. 🟡 ~5% savings — Remove unused tools from planner (3 never called: calendar_event, list_files, run_search)
  4. 🟠 Refine translator prompt: 6 tool calls/run is high; consolidate or reduce branching logic

📊 Top spenders:

  • researcher (Opus): $0.7580, 23 calls
  • scraper (Sonnet): $0.2765, 12 calls

Esto es el output completo de una corrida. Haiku tomó el JSON estructurado y lo convirtió en bullets accionables con números.

Coste del análisis: $0.00211 USD.

Un análisis a la semana cuesta $0.11 al año. Comparado con los $40-60 USD/mes que el sistema entero te puede ayudar a recortar, es ridículamente barato.

Cómo lo aplico en producción

Tengo un job en cron de mi scheduler que corre cada lunes 9 AM:

# pseudocode del scheduler
@cron("0 9 * * MON")
async def weekly_optimizer():
    briefing = await get_optimizer_briefing(pool, days_back=7)
    result = await analyze(briefing)
    await discord.send_to_channel(
        channel_id=OPTIMIZER_CHANNEL,
        content=result["text"]
    )

Cada lunes a las 9 AM aparece el reporte en mi Discord. Lo leo con mi café, aplico 1-2 cambios, sigo con mi día.

Resultados después de 2 meses

Estos son mis números reales, no proyecciones.

MesSpend AnthropicNotas
Marzo 2026$50.13Baseline. Sin optimizer.
Abril 2026$18.40Bajé creative de Opus a Sonnet, arreglé cache de coordinator.
Mayo 2026$8.71Quité 14 tools no usadas, bajé dev a Haiku para clasificación, ajusté loop de marketing.
Junio 2026 (proyectado)$4.80Optimizaciones residuales + más uso de prompt caching.

Reducción: ~90%. Mismos 11 agentes. Mismo workload. Sin recortar features.

Lo que NO esperaba

Hay 3 cosas que me sorprendieron usando el optimizer:

1. La heurística de “tools sin uso” fue la más productiva al principio.

Mucha gente declara tools “por si acaso”. Es una mala práctica. Cada tool agrega ~80-150 tokens al system prompt. Tener 5 tools no usadas en un agente con cache rate 80% = pagas 600 tokens × 20% (lo no cacheado) en cada call. Por 500 calls/día son 60K tokens al día solo en overhead muerto.

2. El cache invalida por cosas estúpidas.

Mi agente sales tenía cache hit rate de 12%. El sistema prompt incluía:

Hoy es: {datetime.now().isoformat()}

Cada llamada cambiaba el timestamp → cache miss garantizado. Saqué esa línea (no la necesitaba para nada) → cache hit rate subió a 78%.

3. El “model routing” es el ahorro más grande, pero requiere disciplina.

Es muy tentador “darle Opus a todo, total es solo $0.075/1K”. Pero ese costo se suma rápido. Mi agente dev originalmente usaba Opus. Cuando lo bajé a Sonnet para code review estándar y solo subo a Opus en preguntas complejas explícitamente, el costo bajó 5×.

Quién debería correr esto

Mi heurística personal: si gastas más de $20 USD/mes en LLMs en producción, este optimizer paga su instalación en una semana.

Si gastas menos, probablemente no vale la pena instrumentarlo todavía. Concéntrate en hacer crecer el uso primero.

Si gastas más de $200/mes: probablemente ya tienes observability propia, pero el patrón de “SQL hace la matemática, LLM hace el ranking” es transferible a tu stack. Esto está bajo MIT.

Open source

Lo que ves arriba es el sistema que corro en producción, pero aislado para que cualquiera lo pueda correr en una máquina propia con docker compose up.

🔗 github.com/L-A-L-U/llm-cost-optimizer

Incluye:

License MIT. PRs bienvenidos — el roadmap incluye:

Conclusión

El instinto cuando construyes con LLMs es darle todo al LLM: la pregunta, el contexto, los datos, los cálculos. Es caro y frágil.

El patrón que aprendí construyendo este sistema:

El LLM razona. La base de datos calcula. El humano decide.

Cada uno hace lo que mejor sabe. Postgres no se cansa de hacer joins. Claude es brutal escribiendo bullets accionables. Yo elijo qué aplicar.

Un agente puede ser barato y útil al mismo tiempo. Y a veces el truco es darle menos trabajo, no más.


Si construyes algo con esto, mándame el link. Estoy en LinkedIn y oficina.lalu.dev. El próximo artículo va a ser sobre las otras dos cosas que más me ahorraron: prompt caching y model routing inteligente.


← todos los posts