⚠️ O Problema do Custo Não Monitorado
Este não é um cenário hipotético. Em 2024, múltiplas startups reportaram publicamente faturas de $5.000 a $50.000 em APIs de LLM causadas por bugs de loop em agentes sem controle de custo. Aqui está como isso acontece.
Anatomia de um loop de custo
💵 Budget por Execução
Cada job do orquestrador tem um budget máximo. O orquestrador monitora custo acumulado em tempo real e para quando o limite é atingido — entregando o resultado parcial em vez de falhar silenciosamente.
Implementação: BudgetMonitor
class BudgetMonitor:
"""Monitora custo em tempo real e aplica limites por execução."""
# Custo por 1k tokens por modelo (USD) — atualizar conforme preços vigentes
MODEL_COSTS = {
"claude-haiku-4-5": {"input": 0.00025, "output": 0.00125},
"claude-sonnet-4-6": {"input": 0.003, "output": 0.015},
"claude-opus-4-6": {"input": 0.015, "output": 0.075},
"gpt-4o-mini": {"input": 0.00015, "output": 0.0006},
"gpt-4o": {"input": 0.005, "output": 0.015},
}
def __init__(self,
hard_limit_usd: float,
soft_limit_usd: float = None,
session_id: str = None):
self.hard_limit = hard_limit_usd
self.soft_limit = soft_limit_usd or (hard_limit_usd * 0.8)
self.session_id = session_id
self.accumulated_cost = 0.0
self.cost_by_task: dict[str, float] = {}
self.cost_by_model: dict[str, float] = {}
self._soft_alert_sent = False
def record_llm_call(self, task_id: str, model: str,
input_tokens: int, output_tokens: int) -> dict:
"""
Registra custo de uma chamada LLM.
Retorna {"cost": X, "status": "ok"|"warning"|"exceeded"}
"""
costs = self.MODEL_COSTS.get(model, {"input": 0.005, "output": 0.015})
call_cost = (input_tokens / 1000 * costs["input"] +
output_tokens / 1000 * costs["output"])
self.accumulated_cost += call_cost
self.cost_by_task[task_id] = self.cost_by_task.get(task_id, 0) + call_cost
self.cost_by_model[model] = self.cost_by_model.get(model, 0) + call_cost
status = "ok"
if self.accumulated_cost >= self.hard_limit:
status = "exceeded"
elif self.accumulated_cost >= self.soft_limit and not self._soft_alert_sent:
status = "warning"
self._soft_alert_sent = True
return {
"call_cost": round(call_cost, 8),
"accumulated": round(self.accumulated_cost, 6),
"hard_limit": self.hard_limit,
"utilization_pct": round(self.accumulated_cost / self.hard_limit * 100, 1),
"status": status
}
def check_budget(self) -> bool:
"""Retorna True se ainda há budget disponível."""
return self.accumulated_cost < self.hard_limit
def raise_if_exceeded(self):
"""Lança exceção se budget foi excedido."""
if not self.check_budget():
raise BudgetExceededException(
f"Budget de ${self.hard_limit:.2f} excedido. "
f"Custo acumulado: ${self.accumulated_cost:.4f}"
)
def get_summary(self) -> dict:
return {
"session_id": self.session_id,
"total_cost_usd": round(self.accumulated_cost, 6),
"hard_limit_usd": self.hard_limit,
"utilization_pct": round(self.accumulated_cost / self.hard_limit * 100, 1),
"cost_by_task": {k: round(v, 6) for k, v in self.cost_by_task.items()},
"cost_by_model": {k: round(v, 6) for k, v in self.cost_by_model.items()},
}
class BudgetExceededException(Exception):
"""Lançada quando execução atinge o limite de custo configurado."""
pass
🔔 Alertas de Custo
Alertas multicamada garantem que nenhum padrão anômalo de custo passe despercebido — sem criar alert fatigue com notificações excessivas.
Camada 1: Por execução
ImediatoExecução ultrapassa 80% do budget → alerta soft (warning). 100% → budget hard stop automático. Ação: notificar responsável + pausar execução se configurado.
Camada 2: Por hora/dia
AgregadoCusto horário ultrapassa threshold configurado → alerta. Custo diário acima do baseline histórico → alerta de anomalia. Identifica padrões antes da fatura chegar.
Camada 3: Por usuário/tenant
MensalCusto mensal de um departamento/cliente ultrapassa limite configurado → alerta para gestor responsável. Permite chargeback proativo.
class CostAlertSystem:
"""Sistema de alertas de custo multicamada."""
def __init__(self, slack_webhook: str, email_recipients: list[str]):
self.slack_webhook = slack_webhook
self.emails = email_recipients
# Thresholds configuráveis
self.thresholds = {
"per_execution_warning_usd": 4.0, # 80% de budget $5
"per_execution_critical_usd": 5.0, # budget máximo
"per_hour_usd": 20.0, # custo/hora máximo
"per_day_usd": 100.0, # custo/dia máximo
"anomaly_multiplier": 2.5, # X vezes o baseline histórico
}
self._hourly_cost = 0.0
self._daily_cost = 0.0
self._baseline_hourly = None # calculado do histórico
async def check_and_alert(self, cost_event: dict):
"""Verifica todas as camadas e dispara alertas se necessário."""
cost = cost_event["call_cost"]
accumulated = cost_event["accumulated"]
self._hourly_cost += cost
self._daily_cost += cost
alerts = []
# Camada 1: por execução
if accumulated >= self.thresholds["per_execution_critical_usd"]:
alerts.append({
"level": "CRITICAL",
"message": f"Budget crítico: ${accumulated:.2f} atingido",
"action": "Execução pausada automaticamente"
})
elif accumulated >= self.thresholds["per_execution_warning_usd"]:
alerts.append({
"level": "WARNING",
"message": f"Execução em ${accumulated:.2f} (80% do budget)",
"action": "Monitorar de perto"
})
# Camada 2: por hora
if self._hourly_cost > self.thresholds["per_hour_usd"]:
alerts.append({
"level": "WARNING",
"message": f"Custo horário: ${self._hourly_cost:.2f}/h (limite: ${self.thresholds['per_hour_usd']})"
})
# Anomaly detection simples
if self._baseline_hourly and self._hourly_cost > self._baseline_hourly * self.thresholds["anomaly_multiplier"]:
alerts.append({
"level": "WARNING",
"message": f"Anomalia: custo {self._hourly_cost/self._baseline_hourly:.1f}x acima do baseline"
})
for alert in alerts:
await self._send_alert(alert, cost_event)
async def _send_alert(self, alert: dict, context: dict):
"""Envia alerta para Slack."""
emoji = "🚨" if alert["level"] == "CRITICAL" else "⚠️"
message = {
"text": f"{emoji} *Alerta de Custo de Agente*",
"attachments": [{
"color": "#FF0000" if alert["level"] == "CRITICAL" else "#FFA500",
"fields": [
{"title": "Nível", "value": alert["level"], "short": True},
{"title": "Mensagem", "value": alert["message"], "short": False},
{"title": "Session", "value": context.get("session_id", "N/A"), "short": True},
]
}]
}
async with aiohttp.ClientSession() as session:
await session.post(self.slack_webhook, json=message)
🏢 Cost Allocation
Cada execução do orquestrador precisa ter um cost_center associado — departamento, projeto ou cliente. Isso cria accountability e permite chargeback interno.
Schema de cost allocation
-- Adição ao schema de sessões para cost allocation
ALTER TABLE orchestration_sessions ADD COLUMN IF NOT EXISTS
cost_center VARCHAR(100), -- "financeiro/q2-análise"
department VARCHAR(100), -- "financeiro"
project_id VARCHAR(100), -- "q2-analise-concorrencia"
client_id VARCHAR(100), -- para SaaS multi-tenant
requester VARCHAR(100); -- quem solicitou a execução
-- View para relatório mensal de cost center
CREATE VIEW monthly_cost_by_department AS
SELECT
department,
DATE_TRUNC('month', started_at) AS month,
COUNT(*) AS total_sessions,
SUM(cost_usd) AS total_cost_usd,
AVG(cost_usd) AS avg_cost_per_session,
COUNT(*) FILTER (WHERE status = 'completed') AS successful
FROM orchestration_sessions
WHERE started_at >= NOW() - INTERVAL '12 months'
GROUP BY department, DATE_TRUNC('month', started_at)
ORDER BY month DESC, total_cost_usd DESC;
Relatório de chargeback mensal
| Departamento | Sessões | Custo Março | Custo Fev |
|---|---|---|---|
| Financeiro | 47 | $234.50 | $198.20 |
| Jurídico | 32 | $189.10 | $201.40 |
| Produto | 83 | $412.80 | $356.90 |
| TOTAL | 162 | $836.40 | $756.50 |
📈 Relatório de Custo e ROI
O argumento final para manter orçamento em agentes: mostrar o ROI real. Custo de IA vs. custo de trabalho humano equivalente para as mesmas tarefas.
Exemplo: Due Diligence de empresa
Onde o ROI é menor (mas ainda positivo)
Query SQL para cálculo de ROI
-- ROI por tipo de tarefa (baseado em custo humano configurado)
WITH human_cost_reference AS (
SELECT
task_type,
human_hourly_rate_usd,
estimated_human_hours
FROM task_type_config -- tabela de configuração com custo humano estimado
),
agent_cost AS (
SELECT
JSON_EXTRACT(input_data, '$.agente_tipo') AS task_type,
AVG(cost_usd) AS avg_agent_cost,
AVG(EXTRACT(EPOCH FROM (completed_at - started_at))) AS avg_duration_sec,
COUNT(*) AS total_executions
FROM task_executions
WHERE status = 'done'
AND started_at >= NOW() - INTERVAL '30 days'
GROUP BY task_type
)
SELECT
a.task_type,
a.avg_agent_cost,
a.avg_duration_sec / 60 AS avg_duration_min,
(h.human_hourly_rate_usd * h.estimated_human_hours) AS human_cost_equivalent,
(h.human_hourly_rate_usd * h.estimated_human_hours) / a.avg_agent_cost AS roi_multiplier,
a.total_executions,
a.total_executions * a.avg_agent_cost AS total_ai_cost,
a.total_executions * (h.human_hourly_rate_usd * h.estimated_human_hours) AS total_human_equivalent
FROM agent_cost a
JOIN human_cost_reference h ON a.task_type = h.task_type
ORDER BY roi_multiplier DESC;
✂️ Otimização de Custo
Além dos controles de custo, há técnicas ativas para reduzir o custo por execução mantendo a qualidade. A combinação das 3 técnicas pode reduzir custo em 70-90%.
Técnica 1: Heterogeneous Routing (detalhado no módulo 5.10)
Usar o modelo mais barato capaz para cada tipo de tarefa. Classificador de complexidade direciona para Haiku ($0.00025/1k) quando possível, Sonnet para médio, Opus apenas para complexo.
Técnica 2: Semantic Caching (detalhado no módulo 5.10)
Quando duas execuções fazem perguntas semanticamente similares, retornar resultado cacheado. Embedding similarity com threshold de 0.92+ detecta queries equivalentes.
Técnica 3: Context Compression
Em vez de passar o resultado completo de uma tarefa para a próxima, passar um sumário comprimido. Compressão 10:1 nos resultados intermediários reduz tokens de contexto drasticamente.
Resultado combinado das otimizações
# Exemplo: due diligence de empresa sem vs. com otimizações
baseline = {
"modelo": "claude-opus-4-6 para tudo",
"cache_hit_rate": "0%",
"context_compressao": "sem compressão",
"custo_estimado": "$24.80",
"tempo_min": 8.5,
"qualidade_score": 9.2 # de 10
}
otimizado = {
"modelo": "heterogeneous: 40% Haiku, 45% Sonnet, 15% Opus",
"cache_hit_rate": "35%",
"context_compressao": "10:1 nos resultados intermediários",
"custo_estimado": "$2.40", # 90% de redução!
"tempo_min": 6.5, # mais rápido também
"qualidade_score": 8.9 # quase idêntico
}
# A pergunta: você aceita 0.3 pontos de qualidade por 90% de redução de custo?
# Na maioria dos casos: SIM — essa é a fronteira de Pareto correta