Telemetria / Dashboard¶
Todo turno do agente é logado em 4 tabelas para análise operacional.
Tabelas¶
dashboard_sessions— uma linha por sessão novadashboard_token_usage— uma linha por chamada LLMdashboard_errors— uma linha por erro capturadodashboard_tags— uma linha por tag aplicada via WTS
Schema completo em Banco de dados.
Funções em tracking.py¶
log_session¶
def log_session(session_id: str, phone: str, contact_name: str = "",
content_type: str = "TEXT") -> None:
with _conn.cursor() as cur:
cur.execute(
"""INSERT INTO dashboard_sessions
(session_id, phone_number, contact_name, content_type)
VALUES (%s, %s, %s, %s)
ON CONFLICT (session_id) DO NOTHING""",
(session_id, phone, contact_name, content_type),
)
Chamado no início do workflow — idempotente via ON CONFLICT DO NOTHING.
log_token_usage¶
def log_token_usage(session_id, provider, model, prompt_tokens, completion_tokens):
total = prompt_tokens + completion_tokens
cost_usd = _calc_cost_usd(model, prompt_tokens, completion_tokens)
cost_brl = cost_usd * 5.70 # taxa estática; ajuste quando necessário
with _conn.cursor() as cur:
cur.execute(
"""INSERT INTO dashboard_token_usage
(session_id, provider, model, prompt_tokens, completion_tokens,
total_tokens, cost_usd, cost_brl)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)""",
(session_id, provider, model, prompt_tokens, completion_tokens,
total, cost_usd, cost_brl),
)
Tabela de preços OpenAI (simplificada)¶
_OPENAI_PRICES = {
# $ por 1M tokens (atualizar conforme tabela oficial)
"gpt-4.1": {"input": 2.50, "output": 10.00},
"gpt-4.1-mini": {"input": 0.15, "output": 0.60},
"whisper-1": {"per_min": 0.006},
}
def _calc_cost_usd(model: str, p_in: int, p_out: int) -> float:
price = _OPENAI_PRICES.get(model, {})
return (p_in / 1e6) * price.get("input", 0) + (p_out / 1e6) * price.get("output", 0)
log_error¶
def log_error(error_type: str, error_message: str, session_id: str = "",
phone_number: str = "", stack_trace: str = "") -> None:
with _conn.cursor() as cur:
cur.execute(
"""INSERT INTO dashboard_errors
(session_id, phone_number, error_type, error_message, stack_trace)
VALUES (%s, %s, %s, %s, %s)""",
(session_id, phone_number, error_type, error_message, stack_trace),
)
error_type usado tipicamente:
"agent_error"— exceção no agente"workflow_error"— exceção no workflow"otimus_error"— falha em chamada OTIMUS"media_error"— falha em Gemini/Whisper"falha_consulta"— FALHA_CONSULTA controlado (opcional logar)
log_transfer¶
def log_transfer(session_id: str) -> None:
with _conn.cursor() as cur:
cur.execute(
"UPDATE dashboard_sessions SET transferred = TRUE WHERE session_id = %s",
(session_id,),
)
log_tag¶
def log_tag(session_id: str, phone: str, tag_name: str) -> None:
with _conn.cursor() as cur:
cur.execute(
"""INSERT INTO dashboard_tags (session_id, phone_number, tag_name)
VALUES (%s, %s, %s)""",
(session_id, phone, tag_name),
)
Queries úteis¶
Sessões hoje¶
Taxa de transferência¶
SELECT
COUNT(*) FILTER (WHERE transferred) * 100.0 / COUNT(*) AS transfer_rate
FROM dashboard_sessions
WHERE started_at > NOW() - INTERVAL '7 days';
Custo acumulado¶
SELECT
DATE(created_at) AS dia,
SUM(cost_usd) AS usd,
SUM(cost_brl) AS brl,
SUM(total_tokens) AS tokens
FROM dashboard_token_usage
WHERE created_at > NOW() - INTERVAL '30 days'
GROUP BY dia
ORDER BY dia DESC;
Top erros¶
SELECT error_type, COUNT(*) AS n
FROM dashboard_errors
WHERE created_at > NOW() - INTERVAL '7 days'
GROUP BY error_type
ORDER BY n DESC;
Dashboard visual¶
Os projetos atuais não expõem um dashboard visual pronto — as queries são rodadas direto no Postgres. Se quiser:
- Grafana com datasource Postgres: painel de sessões, tokens, erros.
- Metabase em container separado: mais visual para time não-técnico.
Limpeza / retenção¶
-- Apagar telemetria > 180 dias
DELETE FROM dashboard_token_usage WHERE created_at < NOW() - INTERVAL '180 days';
DELETE FROM dashboard_errors WHERE created_at < NOW() - INTERVAL '180 days';
DELETE FROM dashboard_tags WHERE created_at < NOW() - INTERVAL '180 days';
DELETE FROM dashboard_sessions WHERE started_at < NOW() - INTERVAL '180 days';
Rodar mensalmente via cron no servidor.
Integração com Slack / alertas¶
Nenhum projeto tem hoje, mas recomendado:
- Alerta Slack quando `COUNT(dashboard_errors WHERE created_at > NOW()
- interval '5 min') > 10`.
- Alerta Slack quando
cost_brldiário > R$ X (detecta loop / prompt ruim).