🎯 Resumo: Implementação Completa de Persistência de Contexto¶
📋 Status: ✅ COMPLETADO¶
Data: 17 de novembro de 2025
🎯 Objetivo Final¶
Resolver o problema: "Agent perde contexto entre mensagens"
Resultado: ✅ RESOLVIDO¶
O Agent agora mantém: - ✅ Contexto na mesma sessão - Lembra todas as mensagens da conversa atual - ✅ Contexto entre diferentes sessões - Armazena histórico em Firestore - ✅ Rate limiting - Evita sobrecarga (2 simultâneas, 3/min por usuário) - ✅ Error handling - Trata erros 503/429 do Gemini
🏗️ Arquitetura Implementada¶
┌─────────────────────────────────────────────────────────────┐
│ Layer 1: Rate Limiting & Concurrency Control │
│ - 2 requisições simultâneas globalmente │
│ - 3 requisições por usuário/minuto │
│ - Semáforo global + Lock por usuário │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Layer 2: ADK Runner com SessionService + MemoryService │
│ - Runner gerencia ciclo de vida da conversa │
│ - SessionService persiste no Firestore │
│ - MemoryService armazena histórico │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Layer 3: Root Agent (LlmAgent) │
│ - Acesso a ferramentas para busca de produtos │
│ - Acesso a histórico via SessionService │
│ - Acesso a memória de longo prazo via MemoryService │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Layer 4: Tools Especializadas │
│ - busca_produtos_tool │
│ - tem_variacao_tool │
│ - listar_variacoes_tool │
│ - detalhes_experience │
│ - disponibilidade_calendario_tool │
│ - disponibilidade_horarios_tool │
│ - calcular_preco_tool │
│ - gerar_csv_tool │
└─────────────────────────────────────────────────────────────┘
📦 Componentes Implementados¶
1. FirestoreSessionService (session/session_service.py)¶
Gerencia o ciclo de vida da sessão:
class FirestoreSessionService(BaseSessionService):
# Métodos principais:
async def create_session(...)
# Cria nova sessão no Firestore
# Inicializa com messages=[]
async def get_session(...)
# Recupera sessão existente
# Se não existe, cria automaticamente
# Carrega histórico do Firestore
async def update_session(...)
# Atualiza com nova mensagem
# Mantém histórico completo
async def append_event(...)
# Adiciona evento/interação à sessão
# Chamado automaticamente pelo Runner
async def delete_session(...)
# Cleanup de sessão expirada
async def list_sessions(...)
# Lista todas as sessões ativas do usuário
async def cleanup_expired_sessions(...)
# Remove sessões antigas (>TTL)
Armazenamento Firestore:
Collection: agente_busca_produtos_sessions
Document: {session_id}
- id: string
- user_id: string
- app_name: string
- created_at: timestamp
- updated_at: timestamp
- expires_at: timestamp
- messages: [
{role, parts, timestamp},
{role, parts, timestamp},
...
]
2. FirestoreMemoryService (session/memory_service.py)¶
Gerencia conhecimento de longo prazo:
class FirestoreMemoryService(BaseMemoryService):
# Métodos principais:
async def add_session_to_memory(session: Session)
# Extrai conhecimento da sessão completa
# Armazena para futuras referências
# Chamado ao final de cada conversa
async def search_memory(app_name, user_id, query)
# Busca informações relevantes no histórico
# Busca por palavras-chave
# Retorna resultados relevantes
async def delete_memory(app_name, user_id)
# Remove toda a memória de um usuário
# Cleanup completo
Armazenamento Firestore:
Collection: agente_busca_produtos_memory
Document: {user_id}_{timestamp}
- app_name: string
- user_id: string
- session_id: string
- created_at: timestamp
- content: string (extracted knowledge)
- event_count: number
3. Slack Bot Integration (slack_bot.py)¶
Integrações principais:
# Configuração
session_service = FirestoreSessionService(...)
memory_service = FirestoreMemoryService(...)
runner = Runner(
agent=root_agent,
session_service=session_service,
memory_service=memory_service
)
# Handler de mensagens
@app.message(".*")
async def handle_message(message, say, context, ack, client):
# 1. Rate limiting check
# 2. Criar session_id único
# 3. Enviar feedback em tempo real
# 4. Executar Agent com runner.run_async()
# 5. Tratar erros 503/429
# 6. Salvar memória após conversa
# 7. Responder no Slack
4. Root Agent (agent.py)¶
Agent inteligente com acesso completo:
root_agent = LlmAgent(
name='root_agent',
model=llm_with_retry, # Com retry automático
instruction="...", # Instruções detalhadas
tools=[
busca_produtos_tool,
tem_variacao_tool,
listar_variacoes_tool,
detalhes_experience,
disponibilidade_calendario_tool,
disponibilidade_horarios_tool,
calcular_preco_tool,
gerar_csv_tool
]
)
🔄 Fluxo de Funcionamento¶
Primeira Interação¶
1. Slack: Usuário envia mensagem
↓
2. slack_bot.py:
- Rate limit check ✅
- Cria session_id = "user123_slack_channel456"
↓
3. runner.run_async():
- Chama session_service.get_session()
- Retorna Session (primeira vez = vazia)
↓
4. root_agent:
- Processa mensagem
- Chama ferramentas conforme necessário
↓
5. runner:
- Chama session_service.append_event() [x2]
- Salva interações no Firestore
↓
6. slack_bot.py:
- Recupera session completa
- Chama memory_service.add_session_to_memory()
- Responde no Slack
Interação Subsequente (mesma sessão)¶
1. Slack: Usuário envia nova mensagem
↓
2. runner.run_async():
- Chama session_service.get_session()
- Retorna Session COM HISTÓRICO CARREGADO ✅
↓
3. root_agent:
- VÊ mensagens anteriores
- Mantém continuidade da conversa
- Respostas contextualizadas
↓
... (resto do fluxo igual)
Interação em Sessão Futura (usuário novo)¶
1. Slack: Usuário pergunta "Você lembra do que falamos antes?"
↓
2. runner.run_async():
- Cria nova Session (diferente channel/dia)
- Session vazia (primeira da nova sessão)
↓
3. root_agent:
- Pode chamar memory_service.search_memory()
- Busca no histórico armazenado
- Encontra contexto de conversas passadas
- Responde com informações do passado ✅
↓
... (resto do fluxo igual)
📊 Dados Persistidos¶
Firestore - Collection 1: Sessions¶
agente_busca_produtos_sessions/user123_slack_channel456:
{
"id": "user123_slack_channel456",
"user_id": "user123",
"app_name": "ifriend_busca_produtos_slack",
"created_at": "2025-11-17T10:00:00Z",
"updated_at": "2025-11-17T10:05:30Z",
"expires_at": "2025-11-17T11:00:00Z",
"messages": [
{
"role": "user",
"parts": [{"text": "Quais produtos vocês têm de viagem?"}],
"timestamp": "2025-11-17T10:00:05Z"
},
{
"role": "assistant",
"parts": [{"text": "Encontrei 5 produtos de viagem..."}],
"timestamp": "2025-11-17T10:00:15Z"
},
{
"role": "user",
"parts": [{"text": "Qual é o preço do primeiro?"}],
"timestamp": "2025-11-17T10:00:20Z"
},
{
"role": "assistant",
"parts": [{"text": "O primeiro produto custa R$ 299..."}],
"timestamp": "2025-11-17T10:00:30Z"
}
]
}
Firestore - Collection 2: Memory¶
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:05:35Z",
"content": "P: Quais produtos vocês têm de viagem?\n\nEncontrei 5 produtos de viagem...\n\nP: Qual é o preço do primeiro?\n\nO primeiro produto custa R$ 299...",
"event_count": 4
}
🧪 Testes Inclusos¶
Test Files¶
tests/test_session_service.py- Testes de persistênciatest_session_events_loaded_on_get()- Valida carregamento de eventostest_append_event_saves_to_firestore()- Valida salvamento de eventos
Execute com:
cd busca_produtos
python -m pytest tests/test_session_service.py -v
🚀 Deployment¶
Cloud Run - Deploy¶
cd busca_produtos
./deploy-test.sh
Configurações automáticas: - ✅ Vertex AI habilitado (GOOGLE_GENAI_USE_VERTEXAI=1) - ✅ Rate limiting configurado - ✅ Firestore inicializado - ✅ Slack bot conectado
Verificação¶
curl https://agente-busca-produtos-slack-REGION.run.app/health
Esperado:
{
"status": "healthy",
"service": "ifriend_busca_produtos_slack",
"slack_configured": true
}
📈 Métricas & Monitoramento¶
Logs (Cloud Logging)¶
✅ "ADK Runner inicializado com FirestoreSessionService + FirestoreMemoryService"
📝 "Sessão criada: user123_slack_channel456"
🧠 "Memória salva para user123 (4 eventos)"
✅ "Enviando resposta ao Slack"
Firestore Metrics¶
- Sessions Collection: Crescimento linear com conversas
- Memory Collection: Crescimento linear com conversas completadas
- TTL: Sessions expiram em 60 minutos
🎯 Casos de Uso Habilitados¶
1. Contexto na Mesma Conversa ✅¶
User: "Qual é o preço?"
Agent: [busca no Firestore] "R$ 299"
User: "Você já calculou isso?"
Agent: [vê session.messages] "Sim, foi R$ 299" ✅
2. Personalizacão Entre Conversas ✅¶
Sessão 1 (Dia 1):
User: "Gosto de viagem"
Agent: Recomenda produtos de viagem
Sessão 2 (Dia 7):
User: "Me recomende algo"
Agent: [busca em memory_service] "Lembro que você gosta de viagem..."
3. Escalabilidade ✅¶
- Múltiplos usuários simultâneos
- Rate limiting previne sobrecarga
- Firestore escala automaticamente
- Sessions TTL evita crescimento infinito
📚 Documentação Associada¶
- PASSO_1_SESSION_EVENTS.md - Detalhes de persistência de sessão
- PASSO_2_MEMORY_SERVICE.md - Detalhes de memória de longo prazo
- FIREBASE_CONTEXT_SOLUTIONS.md - Soluções anteriores (referência)
🔍 Diagrama de Sequência¶
Slack Bot Runner SessionService MemoryService Firestore
| | | | | |
|--msg-------->| | | | |
| |--check---->| | | |
| | rate limit | | | |
| |<--ok-------| | | |
| | |--get_session()---> | |
| | | |--load from DB----------> |
| | | |<--return session-------| |
| | | | | |
| | |--run_agent()---| | |
| | | [tool calls] | | |
| | | | | |
| | |--append_event()| | |
| | | |--save to DB----------> |
| | |<--done---------| | |
| | | | | |
| |--add_to_memory()------------| | |
| | |--save knowledge-----------> |
| | | | |
|<--response---| | | |
| | | | |
✅ Checklist Final¶
- ✅ FirestoreSessionService implementado
- ✅ FirestoreMemoryService implementado
- ✅ Rate limiting funcional
- ✅ Error handling (503/429)
- ✅ Vertex AI integrado
- ✅ Slack bot conectado
- ✅ Firestore persistência
- ✅ Testes inclusos
- ✅ Documentação completa
- ✅ Deploy automático
🎉 Resultado¶
O Agent agora: - Mantém contexto na mesma conversa ✅ - Lembra de conversas passadas ✅ - Responde a "Você lembra?" com histórico ✅ - Não fica sobrecarregado com rate limiting ✅ - Trata erros elegantemente ✅ - Escala bem com crescimento ✅
Status: PRONTO PARA PRODUÇÃO 🚀