Arquitetura Completa - CloudSQL Session + Memory Services¶
🏗️ Visão Geral do Stack¶
┌────────────────────────────────────────────────────────────────────┐
│ Aplicação (Slack Bot / API) │
└────────────────────────────────┬───────────────────────────────────┘
│
┌────────────▼────────────┐
│ Google ADK Runner │
└────────────┬────────────┘
│
┌───────────────┼───────────────┐
│ │
┌────────────▼────────────┐ ┌────────────▼────────────┐
│ CloudSQLSessionService │ │ CloudSQLMemoryService │
│ (Conversa atual) │ │ (Histórico longo prazo)│
└────────────┬────────────┘ └────────────┬────────────┘
│ │
│ │
└───────────────┬───────────────┘
│
┌────────────▼────────────┐
│ Google Cloud SQL │
│ (MySQL 5.7) │
│ │
│ ┌──────────────────┐ │
│ │ agent_sessions │ │
│ │ (TTL: 60min) │ │
│ └──────────────────┘ │
│ │
│ ┌──────────────────┐ │
│ │ agent_memories │ │
│ │ (Permanente) │ │
│ └──────────────────┘ │
└─────────────────────────┘
🔄 Fluxo Completo de Dados¶
1. Nova Conversa¶
User: "Olá!"
│
▼
┌───────────────────────────────────────────────────┐
│ Runner.run_async() │
│ │
│ 1. session_service.get_session() │
│ ├─ SELECT FROM agent_sessions │
│ └─ Se não existe: INSERT INTO agent_sessions │
│ │
│ 2. memory_service.search_memory() │
│ └─ SELECT FROM agent_memories WHERE... │
│ │
│ 3. Agent processa com context │
│ │
│ 4. session_service.append_event() │
│ └─ UPDATE agent_sessions SET messages=[...] │
└───────────────────────────────────────────────────┘
2. Continuação da Conversa¶
User: "Me conte mais"
│
▼
┌───────────────────────────────────────────────────┐
│ Runner.run_async() │
│ │
│ 1. session_service.get_session() │
│ └─ SELECT FROM agent_sessions │
│ └─ Retorna session com messages anteriores │
│ │
│ 2. Agent usa histórico da sessão atual │
│ │
│ 3. session_service.append_event() │
│ └─ UPDATE agent_sessions (adiciona msg) │
└───────────────────────────────────────────────────┘
3. Fim da Conversa (Salvar Memória)¶
Conversa finalizada
│
▼
┌───────────────────────────────────────────────────┐
│ Application Code │
│ │
│ session = await session_service.get_session() │
│ await memory_service.add_session_to_memory() │
│ │
│ └─ INSERT INTO agent_memories │
│ (extrai conhecimento da sessão) │
└───────────────────────────────────────────────────┘
4. Nova Sessão (Dias Depois)¶
User: "Lembra da nossa conversa?"
│
▼
┌───────────────────────────────────────────────────┐
│ Runner.run_async() │
│ │
│ 1. Nova session (session_id diferente) │
│ └─ agent_sessions: [] (vazia) │
│ │
│ 2. memory_service.search_memory() │
│ └─ SELECT FROM agent_memories WHERE... │
│ └─ Encontra conversa anterior! │
│ │
│ 3. Agent usa memória de longo prazo │
│ └─ "Sim, lembro! Conversamos sobre..." │
└───────────────────────────────────────────────────┘
📊 Estrutura das Tabelas¶
agent_sessions (Conversa Atual)¶
┌────────────────────────────────────────────────────────┐
│ id │ "user123_20241219_001" │
│ app_name │ "ifriend-agent" │
│ user_id │ "user123" │
│ created_at │ 2024-12-19 10:00:00 │
│ updated_at │ 2024-12-19 10:15:00 │
│ expires_at │ 2024-12-19 11:00:00 (⏰ TTL: 60min) │
│ messages │ [ │
│ │ { │
│ │ "role": "user", │
│ │ "parts": [{"text": "Olá!"}], │
│ │ "timestamp": "2024-12-19T10:00:00Z" │
│ │ }, │
│ │ { │
│ │ "role": "assistant", │
│ │ "parts": [{"text": "Oi! Como..."}], │
│ │ "timestamp": "2024-12-19T10:00:05Z" │
│ │ } │
│ │ ] │
└────────────────────────────────────────────────────────┘
↓ Após TTL expirar ou ao final da conversa
↓
┌────────────────────────────────────────────────────────┐
│ Sessão deletada automaticamente │
│ (ou salva como memória antes de expirar) │
└────────────────────────────────────────────────────────┘
agent_memories (Histórico Permanente)¶
┌────────────────────────────────────────────────────────┐
│ id │ 1 │
│ app_name │ "ifriend-agent" │
│ user_id │ "user123" │
│ session_id │ "user123_20241219_001" │
│ content │ "P: Olá! Estou procurando notebooks\n\n │
│ │ Claro! Temos várias opções..." │
│ event_count │ 6 │
│ created_at │ 2024-12-19 10:30:00 │
└────────────────────────────────────────────────────────┘
↑
│ PERMANENTE - Não expira
│ Usado para busca em futuras conversas
🎯 Casos de Uso¶
Caso 1: Atendimento ao Cliente¶
Dia 1 (SessionService):
User: "Meu pedido #123 não chegou"
Agent: "Vou verificar... O pedido está a caminho"
[Salvo em agent_sessions com TTL 60min]
Após conversa:
→ add_session_to_memory()
[Salvo em agent_memories permanentemente]
Dia 5 (MemoryService):
User: "Qual era o status do meu pedido?"
Agent: "Lembro que você perguntou sobre o pedido #123..."
[Buscado em agent_memories]
Caso 2: Vendas / Recomendações¶
Sessão 1 (SessionService):
User: "Quero um notebook para Python"
Agent: "Recomendo modelos com 16GB RAM..."
[Mensagens em agent_sessions]
Fim da sessão:
→ add_session_to_memory()
[Preferências salvas em agent_memories]
Sessão 2 semanas depois (MemoryService):
User: "Chegaram notebooks novos?"
Agent: "Sim! E lembro que você procurava modelos para Python..."
[Preferências recuperadas de agent_memories]
Caso 3: Chat Multi-turno¶
Turno 1:
User: "Como fazer bolo?"
Agent: "Você precisa de farinha, ovos..."
[agent_sessions: mensagem 1]
Turno 2 (mesma sessão):
User: "Que temperatura usar?"
Agent: "180°C, como mencionei antes..."
[agent_sessions: mensagem 2 - Agent vê mensagem 1]
Turno 3 (mesma sessão):
User: "Quanto tempo?"
Agent: "45 minutos no forno a 180°C"
[agent_sessions: mensagem 3 - Agent vê mensagens 1 e 2]
⚡ Performance e Otimizações¶
Latências Típicas¶
| Operação | CloudSQL | Firestore |
|---|---|---|
| get_session | 5-20ms | 50-100ms |
| append_event | 10-25ms | 50-150ms |
| search_memory | 20-50ms | 100-200ms |
| add_to_memory | 10-30ms | 100-150ms |
Índices Otimizados¶
-- agent_sessions
INDEX idx_user_id (user_id) -- Para list_sessions
INDEX idx_expires_at (expires_at) -- Para cleanup
INDEX idx_app_name (app_name) -- Para multi-tenancy
-- agent_memories
INDEX idx_user_id (user_id) -- Para search_memory
INDEX idx_created_at (created_at) -- Para ordenação
INDEX idx_app_name (app_name) -- Para multi-tenancy
🔐 Configuração de Produção¶
1. Mesma Instância Cloud SQL¶
# Configuração compartilhada
config = {
"unix_socket": "/cloudsql/project:region:instance",
"database": "ifriend_db",
"user": "agent_user",
"password": secret_manager.get("db_password")
}
session_service = CloudSQLSessionService(**config)
memory_service = CloudSQLMemoryService(**config)
2. Instâncias Separadas (Alta Escala)¶
# Session: Instância otimizada para escrita rápida
session_service = CloudSQLSessionService(
unix_socket="/cloudsql/project:region:sessions-instance",
database="sessions_db"
)
# Memory: Instância otimizada para leitura
memory_service = CloudSQLMemoryService(
unix_socket="/cloudsql/project:region:memories-instance",
database="memories_db"
)
3. Com Read Replicas¶
# Writes vão para a instância principal
session_service = CloudSQLSessionService(
host="10.x.x.x", # Primary instance
database="agent_db"
)
# Reads podem usar replica (memória apenas lê)
memory_service = CloudSQLMemoryService(
host="10.x.x.y", # Read replica
database="agent_db"
)
📈 Escalabilidade¶
Cenários de Carga¶
| Usuários Simultâneos | CloudSQL Tier | Custo/mês | Latência |
|---|---|---|---|
| < 100 | db-f1-micro | $7 | <20ms |
| 100-1k | db-g1-small | $25 | <15ms |
| 1k-10k | db-n1-standard-1 | $50 | <10ms |
| 10k+ | db-n1-standard-4+ | $200+ | <5ms |
Monitoramento¶
import time
import logging
class MonitoredCloudSQLService:
async def get_session(self, **kwargs):
start = time.time()
result = await super().get_session(**kwargs)
duration = time.time() - start
logging.info(f"get_session: {duration*1000:.2f}ms")
if duration > 0.1: # 100ms
logging.warning(f"Slow query: get_session took {duration}s")
return result
🧹 Manutenção¶
Limpeza Periódica¶
# Executar via Cron/Cloud Scheduler
async def maintenance_job():
# Limpar sessões expiradas
removed_sessions = await session_service.cleanup_expired_sessions()
# Limpar memórias antigas (> 6 meses)
# TODO: Implementar em MemoryService
print(f"Manutenção: {removed_sessions} sessões removidas")
Backup Automático¶
# Cloud SQL Automated Backups (configurar no console)
# Ou manual:
mysqldump -h 10.x.x.x -u agent_user -p ifriend_db > backup_$(date +%Y%m%d).sql
💰 Custos Estimados¶
Exemplo: 1000 usuários/dia, 5 mensagens/sessão¶
CloudSQL db-g1-small: $25/mês
Storage:
- 1000 sessões ativas × 5KB = 5MB
- 1000 memórias/dia × 2KB × 30 dias = 60MB
- Total: ~65MB (~$0.17/mês)
Total: ~$25/mês
vs
Firestore:
- 1000 usuários × 5 mensagens × 2 reads = 10k reads/dia
- 10k reads × 30 dias × $0.06/100k = $1.80/mês
- 5k writes/dia × 30 dias × $0.18/100k = $2.70/mês
Total: ~$4.50/mês
CloudSQL é mais caro mas oferece: - Latência 5-10x menor - SQL completo - Controle total - Melhor para apps de alto tráfego
🎓 Boas Práticas¶
- Use TTL apropriado - 60min para chat, 24h para workflows longos
- Salve na memória - Apenas sessões relevantes
- Limpe regularmente - Execute cleanup_expired_sessions()
- Monitore latências - Alert se > 100ms
- Use connection pooling - Instância singleton do service
- Backup diário - Configure Cloud SQL automated backups