Skip to content

🎯 Diagrama Visual: Solução de Contexto Persistente

πŸ“Š Arquitetura Completa

╔═════════════════════════════════════════════════════════════════╗
β•‘                         SLACK USER                             β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
                             β”‚
                    "Qual Γ© o preΓ§o?"
                             β”‚
                             β–Ό
╔═════════════════════════════════════════════════════════════════╗
β•‘                    SLACK_BOT.PY                                 β•‘
β•‘  Rate Limiting: 2 simultΓ’neas, 3/min por user                 β•‘
β•‘  ConcorrΓͺncia: AsyncApp + asyncio locks                       β•‘
β•šβ•β•β•β•β•β•β•β•β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•
             β”‚                                  β”‚
             β”‚ check_user_rate_limit()         β”‚
             β”‚                                  β”‚
             β–Ό                                  β–Ό
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚ PASS: 1/3 βœ…     β”‚              β”‚ FAIL: >= 3 ❌   β”‚
      β”‚ Proceed          β”‚              β”‚ Reject with msg  β”‚
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
               β”‚ runner.run_async(user_id, session_id, msg)
               β”‚
               β–Ό
╔═════════════════════════════════════════════════════════════════╗
β•‘          ADK RUNNER (google.adk.runners.Runner)                β•‘
β•‘          Orquestra Session + Memory Services                   β•‘
β•šβ•β•β•β•β•β•β•β•β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•
             β”‚                                β”‚
      Etapa 1β”‚ session_service.get_session()  β”‚ Etapa 3
             β”‚                                β”‚ memory_service
             β–Ό                                β”‚ .add_session_...
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                 β”‚
    β”‚ SESSION SERVICE      β”‚                 β”‚
    β”‚ (Firestore)          β”‚                 β”‚
    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                 β”‚
    β”‚ πŸ”‘ PASSO 1           β”‚                 β”‚
    β”‚                      β”‚                 β”‚
    β”‚ Recupera session_id  β”‚                 β”‚
    β”‚ ↓                    β”‚                 β”‚
    β”‚ Carrega messages do  β”‚                 β”‚
    β”‚ Firestore            β”‚                 β”‚
    β”‚ ↓                    β”‚                 β”‚
    β”‚ Converte para Event  β”‚                 β”‚
    β”‚ objects              β”‚                 β”‚
    β”‚ ↓                    β”‚                 β”‚
    β”‚ Popula session.eventsβ”‚                 β”‚
    β”‚ ↓                    β”‚                 β”‚
    β”‚ Return Session βœ…    β”‚                 β”‚
    β”‚ with events          β”‚                 β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                 β”‚
              β”‚                               β”‚
              β”‚ Session(events=[...])         β”‚
              β”‚                               β”‚
              β–Ό                               β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                 β”‚
    β”‚    ROOT_AGENT        β”‚                 β”‚
    β”‚    (LlmAgent)        β”‚                 β”‚
    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                 β”‚
    β”‚ Instruction: "Buscar β”‚                 β”‚
    β”‚ produtos..."         β”‚                 β”‚
    β”‚                      β”‚                 β”‚
    β”‚ Tools:               β”‚                 β”‚
    β”‚  β€’ PreloadMemoryTool │───────┐         β”‚
    β”‚  β€’ busca_produtos    β”‚       β”‚         β”‚
    β”‚  β€’ calcular_preco    β”‚       β”‚         β”‚
    β”‚  β€’ ...               β”‚       β”‚         β”‚
    β”‚                      β”‚       β”‚         β”‚
    β”‚ Agent vΓͺ:            β”‚       β”‚         β”‚
    β”‚ β€’ session.events = [ β”‚       β”‚         β”‚ Etapa 2
    β”‚     "PreΓ§o?",        β”‚       β”‚         β”‚ Agent
    β”‚     "R$ 100"         β”‚       β”‚ chama   β”‚ pode
    β”‚   ]                  β”‚       β”‚ buscar  β”‚ buscar
    β”‚ β€’ user_id = "user123"β”‚       β”‚ histΓ³rico
    β”‚                      β”‚       β”‚         β”‚
    β”‚ Responde com contextoβ”‚       β”‚         β”‚
    β”‚ "VocΓͺ perguntou..."  β”‚       β”‚         β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚         β”‚
              β”‚                    β”‚         β”‚
        Evento (user +             β”‚         β”‚
        agent response)            β”‚         β”‚
              β”‚                    β”‚         β”‚
              β–Ό                    β”‚         β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚         β”‚
    β”‚ append_event()       β”‚       β”‚         β”‚
    β”‚ [x2 calls]           β”‚       β”‚         β”‚
    β”‚                      β”‚       β”‚         β”‚
    β”‚ EVENT 1: User msg    β”‚       β”‚         β”‚
    β”‚ EVENT 2: Agent resp  β”‚       β”‚         β”‚
    β”‚                      β”‚       β”‚         β”‚
    β”‚ Salva no Firestore   β”‚       β”‚         β”‚
    β”‚ session.messages βœ…  β”‚       β”‚         β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚         β”‚
               β”‚                   β”‚         β”‚
               β”‚                   β”‚         β”‚
               β”‚ Session(          β”‚         β”‚
               β”‚ events=[...],     β”‚         β”‚
               β”‚ messages: [       β”‚         β”‚
               β”‚   {msg1},         β”‚         β”‚
               β”‚   {msg2}          β”‚         β”‚
               β”‚ ])                β”‚         β”‚
               β”‚                   β”‚         β”‚
               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚        β”‚
                          β”‚        β”‚
                          β–Ό        β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚  MEMORY SERVICE                β”‚
            β”‚  (Firestore)                   β”‚
            β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
            β”‚  πŸ”‘ PASSO 2                    β”‚
            β”‚                                β”‚
            β”‚  add_session_to_memory()       β”‚
            β”‚  ↓                             β”‚
            β”‚  Extrai content relevante:     β”‚
            β”‚  "P: Qual Γ© o preΓ§o?"          β”‚
            β”‚  "R: R$ 100"                   β”‚
            β”‚  ↓                             β”‚
            β”‚  Salva em collection:          β”‚
            β”‚  agente_busca_produtos_memory  β”‚
            β”‚  ↓                             β”‚
            β”‚  user_id + timestamp = key βœ…  β”‚
            β”‚                                β”‚
            β”‚  search_memory()               β”‚
            β”‚  ↓                             β”‚
            β”‚  Busca por keyword:            β”‚
            β”‚  "preΓ§o" β†’ encontra! βœ…        β”‚
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         β”‚ SearchMemoryResponse
                         β”‚ [resultado1, ...]
                         β”‚
                         β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚ Agent Usa Resultado            β”‚
            β”‚                                β”‚
            β”‚ PreloadMemoryTool("preΓ§o")     β”‚
            β”‚ ↓                              β”‚
            β”‚ "Ah sim, vocΓͺ perguntou sobre  β”‚
            β”‚  preΓ§o antes e era R$ 100"     β”‚
            β”‚                                β”‚
            β”‚ PersonalizacΓ£o! βœ…             β”‚
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ”„ Fluxo Temporal: 2 Conversas Mesma Session

