Skip to content

5. Sessão, Memória e Autenticação

Visão geral dos backends

O projeto usa um Factory Pattern para criar session e memory services a partir de env vars:

flowchart TD
    ENV["SESSION_BACKEND=redis<br/>MEMORY_BACKEND=cloudsql"]

    ENV --> SF["get_session_service(backend)"]
    ENV --> MF["get_memory_service(backend)"]

    SF --> |"redis"| REDIS_S["RedisSessionService<br/><i>~1-5ms latência</i>"]
    SF --> |"cloudsql"| CSQL_S["DatabaseSessionService<br/><i>~100-800ms latência</i>"]
    SF --> |"inmemory"| MEM_S["InMemorySessionService<br/><i>~0ms (dev only)</i>"]

    MF --> |"cloudsql"| CSQL_M["CloudSQLMemoryService<br/><i>MySQL persistente</i>"]
    MF --> |"redis"| REDIS_M["RedisMemoryService<br/><i>Cache rápido</i>"]
    MF --> |"openmemory"| OM["OpenMemoryService<br/><i>Vetorial (pgvector)</i>"]

Factory definida em ifriend_agent/config/backends.py.


Sessions (estado da conversa)

A session mantém o estado atual da conversa de um usuário: histórico de mensagens, contexto JWT, metadata.

Backends disponíveis

Backend Classe Latência Quando usar
Redis RedisSessionService ~1-5ms Produção (recomendado)
CloudSQL ADK DatabaseSessionService ~100-800ms Produção (persistência SQL)
InMemory ADK InMemorySessionService ~0ms Dev local / testes

O que é salvo na session

flowchart TD
    SESSION["Session"]

    SESSION --> STATE["state (dict)"]
    SESSION --> EVENTS["events (lista)"]

    STATE --> JWT["jwt_context<br/>{user_id, email, roles,<br/>partner_code, token, token_exp}"]
    STATE --> DATE["current_date<br/>'2026-03-11 10:30:00'"]
    STATE --> META["message_metadata<br/>{platform, channel_id, thread_id}"]
    STATE --> MEMS["memories<br/>[memórias carregadas]"]

    EVENTS --> E1["UserContent: 'Quero um tour em Roma'"]
    EVENTS --> E2["FunctionCall: busca_produtos(...)"]
    EVENTS --> E3["FunctionResponse: {resultados...}"]
    EVENTS --> E4["AgentContent: 'Encontrei 3 opções...'"]

Ciclo de vida da session

sequenceDiagram
    participant CP as ConversationProcessor
    participant SS as SessionService

    CP->>SS: get_session(app_name, user_id)
    alt Session existe
        SS-->>CP: Session existente (com state e eventos)
    else Session não existe
        CP->>SS: create_session(app_name, user_id, state={})
        SS-->>CP: Nova Session
    end

    Note over CP: ADK Runner processa mensagem
    Note over CP: State é atualizado (jwt, date, etc.)

    CP->>SS: Session é salva automaticamente pelo Runner

Memory (memória de longo prazo)

A memória persiste entre sessões — o agente "lembra" de conversas anteriores do usuário.

Backends disponíveis

Backend Classe Uso
CloudSQL CloudSQLMemoryService Persistência MySQL (produção)
Redis RedisMemoryService Cache rápido com TTL
OpenMemory OpenMemoryService Memória vetorial com pgvector

Fluxo da memória

flowchart LR
    subgraph "Automático — PreloadMemoryTool"
        LOAD["PreloadMemoryTool<br/><i>FULLTEXT search com query do usuário<br/>Executa antes de cada chamada LLM</i>"]
    end

    subgraph "Processamento"
        AGENT["Root Agent + Sub-Agentes<br/><i>Usa memórias como contexto<br/>via PAST_CONVERSATIONS</i>"]
    end

    subgraph "After Agent (callback)"
        SAVE["save_session_to_memory_callback<br/><i>Sumariza conversa via LLM<br/>UPSERT no CloudSQL</i>"]
    end

    LOAD --> AGENT --> SAVE

    DB[(CloudSQL / MySQL<br/>FULLTEXT index)] --> LOAD
    SAVE --> DB

Autenticação JWT

O projeto trabalha com dois tipos de JWT:

flowchart TD
    subgraph "JWT Sistêmico 🔑"
        SYS_DESC["Credenciais da API iFriend<br/>(API_EMAIL + API_PASSWORD)"]
        SYS_USE["Usado para: operações admin,<br/>emitir reservas no sistema"]
        SYS_MGR["Gerenciado por: AuthTokenManager<br/>(ifriend_agent/tools/booking/auth.py)"]
    end

    subgraph "JWT de Usuário 👤"
        USR_DESC["Token do frontend/plataforma<br/>(passado pelo usuário logado)"]
        USR_USE["Usado para: personalizar preços,<br/>identificar agência/operadora"]
        USR_MGR["Gerenciado por: JWTContextManager<br/>(ifriend_agent/session/jwt_context.py)"]
    end

