~ / tutoriais /memoria-agente-assistente-compras $ _

Memória de agente: por que seu assistente de compras esquece o usuário (e como consertar)

Lucas Souza Lucas Souza 12 min de leitura Tutoriais
Memória de agente: por que seu assistente de compras esquece o usuário (e como consertar)

Você está construindo um assistente de ofertas. O usuário diz, no terceiro turno, "não me oferece nada da Apple, eu odeio o ecossistema deles". Cinco minutos depois, na mesma conversa, ainda funciona. Volta no dia seguinte, abre uma nova sessão, pergunta "achou um celular bom por menos de 3 mil?" e o agente responde, sorridente: "Olha que oferta, iPhone 15 por R$ 4.299".

Isso não é falha do modelo. É falha de arquitetura.

Agente sem memória persistente é um peixinho dourado caro. Cada turno começa do zero, ou quase. Preço de referência que o usuário citou ontem, marcas que ele rejeitou, faixa de orçamento, tamanho do filho que ele está vestindo, voltagem do estado dele, tudo isso volta a ser desconhecido. Neste post a gente disseca por que isso acontece, mostra as três camadas de memória (curto, médio e longo prazo) e aplica cada uma ao caso concreto de um assistente de ofertas.

TL;DR

  • O que é: arquitetura de memória em três camadas (contexto, sumário e memória vetorizada por usuário) aplicada a um assistente de compras.
  • Stack/Modelos: Claude Sonnet 4.6 (memory tool), LangGraph (store + checkpointer), Mem0 (memory layer), pgvector para o long-term.
  • Custo/Acesso: memory tool gratuita na API Claude, LangGraph e Mem0 open-source, pgvector grátis.
  • Repositório/Link útil: Anthropic memory tool e Mem0 no GitHub.

O contexto: por que ainda criamos agentes amnésicos?

A maioria dos times monta o assistente de compras do jeito mais rápido. Pega a Chat Completions, joga as últimas N mensagens dentro do prompt, manda. Funciona na demo. Quebra em produção.

Quebra porque "memória" virou uma palavra de marketing. Todo provedor diz que "tem memória". Anthropic lançou a memory tool baseada em arquivos client-side. OpenAI tem Threads na Assistants API que persistem mensagens. Google tem context cache. Mas isso são primitivas, não arquitetura.

A pesquisa séria já mapeou isso. O paper Episodic Memory is the Missing Piece for Long-Term LLM Agents de 2025 deixa claro que sistemas reais precisam de tipos diferentes de memória, com políticas de escrita, leitura e expiração distintas. A documentação oficial do LangGraph divide explicitamente memória semântica (fatos), episódica (experiências) e procedural (instruções).

Ou seja: o problema não é falta de tooling. É falta de design.

"Context engineering refers to the set of strategies for curating and maintaining the optimal set of tokens (information) during LLM inference." Anthropic, Effective context engineering for AI agents.

Memória de agente é, no fundo, decidir o que entra no contexto, quando entra, e o que sai dele.

Pré-requisitos e ferramentas

Antes de implementar:

  • [ ] Chave de API da Anthropic (ou OpenAI). Vamos usar Claude nos exemplos.
  • [ ] Conhecimento básico de chamadas a LLM com tool use.
  • [ ] Banco com suporte a vetor (vamos usar PostgreSQL + pgvector).
  • [ ] Ambiente: Python 3.11+ ou PHP 8.3+ se preferir Laravel.
  • [ ] (Opcional) Mem0 ou LangGraph se quiser pular o boilerplate.

As três camadas de memória

Para um assistente de compras, pensa assim. Tem três janelas temporais distintas, e cada uma tem trabalho diferente.

Curto prazo: o contexto da conversa

É o que está dentro da janela de contexto do modelo agora. Histórico de mensagens, ferramentas chamadas, resultados que voltaram. Em LangGraph isso é estado thread-scoped, persistido por um checkpointer. Em Claude/OpenAI puros, é a lista de mensagens que você envia em cada call.

Função no assistente de compras: lembrar o que foi dito neste atendimento. "Quero algo até R$ 500", "prefiro entregas via SP", "esse aí já me mostrou".

Limite: explode com o tempo. 100 mil tokens parece muito até o agente fazer 40 buscas de produto e a janela ficar 80% lixo.

