✅ Passo 2: MemoryService Implementado¶
🎯 Objetivo¶
Permitir que o Agent acesse informações de conversas passadas (cross-session), não apenas da conversa atual.
📚 Conceito¶
Session vs Memory¶
| Aspecto | Session | Memory |
|---|---|---|
| Escopo | UMA conversa | MUITAS conversas |
| Duração | Enquanto conversa ativa | Indefinido (histórico) |
| Armazenamento | session.events | MemoryService |
| Acesso | Automático (Runner passa) | Tool (Agent solicita) |
| Exemplo | "O que você respondeu antes?" | "Lembra quando você me ajudou semana passada?" |
🔧 Implementação¶
1. FirestoreMemoryService (session/memory_service.py)¶
class FirestoreMemoryService(BaseMemoryService):
async def add_session_to_memory(session: Session) -> None:
"""Salva conhecimento extraído de uma sessão ao histórico"""
# Extrai conteúdo relevante dos eventos
# Armazena no Firestore com user_id + timestamp
async def search_memory(app_name, user_id, query) -> SearchMemoryResponse:
"""Busca informações relevantes no histórico"""
# Busca por palavras-chave nos documentos de memória do usuário
# Retorna resultados relevantes
2. Integração com Slack Bot (slack_bot.py)¶
# Configuração
memory_service = FirestoreMemoryService(collection_name="...")
runner = Runner(
agent=root_agent,
app_name="...",
session_service=session_service,
memory_service=memory_service # ← Passo 2
)
# Após cada conversa, salvar memória
completed_session = await session_service.get_session(...)
await memory_service.add_session_to_memory(completed_session)
3. Agent com PreloadMemoryTool (agent.py)¶
from google.adk.tools import PreloadMemoryTool
root_agent = LlmAgent(
name='root_agent',
instruction="...",
tools=[
PreloadMemoryTool(), # ← Agent pode acessar histórico
busca_produtos_tool,
...
]
)
🔄 Fluxo Completo (2 Passos + Memory)¶
═══════════════════════════════════════════════════════════════
REQUEST 1 (primeira conversa com usuário)
────────────────────────────────────────────────────────────
1️⃣ Usuário: "Quais produtos vocês têm?"
2️⃣ slack_bot.py:
- Cria session_id = "user123_slack_channel456"
- runner.run_async(user_id, session_id, mensagem)
3️⃣ Runner:
- Chama: session_service.get_session()
- Retorna: Session(events=[]) ← Primeira vez
- Passa ao Agent
4️⃣ Agent:
- Pode chamar PreloadMemoryTool() ← Busca histórico
- Resultado: Nenhuma memória anterior (primeira conversa)
- Responde: "Temos produtos X, Y, Z"
5️⃣ Runner:
- Chama: session_service.append_event(session, evento_user)
- Chama: session_service.append_event(session, evento_agent)
- Eventos salvos no Firestore ✅
6️⃣ slack_bot.py finaliza:
- Recupera session completa com events
- Chama: memory_service.add_session_to_memory(session)
- Memória extraída e salva! ✅
════════════════════════════════════════════════════════════
REQUEST 2 (dias depois, mesmo usuário, novo canal)
────────────────────────────────────────────────────────────
1️⃣ Usuário: "Você consegue lembrar o que você falou antes?"
2️⃣ slack_bot.py:
- Nova session_id = "user123_slack_channel789"
- runner.run_async(user_id, session_id, mensagem)
3️⃣ Runner:
- Chama: session_service.get_session()
- Retorna: Session(events=[]) ← Nova sessão (vazia)
- Passa ao Agent
4️⃣ Agent ✨:
- Vê que não tem eventos nesta sessão
- Chama: PreloadMemoryTool("produtos que ofereço")
- MemoryService busca no histórico
- Encontra: "Temos produtos X, Y, Z"
- Responde: "Claro! Você perguntou sobre produtos X, Y, Z no nosso último papo."
5️⃣ Runner:
- Chama: session_service.append_event(session, evento_user)
- Chama: session_service.append_event(session, evento_agent)
- Novos eventos salvos ✅
6️⃣ slack_bot.py finaliza:
- Chama: memory_service.add_session_to_memory(session)
- Nova memória adicionada ao histórico ✅
════════════════════════════════════════════════════════════
🧠 Dados no Firestore¶
Collection: agente_busca_produtos_sessions¶
{
"user123_slack_channel456": {
"id": "user123_slack_channel456",
"user_id": "user123",
"app_name": "ifriend_busca_produtos_slack",
"messages": [
{
"role": "user",
"parts": [{"text": "Quais produtos?"}],
"timestamp": "2025-11-17T10:00:00Z"
},
{
"role": "assistant",
"parts": [{"text": "Temos produtos X, Y, Z"}],
"timestamp": "2025-11-17T10:00:05Z"
}
]
}
}
Collection: agente_busca_produtos_memory¶
{
"user123_1731834000000": {
"app_name": "ifriend_busca_produtos_slack",
"user_id": "user123",
"session_id": "user123_slack_channel456",
"created_at": "2025-11-17T10:00:10Z",
"content": "P: Quais produtos vocês têm?\n\nTemos produtos X, Y, Z",
"event_count": 2
}
}
🎯 O que o Agent pode fazer agora¶
Cenário 1: Contexto na mesma sessão¶
User: "Qual é o preço?"
Agent: [busca_produtos_tool] → tem info → responde com preço ✅
User: "Você já calculou isso antes?"
Agent: Vê session.events → "Sim, foi R$ 100" ✅
Cenário 2: Contexto em sessão futura¶
User: "O que você me recomendou antes?"
Agent: [PreloadMemoryTool("minhas recomendações")]
→ Busca em agente_busca_produtos_memory
→ Encontra: "Recomendei produtos X, Y, Z"
→ Responde ✅
Cenário 3: Personalizacão¶
User: "Gera um relatório de tudo que conversei com você"
Agent: [PreloadMemoryTool("todo histórico")]
→ Busca todas as memórias do usuário
→ Consolida informações
→ Gera relatório personalizado ✅
📊 Fluxo de Dados¶
Slack Message
↓
slack_bot.py (rate limiting + concurrency)
↓
runner.run_async()
├→ session_service.get_session()
│ └→ Carrega session.events do Firestore (Passo 1)
│
├→ root_agent.run()
│ ├→ PreloadMemoryTool()
│ │ └→ memory_service.search_memory()
│ │ └→ Busca em histórico cross-session
│ │
│ └→ Outras tools (busca_produtos_tool, etc)
│
├→ session_service.append_event() [x2]
│ └→ Salva interações no Firestore
│
└→ memory_service.add_session_to_memory()
└→ Extrai e armazena conhecimento
↓
Resposta final ao Slack ✅
✨ Benefícios¶
| Antes | Depois |
|---|---|
| ❌ Agent esquecia tudo entre mensagens | ✅ Agent lembra tudo na mesma sessão (Passo 1) |
| ❌ Nenhuma memória de conversas passadas | ✅ Agent tem acesso a histórico indefinido (Passo 2) |
| ❌ Não personalizava | ✅ Respostas personalizadas baseadas em histórico |
| ❌ Cada conversa era isolada | ✅ Continuidade e contexto cross-session |
🧪 Testes¶
Teste a integração completa:
Teste 1: Mesma sessão¶
# Request 1
curl -X POST http://localhost:8000/slack/events \
-H "Content-Type: application/json" \
-d '{"user_id":"user123","text":"Produtos de viagem"}'
# Request 2 (MESMA SESSION)
curl -X POST http://localhost:8000/slack/events \
-H "Content-Type: application/json" \
-d '{"user_id":"user123","text":"Qual era o nome do primeiro produto que você listou?"}'
# Agent deveria responder com o nome do produto ✅
Teste 2: Diferentes sessões¶
# Request 1 - Sessão A
curl -X POST .../events -d '{"user_id":"user123","channel":"ch1","text":"Produtos de viagem"}'
# Aguarde alguns segundos (simula usuário voltando depois)
# Request 2 - Sessão B (diferente channel)
curl -X POST .../events -d '{"user_id":"user123","channel":"ch2","text":"Lembra do que falamos antes?"}'
# Agent deveria usar PreloadMemoryTool e responder ✅
🚀 Próximos Passos¶
- Teste em produção com dados reais
- Monitorar Firestore - verificar quantidade de documentos
- Otimizar busca - implementar busca semântica com embeddings
- Cleanup automático - remover memórias antigas (>6 meses)
- Analytics - rastrear hits de memória vs misses