Observability Stack
Sistema completo de observabilidade com métricas de negócio, tracing distribuído, monitoramento de custos de tokens, telemetria de decisões de IA e alertas em tempo real.
Por Que Observabilidade é Crítica
Em sistemas de IA conversacional, "não funcionou" não é um diagnóstico. Precisamos responder perguntas específicas:
- ?Debug: Por que o AI não entendeu "quero marcar amanhã"?
- ?Performance: Qual nó do LangGraph está lento?
- ?Custos: Quanto estamos gastando em tokens por tenant?
- ?Negócio: Qual % das conversas resolvemos sem humano?
- ?Qualidade: O RAG está retornando preços corretos?
Stack Completo
📊 Business Metrics (Prometheus)
Métricas de negócio com ownership claro por serviço (evita double counting):
| Métrica | Owner | Labels |
|---|---|---|
| optimus_handover_requests_total | Orchestrator | tenant_id, reason, outcome |
| optimus_ai_conversations_total | AI Engine | tenant_id, resolved_by |
| optimus_booking_attempts_total | Memory Engine | tenant_id, status |
| optimus_ai_first_response_seconds | AI Engine | tenant_id (histogram) |
Ownership Table: Cada métrica tem um único serviço responsável por emiti-la, evitando contagem dupla em sistemas distribuídos.: Cada métrica tem um único serviço responsável por emiti-la, evitando contagem dupla em sistemas distribuídos.
🔍 Distributed Tracing (OpenTelemetry)
Tracing end-to-end com propagação de contexto entre serviços:
Request → Orchestrator → AI Engine → Memory Engine → PostgreSQL
│ │ │ │
│ │ │ └── db.query span
│ │ └── langgraph.node spans (20+)
│ └── chat.orchestration span
└── http.request span
trace_id: a1b2c3d4... (propagado via headers)
- • FastAPI requests/responses
- • HTTPX client calls
- • SQLAlchemy queries
- • Redis operations
- • LangGraph node execution
- • RAG search decisions
- • Booking FSM transitions
- • LLM API calls + tokens
💰 Token Usage & Cost Tracking
Relatórios de uso de tokens via Jaeger traces para controle de custos:
GET /api/token-reports?tenant_id=clinic2&days=30
{
"tenant_id": "clinic2",
"total_input_tokens": 1_250_000,
"total_output_tokens": 450_000,
"total_cost": 42.50, // USD estimated
"models_breakdown": {
"gpt-4o-mini": {"tokens": 1_500_000, "cost": 15.00},
"gpt-4o": {"tokens": 200_000, "cost": 27.50}
},
"daily_usage": {
"2025-01-15": {"tokens": 50_000, "cost": 1.50},
...
}
}
Dados extraídos de spans OpenTelemetry com atributos llm.input_tokens, llm.output_tokens, llm.model.
📸 State Snapshot Telemetry
Cada response inclui snapshot do estado para debug de conversas:
response_metadata.state_snapshot = {
"appointment_confirmed": true,
"booking_id": "apt_12345",
"booking_datetime": "2025-01-20T10:00:00",
"pending_action": null,
"user_intent": "schedule",
"customer_name": "João Silva",
"tool_budget_reached": false,
"tool_calls_used": 3,
"tool_budget_max": 6
}
🎯 RAG Decision Telemetry
Explicabilidade de decisões do pipeline de pricing/RAG:
{
"total_entries": 10,
"match_types": {
"structured": 8, // Direct catalog match
"semantic": 2, // Embeddings match
"alias": 0, // Synonym match
"fallback": 0 // Generic fallback
},
"score_stats": {"avg": 0.89, "min": 0.82, "max": 0.95},
"fallback_rate": 0.0,
"reasoning_samples": [
{"item": "hemograma", "reason": "exact_match", "score": 0.95}
]
}
Permite identificar: catálogo incompleto, aliases faltando, threshold de score inadequado.
📡 Usage Monitoring Middleware
Monitoramento de endpoints para decisões de deprecação:
- ✓Non-intrusive: Não bloqueia requests, apenas observa
- ✓Sample rate: Configurável por ambiente (prod: 1%, dev: 100%)
- ✓30+ days data: Coleta dados antes de decisões de remoção
- ✓Endpoint tracking: Identifica endpoints legados ainda em uso
📈 Performance Thresholds
Thresholds automáticos para alertas:
- • memory_retrieval_latency > 100ms
- • cache_hit_rate < 80%
- • error_rate > 1%
- • memory_retrieval_latency > 500ms
- • cache_hit_rate < 60%
- • error_rate > 5%
Arquitetura
┌─────────────────────────────────────────────────────────────┐ │ OBSERVABILITY STACK │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Orchestrator│ │ AI Engine │ │Memory Engine│ │ │ │ metrics │ │ metrics │ │ metrics │ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ │ └────────────────┼────────────────┘ │ │ │ │ │ ┌───────────────────────▼─────────────────────────────┐ │ │ │ PROMETHEUS │ │ │ │ - Business metrics (handover, booking, AI) │ │ │ │ - System metrics (latency, errors, cache) │ │ │ │ - Custom metrics (RAG, tool budget) │ │ │ └───────────────────────┬─────────────────────────────┘ │ │ │ │ │ ┌───────────────────────▼─────────────────────────────┐ │ │ │ GRAFANA DASHBOARDS │ │ │ │ - Real-time KPIs per tenant │ │ │ │ - Cost tracking & forecasting │ │ │ │ - Alert rules & notifications │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ OPENTELEMETRY + JAEGER │ │ │ │ - Distributed traces across services │ │ │ │ - Token usage extraction from spans │ │ │ │ - Latency breakdown per operation │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ LOKI + JSON LOGS │ │ │ │ - Structured logging with trace correlation │ │ │ │ - trace_id + span_id in every log entry │ │ │ │ - Searchable by tenant, operation, error │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘
Decisões Técnicas
Por que ownership table para métricas?
Em sistemas distribuídos, múltiplos serviços podem observar o mesmo evento. Sem ownership claro, métricas de handover seriam emitidas pelo Orchestrator E pelo AI Engine, causando double counting. Ownership table define um único emissor por métrica.
Por que extrair tokens de traces e não de logs?
Traces têm estrutura padronizada (span attributes) e contexto (trace_id liga request a response). Logs são texto livre que requer parsing. Jaeger já indexa spans por atributo, facilitando queries como "total tokens por modelo no último mês".
Por que state snapshot em response_metadata?
Debugging de conversas requer saber o estado exato após cada turno. Incluir snapshot no response permite reproduzir problemas sem acessar logs ou traces. Frontend pode mostrar "estado interno" em modo debug.
Por que sample rate configurável?
Em produção com milhões de requests, 100% sampling é caro (storage, processing). 1-10% sampling captura anomalias suficientes para debug. Em dev/staging, 100% permite debug completo de cada request.