TEMPO ───────────────────────────────────────────────────────────>

T0: UsuΓ‘rio comeΓ§a conversa
    user123 em channel#produtos
    session_id = "user123_slack_products"
    β”‚
    β”œβ”€> runner.run_async(user_id, session_id, "Produtos de viagem?")
    β”‚
    β”œβ”€> get_session() ← Carrega eventos
    β”‚   β”‚ Firestore: messages = []
    β”‚   β”‚ session.events = []
    β”‚   └─> Retorna Session vazio (primeira vez)
    β”‚
    β”œβ”€> Agent processa
    β”‚   β”œβ”€> PreloadMemoryTool("viagem")
    β”‚   β”‚   └─> search_memory() ← Busca histΓ³rico anterior
    β”‚   β”‚       └─> "Nenhum resultado" (primeira conversa)
    β”‚   β”‚
    β”‚   └─> busca_produtos_tool("viagem")
    β”‚       └─> "Temos Safari, Cruzeiro, Ski"
    β”‚
    β”œβ”€> append_event(evento_user)  ← Salva pergunta
    β”œβ”€> append_event(evento_agent) ← Salva resposta
    β”‚   β”‚ Firestore: messages = [{user_msg}, {agent_msg}]
    β”‚   β”‚
    β”‚   └─> add_session_to_memory() ← Extrai conhecimento
    β”‚       └─> memory collection = [{content: "P:Viagem? R:Safari..."}]
    β”‚
    └─> Resposta ao Slack: "Temos Safari, Cruzeiro, Ski"