Como o JWT chega ao agente

flowchart TD
    subgraph "WebChat / SSE"
        WC["Header: Authorization: Bearer {token}<br/>ou body.jwt_token"]
    end

    subgraph "WhatsApp / Slack"
        WA["JWT Lookup via Redis"]
        WA_LINK["Passo 1: POST /whatsapp/link {phone, jwt}<br/>ou /ifriend-auth no Slack"]
        WA_GET["Passo 2: Mensagem chega → busca JWT<br/>no Redis pelo user_id"]
        WA_LINK --> WA_GET
    end

    WC --> PARSE["Adapter.parse_webhook_request()<br/>extrai jwt_token"]
    WA_GET --> PARSE

    PARSE --> PROC["ConversationProcessor<br/>decodifica JWT"]
    PROC --> STATE["session.state['jwt_context']"]
    STATE --> TOOLS["Tools acessam via<br/>tool_context.state.get('jwt_context')"]

JWT Lookup (WhatsApp/Slack)

Como WhatsApp e Slack não suportam headers personalizados, usamos um serviço de lookup no Redis:

sequenceDiagram
    actor Frontend as Frontend/App
    participant API as FastAPI
    participant Redis as Redis

    Note over Frontend,Redis: Passo 1: Vincular JWT ao user_id
    Frontend->>API: POST /whatsapp/link {phone: "+5511...", jwt: "eyJ..."}
    API->>Redis: SET jwt_lookup:+5511... = "eyJ..."  (TTL: 30 dias)
    API-->>Frontend: OK

    Note over Frontend,Redis: Passo 2: Mensagem chega
    actor User as Usuário WhatsApp
    User->>API: Mensagem via webhook
    API->>Redis: GET jwt_lookup:+5511...
    Redis-->>API: "eyJ..." (token)
    API->>API: Decodifica JWT → jwt_context
    Note over API: Segue fluxo normal com jwt_context no state

Componentes de autenticação

Componente Arquivo Responsabilidade
JWTContextManager ifriend_agent/session/jwt_context.py Decodifica/valida tokens (HS256 e RS256)
JWTLookupService ifriend_agent/session/jwt_lookup_service.py Store/get JWT no Redis (WhatsApp/Slack)
SessionContextHelper ifriend_agent/session/session_context.py Helper para gerenciar JWT em sessões ADK
AuthTokenManager ifriend_agent/tools/booking/auth.py JWT sistêmico (login API_EMAIL/API_PASSWORD)
UserContext Dataclass Struct: user_id, email, name, roles, token
inject_jwt_before_agent ifriend_agent/callbacks/jwt_context_callback.py Callback que garante JWT no state

Enriquecimento de contexto

O JWT do usuário permite personalizar a experiência:

flowchart LR
    JWT["jwt_context<br/>{partner_code: 'AGENCIA_XYZ'}"]

    JWT --> HEADERS["enrich_headers.py<br/>Adiciona X-Context-Partner<br/>nos requests à API"]
    JWT --> PRICES["enrich_products.py<br/>Calcula preços com<br/>markup da agência"]

    HEADERS --> API["API iFriend<br/>(responde com dados personalizados)"]
    PRICES --> USER["Usuário vê preço<br/>com markup aplicado"]

Variáveis de ambiente JWT

Env Var Descrição
JWT_SECRET_KEY Chave para HS256
JWT_ALGORITHM HS256 ou RS256
JWT_PUBLIC_KEY Chave pública para RS256
JWT_PASSPHRASE Passphrase da chave (se aplicável)
JWT_ISSUER Issuer esperado no token
JWT_AUDIENCE Audience esperada
JWT_VERIFY_EXPIRATION true/false — verificar expiração
JWT_LOOKUP_TTL_DAYS TTL do lookup no Redis (padrão: 30)

Resumo visual: session + memory + JWT

flowchart TD
    MSG["Mensagem do usuário"] --> ADAPTER["Adapter<br/>extrai jwt_token"]
    ADAPTER --> PROC["ConversationProcessor"]

    PROC --> |"decodifica"| JWT["JWTContextManager"]
    JWT --> |"jwt_context"| STATE["session.state"]

    PROC --> |"get/create"| SS["SessionService<br/>(Redis)"]
    SS --> STATE

    STATE --> AGENT["Root Agent<br/>(Orquestrador)"]

    AGENT --> |"PreloadMemoryTool<br/>FULLTEXT search"| MM["MemoryService<br/>(CloudSQL)"]
    MM --> |"memórias injetadas<br/>como PAST_CONVERSATIONS"| AGENT

    AGENT --> |"transfer_to_agent"| SUB["Sub-Agentes<br/>Especializados"]
    SUB --> AGENT

    AGENT --> AFTER["After Callback<br/>save_session_to_memory"]
    AFTER --> |"resumo LLM + UPSERT"| MM

Anterior: ← Adaptadores de Mensageria · Próximo: Setup Local e Desenvolvimento →