A janela de contexto é memória de curto prazo. O que falta é memória de longo prazo: um repositório persistente que sobrevive ao fim da sessão, escala para anos de interação e permite recuperar factos por conteúdo, tempo e utilizador.
A equipa da Elastic publicou esta semana um artigo técnico essencial sobre como construir memória persistente para agentes de IA usando Elasticsearch. O autor, Noam Schwartz, detalha a arquitetura, as decisões de design e os resultados — R@10 de 0.89 e zero fugas entre tenants.
Vou resumir os pontos-chave e dar a minha opinião sobre o que isto significa para o ecossistema de agentes de IA.
O Problema: A Janela de Contexto Não é Memória
O truque habitual para dar "memória" a um agente é enfiar o histórico todo na janela de contexto. Isto parte-se em três pontos:
- Custo — tokens custam dinheiro, e contextos enormes pagam-se caro
- Latência — janelas de 1M tokens tornam cada inferência mais lenta
- Lost in the Middle — o modelo ignora factos no meio do contexto, um fenómeno bem documentado
Como diz o artigo: "Uma janela de contexto de 1M tokens é um bloco de notas. Não é um sistema de memória."
A Arquitetura: Três Tipos de Memória
Inspirado na psicologia cognitiva (a framework COALA), a memória do agente é dividida em três categorias, cada uma com o seu índice Elasticsearch:
1. Memória Episódica
Eventos com timestamp — cada interação do utilizador tal como acontece, antes de qualquer extração ou interpretação. A maior parte é de curta duração. Algumas entradas tornam-se evidência para factos duráveis mais tarde.
2. Memória Semântica
Factos destilados e estáveis sobre o utilizador. "A Sarah tem um Lumio Hub v2. A Sarah está no iOS 17.4. O hub da Sarah foi reiniciado em Março." Sobrevivem entre sessões e são a base de conhecimento do agente.
3. Memória Procedimental
Guias de resolução de problemas com vários passos. "Como resolver desligamentos Zigbee." Cada guia tem success_count e failure_count que são incrementados conforme o utilizador confirma se a solução funcionou ou não.
O Pipeline de Retrieval: Híbrido + Reranker
A recuperação de memória usa retrieval híbrido em dois estágios:
BM25 + Vectors (Jina v5) — 80 candidatos por via, fusionados com RRF (Reciprocal Rank Fusion,
rank_constant=30). A BM25 captura correspondências literais (números de versão, códigos de erro). Os vectores densos capturam o sentido semântico de perguntas que usam palavras diferentes.Cross-encoder reranker (Jina v2) — reordena os candidatos fusionados, com atenção total entre o par pergunta-documento. Mais caro por par, mas um sinal de relevância muito mais forte.
Um detalhe subtil: o agente parafraseia a pergunta do utilizador antes de a usar, o que strip números de versão e nomes próprios. Por isso, cada turno abre com uma chamada automática ao índice com a mensagem verbatim do utilizador, antes da paráfrase do agente a ver.
Supersessão: Quando os Factos Mudam
Memória que só adiciona, nunca remove, acaba errada. O utilizador diz "Mudei-me para Edimburgo"; o agente escreve um novo facto. Seis meses depois, o facto antigo "vive em Bristol" ainda está no índice. Ambos aparecem em cada recall.
A solução não é apagar (delete), é superseder: o novo facto marca o antigo com superseded_by e o recall aplica um filtro must_not exists field=superseded_by. O documento antigo permanece no índice para auditoria, mas fica invisível para o agente.
Utilizador: "Saímos de Bristol, estamos em Edimburgo agora."
→ Recall devolve {id: abc, texto: "Sarah vive em Bristol"}
→ Agente detecta conflito, classifica como "natural"
→ write_memory(texto="Sarah vive em Edimburgo", supersedes_id="abc")
→ Novo doc aparece, antigo fica com superseded_by=xyz
→ Recall já não mostra o antigo
Se o utilizador quiser explicitamente que o agente se lembre do histórico (ex: "onde é que já vivi?"), pode usar include_superseded=True.
Time Decay e Use-Count
Nem todos os factos devem ter o mesmo peso. Factos antigos ou raramente usados devem pontuar menos.
- Time decay — multiplicador em forma de gaussiana sobre a data do facto. Offset de 180 dias (factos recentes têm score 1.0), scale de 1825 dias (~5 anos, meia-vida).
- Use-count boost — factos frequentemente recuperados recebem um boost de
1 + log10(1 + use_count) * weight.
Cada vez que o agente recupera um facto semântico, o last_used_at é atualizado. Isto transforma a decadência temporal numa decadência de relevância: factos que o agente não precisa há muito tempo perdem peso, não porque sejam falsos, mas porque são menos relevantes.
Isolamento Multi-Tenant com DLS
Num sistema com múltiplos utilizadores, cada um só pode ver a sua própria memória. A solução é Document-Level Security (DLS) do Elasticsearch.
Cada utilizador recebe uma API key cujo role descriptor inclui uma query DLS que só admite documentos desse utilizador (e do catálogo partilhado, que não tem campo user_id). O cluster simplesmente não devolve documentos de outros tenants. A implementação inclui também um filtro user_id a nível de código como "paranoia pass" contra config drift.
O catálogo partilhado (knowledge base, specs de produtos) entra na mesma query: o DLS alarga-se para aceitar user_id == "sarah" OR must_not exists: user_id.
Ligar Qualquer Agente via MCP
A camada de memória não está presa a um agente específico. O endpoint é /api/atlas/mcp/{user_id}, compatível com o Model Context Protocol (MCP). Qualquer cliente MCP (Claude Desktop, Cursor, ou o teu próprio agente) pode ligar-se e usar as três ferramentas: recall_memory, write_memory, forget_memory.
Resultados
- R@10: 0.89 (média em 168 perguntas)
- R@5: 0.75
- Zero fugas entre tenants (ship gate: o requisito obrigatório para qualquer sistema multi-tenant)
- Memória semântica é o caso mais difícil (R@10 ~0.81) devido a colisões entre factos irmãos — perguntas sobre o hub da Sarah que têm vários factos plausíveis no corpus
- Memória episódica: 0.98
- Memória procedimental: 1.0
O código completo está no GitHub: github.com/noamschwartz/atlas-memory-demo
O Que Isto Significa
Este artigo é importante porque resolve um problema real e imediato dos agentes de IA: a falta de memória persistente. A abordagem é pragmática:
- Usa o que já existe — Elasticsearch, não um sistema novo e exótico
- Separa por tipo de memória — reconhece que eventos, factos e procedimentos têm ciclos de vida diferentes
- Híbrido + reranker — não depende de uma única técnica de retrieval
- MCP — não amarra a memória a um runtime específico
O que mais gosto é da supersessão com pista de auditoria. Num contexto empresarial, saber que um facto foi alterado, quando, e qual era o valor anterior, não é um extra — é um requisito legal (GDPR, auditoria, compliance). A abordagem "nunca apagar, superseder" resolve isto de forma elegante.
E o DLS no Elasticsearch faz o isolamento multi-tenant ao nível do cluster, não ao nível do código. Isto reduz drasticamente a superfície de erro: um bug no código do agente não provoca uma fuga de dados se o cluster não devolver documentos de outros tenants.
O senão? O refresh síncrono (refresh=True) por cada escrita não escala para dezenas de milhar de utilizadores simultâneos. O autor reconhece-o e sugere uma solução (register "acabado de escrever" no contexto do LLM até o índice assentar). Para produção a sério, vai ser preciso resolver este ponto.
No geral, é uma arquitetura sólida que qualquer equipa a construir agentes de IA devia estudar. A memória persistente é o ingrediente que falta para passar de agentes de sessão única para assistentes que realmente conhecem o utilizador ao longo do tempo.
Comentários (0)
Nenhum comentário ainda. Seja o primeiro!
Deixar comentário