────────────────────────────────────────────────────────────────
                      (segundos depois)
────────────────────────────────────────────────────────────────

T1: UsuΓ‘rio faz pergunta 2
    user123 em channel#produtos
    session_id = "user123_slack_products" (MESMA)
    β”‚
    β”œβ”€> runner.run_async(user_id, session_id, "Qual Γ© mais barato?")
    β”‚
    β”œβ”€> get_session() ← Carrega eventos
    β”‚   β”‚ Firestore: messages = [{user_msg1}, {agent_msg1}]
    β”‚   β”‚ session.events = [Event(user_msg1), Event(agent_msg1)]
    β”‚   └─> Retorna Session COM histΓ³rico! βœ…
    β”‚
    β”œβ”€> Agent processa
    β”‚   β”œβ”€> VΓͺ session.events = [pergunta1, resposta1]
    β”‚   β”‚   └─> Agent entende contexto! "Eles perguntaram sobre viagem"
    β”‚   β”‚
    β”‚   β”œβ”€> PreloadMemoryTool("viagem barato")
    β”‚   β”‚   └─> "Cruzeiro Γ© mais barato"
    β”‚   β”‚
    β”‚   └─> calcular_preco_tool(safari, cruzeiro, ski)
    β”‚       └─> "Safari $2000, Cruzeiro $800, Ski $1500"
    β”‚
    β”œβ”€> append_event(evento_user)
    β”œβ”€> append_event(evento_agent)
    β”‚   └─> Firestore: messages = [{user_msg1}, {agent_msg1}, 
    β”‚                               {user_msg2}, {agent_msg2}]
    β”‚
    └─> Resposta: "Cruzeiro Γ© mais barato a $800"

────────────────────────────────────────────────────────────────
                    (dias depois, novo canal)
────────────────────────────────────────────────────────────────

T2: UsuΓ‘rio volta em novo canal
    user123 em channel#suporte  (DIFERENTE!)
    session_id = "user123_slack_suporte" (NOVA SESSION)
    β”‚
    β”œβ”€> runner.run_async(user_id, session_id, "Lembra de mim?")
    β”‚
    β”œβ”€> get_session() ← Carrega eventos
    β”‚   β”‚ Firestore: messages = [] (nova sessΓ£o!)
    β”‚   β”‚ session.events = []
    β”‚   └─> Retorna Session vazio
    β”‚
    β”œβ”€> Agent processa
    β”‚   β”œβ”€> VΓͺ session.events = [] (vazio, nova sessΓ£o)
    β”‚   β”‚   └─> Agent nΓ£o tem contexto desta sessΓ£o
    β”‚   β”‚
    β”‚   β”œβ”€> PreloadMemoryTool("histΓ³rico") ← Busca MEMΓ“RIA! 🧠
    β”‚   β”‚   β”‚ search_memory(user_id, query)
    β”‚   β”‚   β”‚ β”œβ”€> Busca em memory collection
    β”‚   β”‚   β”‚ └─> Encontra: "Conversas sobre viagem, Safari $2000..."
    β”‚   β”‚   β”‚
    β”‚   β”‚   └─> SearchMemoryResponse([resultado1, resultado2])
    β”‚   β”‚
    β”‚   └─> Agent responde com contexto de LONGO PRAZO!
    β”‚       "Sim! VocΓͺ perguntou sobre viagens. Recomendei Safari ($2000),
    β”‚        Cruzeiro ($800), Ski ($1500). Cruzeiro era mais barato."
    β”‚
    └─> Resposta: "Claro! VocΓͺ tinha interesse em viagens..."

🎯 Comparação: Antes vs Depois