Médio prazo: o sumário rolante

Quando a conversa cresce, você não quer mandar 200 mensagens crus para o modelo. A Anthropic chama isso de compaction: pegar a conversa que está chegando perto do limite, resumir, e reiniciar a janela com o resumo. A documentação descreve a técnica como "summarizing its contents, and reinitiating a new context window with the summary".

Função no assistente: comprimir 30 minutos de conversa em "usuário busca tênis de corrida masculino 42, gosta de Asics e Mizuno, rejeita Nike, faixa de R$ 400 a 700, frete para Salvador".

Esse sumário ainda é desta sessão. Ele morre quando a sessão morre, a não ser que você o promova para a próxima camada.

Longo prazo: memória vetorizada por usuário

Aqui está onde a maioria dos times falha. Long-term memory é o que sobrevive entre sessões, é específica do usuário, e precisa ser recuperável por similaridade semântica, não por chave exata.

A documentação do LangGraph descreve memória de longo prazo como "saved within custom 'namespaces'" e que pode ser "recalled at any time and in any thread". O namespace típico é (user_id, application_context), e dentro dele você guarda fatos individuais como documentos JSON com embedding.

Em produção, um padrão comum é o do Mem0: combinar vector store, graph database e key-value, usar três passes de scoring em paralelo (semântica, keyword, entidade) e fundir os resultados. A equipe deles publicou um benchmark mostrando 26% mais acurácia que a memória nativa da OpenAI, com latência menor.

Função no assistente: lembrar para sempre que este usuário odeia Apple, que ele tem um filho de 8 anos chamado Pedro que veste tamanho 10, que ele já gastou R$ 11 mil no Black Friday passado em eletrônicos, que ele só compra com frete grátis.

Camada Escopo Tecnologia típica Quando escrever
Curto prazo turno e sessão array de mensagens, checkpointer a cada turno
Médio prazo sessão sumário gerado por LLM quando contexto > N tokens
Longo prazo cross-session, por usuário vetor + KV (pgvector, Mem0) quando fato é "estável" sobre o usuário

Mão na massa: aplicando ao assistente de ofertas

Vamos sair do abstrato. Três blocos de código, um para cada camada.

Passo 1: curto prazo com a memory tool da Anthropic

A memory tool da Anthropic é client-side. Ela dá ao modelo seis comandos (view, create, str_replace, insert, delete, rename) sobre um diretório /memories que você decide onde armazenar. Funciona ótimo como nota de sessão estruturada.

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-4-7",
    max_tokens=2048,
    tools=[{"type": "memory_20250818", "name": "memory"}],
    system=(
        "Você é um assistente de ofertas. Antes de recomendar qualquer "
        "produto, leia /memories/sessao_atual.md para lembrar restrições, "
        "marcas rejeitadas e orçamento já mencionados pelo usuário."
    ),
    messages=[
        {"role": "user", "content": "Achou um celular bom por menos de 3 mil?"}
    ],
)

O modelo automaticamente chama view /memories antes de responder. Você implementa o handler do lado da sua aplicação. Um arquivo no disco, uma row em Postgres, um blob em S3, tanto faz. A docs oficial inclusive avisa: "Malicious path inputs could attempt to access files outside the /memories directory. Your implementation MUST validate all paths to prevent directory traversal attacks". Não é só por estética, é segurança.

Passo 2: médio prazo com sumário rolante

Quando o histórico cresce, você dispara um agente sumarizador. Esquema simples:

SUMMARIZE_PROMPT = """
Resuma a conversa abaixo focando em:
- Categoria do produto procurado
- Faixa de orçamento mencionada
- Marcas preferidas
- Marcas rejeitadas
- Restrições de entrega (cidade, frete, prazo)
- Produtos já mostrados (não repita)

Saída em JSON com essas chaves. Máximo 200 palavras.
"""

def compact_session(messages: list[dict]) -> dict:
    response = client.messages.create(
        model="claude-haiku-4-5-20251001",
        max_tokens=512,
        system=SUMMARIZE_PROMPT,
        messages=[{"role": "user", "content": serialize(messages)}],
    )
    return json.loads(response.content[0].text)

