Skip to content

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

  1. Use TTL apropriado - 60min para chat, 24h para workflows longos
  2. Salve na memória - Apenas sessões relevantes
  3. Limpe regularmente - Execute cleanup_expired_sessions()
  4. Monitore latências - Alert se > 100ms
  5. Use connection pooling - Instância singleton do service
  6. Backup diário - Configure Cloud SQL automated backups

📚 Referências