❌ ANTES (Sem Contexto)

Request 1:
User: "Produtos de viagem?"
Agent: "Temos Safari, Cruzeiro, Ski" βœ“

Request 2 (MESMA SESSION):
User: "Qual Γ© mais barato?"
Agent: "Desculpe, preciso saber qual produto vocΓͺ quer comparar" ❌
       (Agent nΓ£o lembrava das opΓ§Γ΅es!)

Request 3 (NOVA SESSION):
User: "Lembra de mim?"
Agent: "Desculpe, é a primeira vez que conversamos" ❌
       (Agent perdia todo histΓ³rico!)

βœ… DEPOIS (Com Contexto)

Request 1:
User: "Produtos de viagem?"
Agent: "Temos Safari, Cruzeiro, Ski" βœ“
       [Salvo em session.events + memory]

Request 2 (MESMA SESSION):
User: "Qual Γ© mais barato?"
Agent: "Cruzeiro Γ© mais barato a $800" βœ…
       (Agent viu session.events!)

Request 3 (NOVA SESSION):
User: "Lembra de mim?"
Agent: "Sim! VocΓͺ perguntou sobre viagens.
         Recomendei Safari, Cruzeiro e Ski.
         Cruzeiro era o mais barato a $800" βœ…
       (Agent usou PreloadMemoryTool!)

🧠 Modelo Mental: 2 Níveis de Memória

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ SHORT-TERM MEMORY (Session)          β”‚
β”‚                                      β”‚
β”‚ session.events = [                   β”‚
β”‚   Event(role="user", ...),           β”‚
β”‚   Event(role="assistant", ...),      β”‚
β”‚   Event(role="user", ...),           β”‚
β”‚   Event(role="assistant", ...)       β”‚
β”‚ ]                                    β”‚
β”‚                                      β”‚
β”‚ VΓ‘lido por: DuraΓ§Γ£o da conversa      β”‚
β”‚ Armazenado em: SessionService        β”‚
β”‚ Acessado por: Automaticamente (Runner)
β”‚                                      β”‚
β”‚ Exemplo: "O que vocΓͺ respondeu       β”‚
β”‚          na primeira mensagem?"      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
              β”‚ (quando conversa termina)
              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ LONG-TERM MEMORY (MemoryService)     β”‚
β”‚                                      β”‚
β”‚ memory_content = "P: Viagens?        β”‚
β”‚  R: Safari, Cruzeiro, Ski"           β”‚
β”‚                                      β”‚
β”‚ VΓ‘lido por: Indefinido               β”‚
β”‚ Armazenado em: MemoryService         β”‚
β”‚ Acessado por: Agent com tool         β”‚
β”‚              (PreloadMemoryTool)     β”‚
β”‚                                      β”‚
β”‚ Exemplo: "O que vocΓͺ me recomendou  β”‚
β”‚          semana passada?"            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ” SeguranΓ§a & Privacidade

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ SESSION DATA (Firestore)               β”‚
β”‚ collection: agente_busca_produtos_...  β”‚
β”‚                                         β”‚
β”‚ document: {user_id}_slack_{channel_id} β”‚
β”‚ β”œβ”€ messages: [...]                     β”‚
β”‚ β”œβ”€ updated_at: timestamp               β”‚
β”‚ β”œβ”€ expires_at: timestamp (60 minutos)  β”‚
β”‚ └─ TTL: Auto-delete apΓ³s expiraΓ§Γ£o βœ…  β”‚
β”‚                                         β”‚
β”‚ ProteΓ§Γ£o: Dados antigos deletados      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ MEMORY DATA (Firestore)                 β”‚
β”‚ collection: agente_busca_produtos_...  β”‚
β”‚                                         β”‚
β”‚ document: {user_id}_{timestamp}        β”‚
β”‚ β”œβ”€ content: extracted text             β”‚
β”‚ β”œβ”€ user_id: indexed para busca         β”‚
β”‚ └─ created_at: timestamp               β”‚
β”‚                                         β”‚
β”‚ ProteΓ§Γ£o: Acesso por user_id           β”‚
β”‚           (isolamento entre usuΓ‘rios)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