Dois detalhes importantes. Primeiro: use o modelo barato para sumário (Haiku). Segundo: estruture a saída em JSON, não em texto livre. Sumário em prosa some no contexto; JSON o agente consulta como se fosse uma tabela.

Passo 3: longo prazo com pgvector por usuário

Agora a parte que muda o jogo. Quando algo aparece na conversa que é estável sobre o usuário, preferência declarada, restrição forte, evento de vida, você grava no longo prazo.

Schema mínimo no Postgres:

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE user_memories (
    id          BIGSERIAL PRIMARY KEY,
    user_id     BIGINT NOT NULL,
    kind        TEXT NOT NULL,
    fact        TEXT NOT NULL,
    embedding   VECTOR(1536) NOT NULL,
    created_at  TIMESTAMPTZ DEFAULT NOW(),
    last_used_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX user_memories_user_idx ON user_memories (user_id);
CREATE INDEX user_memories_embedding_idx
    ON user_memories USING ivfflat (embedding vector_cosine_ops);

Escrita: extrair fatos da conversa (idealmente com um agente "memory writer") e gravar com embedding.

def write_memory(user_id: int, fact: str, kind: str = "preference"):
    embedding = embed(fact)
    db.execute(
        "INSERT INTO user_memories (user_id, kind, fact, embedding) "
        "VALUES (%s, %s, %s, %s)",
        (user_id, kind, fact, embedding),
    )

Leitura: na entrada de cada nova sessão, recupere por similaridade ao que o usuário acabou de pedir.

def recall_memories(user_id: int, query: str, k: int = 5) -> list[str]:
    q = embed(query)
    rows = db.fetch_all(
        "SELECT fact FROM user_memories "
        "WHERE user_id = %s "
        "ORDER BY embedding <=> %s "
        "LIMIT %s",
        (user_id, q, k),
    )
    return [r["fact"] for r in rows]

E injete o resultado no system prompt do agente:

memories = recall_memories(user_id=42, query="celular barato")
system = (
    "Você é um assistente de ofertas. Fatos conhecidos sobre este usuário:\n"
    + "\n".join(f"- {m}" for m in memories)
)

Pronto. Agora, quando o usuário pergunta "achou um celular bom por menos de 3 mil?", o agente vê na hora: "este usuário rejeita marcas Apple", "este usuário compra majoritariamente acima de R$ 2.500", "este usuário pediu frete grátis nas últimas 4 compras".

Ele para de oferecer iPhone.

Limitações e pontos de atenção

Antes de sair plugando memória em tudo:

Memória rica vira veneno. Se você gravar todo turno como fato, vai poluir o long-term com ruído. O paper Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory mostra que extração + deduplicação são tão importantes quanto o vetor. Crie um agente "memory writer" separado, com prompt restritivo, que decide o que merece persistir.

Privacidade. Memória de usuário é dado pessoal. LGPD se aplica. Implemente "esquecer-me" de verdade: delete linhas, não esconda. Considere expiração automática para fatos antigos. A Anthropic recomenda explicitamente "clearing out memory files periodically that haven't been accessed in an extended time".

Memória errada é pior que falta de memória. Se o agente lembrar "usuário odeia Apple" porque uma vez ele reclamou do iOS, e na verdade ele só estava de mau humor, você acabou de criar um bug invisível. Deixe o usuário inspecionar e editar a memória dele, nem que seja por uma rota admin.

Path traversal na memory tool. Reforço o aviso da docs oficial: valide paths, resolva canônicos, rejeite ../. Não confie no modelo.

Custo. Cada turno agora faz uma busca vetorial e infla o prompt com fatos recuperados. Cacheie embeddings de queries frequentes e use prompt caching da Anthropic para a parte fixa do system prompt.

Quer construir produtos de IA com essa profundidade?

Memória de agente é um daqueles assuntos que separa quem brincou com prompt de quem entrega IA em produção. Não tem fórmula mágica, tem arquitetura. Tem decisão de quando escrever, o que escrever, como recuperar, como expirar e como deixar o usuário no controle.

Esse é exatamente o tipo de problema que a gente destrincha dentro do Clã Beer & Code. Recomendação, busca semântica, agentes, RAG, memória, tudo aplicado em projetos reais com PHP/Laravel, Python e a stack moderna de IA. Se você quer parar de só consumir IA e começar a construir produto com ela, vem para o Clã.

FAQ rápido

Posso usar só a memory tool da Anthropic e pular o pgvector? Para um único usuário e workflows internos, sim. Para um SaaS multi-tenant com milhões de fatos, não. A memory tool é ótima para nota estruturada de sessão; vetor por usuário é o caminho para escala. Combine os dois.

Mem0 substitui o LangGraph? Não, eles operam em camadas diferentes. LangGraph é orquestração de agentes (state machine, checkpointer, sub-agentes); Mem0 é a camada de memória. A integração oficial Mem0 + LangGraph está documentada e é o caminho mais curto para produção.

Por que não usar só Threads da OpenAI? Threads persistem mensagens, mas não compartilham memória entre threads. Cada conversa nova começa amnésica do mesmo usuário. Para shopping isso é fatal: o cliente não quer reapresentar todas as preferências em cada sessão.

Embedding de qual dimensão eu uso? Para começar, text-embedding-3-small da OpenAI (1536 dims) ou voyage-3-lite da Voyage. Não otimize cedo. Mude quando tiver evidência de que a recuperação está fraca.

Conclusão

Construímos a arquitetura de memória de um assistente de compras: contexto da sessão para o aqui e agora, sumário rolante para comprimir conversa longa, vetor por usuário para o que precisa atravessar sessões. Cada camada com tooling oficial e padrões já validados em produção.

O próximo passo natural dessa tecnologia é memória episódica de verdade, o agente lembrar não só fatos, mas eventos: "no último Black Friday você comprou tênis às 23h47 e ficou irritado porque o frete demorou". Isso muda recomendação, atendimento e até pricing.

Agora que você entendeu como dar memória ao agente, veja como conectar isso a busca semântica de catálogo e fechar o loop completo do assistente de ofertas.

Lucas Souza
Lucas Souza

{AI Engineer} — apaixonado por Laravel, arquitetura de software e construir produtos com impacto. Compartilho aqui tutoriais, descobertas e reflexões sobre o dia a dia de engenharia.

Você também pode gostar

Do prompt ao carrinho: arquitetura de um agente que compara ofertas entre Amazon, Mercado Livre e Magalu
Tutoriais

Do prompt ao carrinho: arquitetura de um agente que compara ofertas entre Amazon, Mercado Livre e Magalu

O agente que compara preços entre Amazon, Mercado Livre e Magalu funciona uma vez na frente da câmera. Em produção quebra em três pontos que a demo nunca mostra: produto que não é o mesmo, frete e cupom ignorados, e API que vai morrer em abril. Este post abre a arquitetura em cinco camadas e mostra as decisões que separam demo de feature real.

· 12 min
Alucinação em e-commerce é caro: quando a IA inventa especificação, cupom e estoque
Notícias

Alucinação em e-commerce é caro: quando a IA inventa especificação, cupom e estoque

Air Canada, DPD e Chevrolet mostraram em escala global o custo de deixar o LLM virar fonte de verdade no atendimento. Especificação inventada, cupom que não existe, estoque que não bate — vira chargeback, processo e dano de marca. O caminho técnico passa por retrieval grounded e tool use validando cada promessa.

· 12 min
Anatomia de um Agent Harness: state, tool execution, feedback loops e guardrails
Tutoriais

Anatomia de um Agent Harness: state, tool execution, feedback loops e guardrails

Harness é o software que envolve o LLM e separa um demo bonito de um agente que aguenta produção. Quebro a anatomia em cinco peças obrigatórias: estado persistente, roteador de ferramentas, validação de I/O, loop de raciocínio e limites de segurança. É o mapa mental que abre a série de posts sobre engenharia de agentes.

· 14 min
Chatbot não é agente: o teste dos 3 turnos que separa brinquedo de produto
Notícias

Chatbot não é agente: o teste dos 3 turnos que separa brinquedo de produto

Três perguntas simples sobre um produto real — preço hoje, reviews recentes, disponibilidade no CEP — quebram qualquer chatbot cru. O que separa brinquedo de produto não é o modelo. É o harness: a camada que transforma um LLM em agente confiável, com tool use, estado e validação contra o mundo real.

· 11 min

VirguIA

beer & code assistant

conectando…

Não foi possível iniciar o chat agora.

tocando