Um passeio baseado em primeiros princípios pela memória de agentes: de listas em Python a arquivos Markdown, passando por busca vetorial e híbridos de grafo-vetor, até chegar a uma solução limpa e open-source para tudo isso.

Um LLM é, por design, sem estado. Cada chamada de API começa do zero. A "memória" que você sente ao conversar com o ChatGPT é uma ilusão criada pelo reenvio de todo o histórico da conversa a cada requisição.
Esse truque funciona para conversas casuais. Mas desmorona quando você tenta construir um agente de verdade.
Aqui estão 7 modos de falha que aparecem na hora que você pula a memória:
- Amnésia de contexto: o agente pede informações que você já forneceu
- Zero personalização: toda interação parece genérica
- Falha em tarefas de múltiplas etapas: o estado intermediário desaparece silenciosamente no meio da tarefa
- Erros repetidos: sem recordação episódica, os mesmos erros se repetem para sempre
- Nenhum acúmulo de conhecimento: toda sessão começa do zero
- Alucinação por lacunas: quando o contexto transborda, o modelo inventa
- Colapso de identidade: sem continuidade, não há confiança
A resposta óbvia é "jogue mais contexto nisso". É por isso que janelas de 128K e 200K tokens parecem que deveriam resolver tudo.
Não resolvem.
A precisão cai mais de 30% quando informações relevantes estão no meio de um contexto longo. Este é o bem documentado efeito "perdido no meio".
Contexto é um orçamento compartilhado: prompts de sistema, documentos recuperados, histórico da conversa e saída disputam os mesmos tokens.
Mesmo com 100K tokens, a ausência de persistência, priorização e saliência torna o comprimento bruto do contexto insuficiente.

Memória não é sobre enfiar mais texto no prompt. É sobre estruturar o que o agente lembra para que ele encontre o que importa.
O referencial da ciência cognitiva que realmente ajuda
A formulação de Lilian Weng de 2023 se tornou o framework padrão:
Agente = LLM + Memória + Planejamento + Uso de Ferramentas.
Os quatro pilares com igual importância.
A taxonomia dela é emprestada da ciência cognitiva, onde a memória humana se divide em três sistemas:
- Memória sensorial captura a entrada perceptual bruta e a mantém por uma fração de segundo. Apenas as partes às quais você presta atenção são passadas adiante.
- Memória de trabalho é onde o pensamento ativo acontece. Ela mantém cerca de 7±2 itens por vez (descoberta de Miller em 1956). Perde o foco, e o conteúdo desaparece.
- Memória de longo prazo é um armazenamento durável sem limite prático de capacidade. A recuperação é o gargalo: você pode armazenar milhões de coisas e ainda assim falhar em recordar a que precisa.
Cada uma mapeia diretamente para um componente nas arquiteturas modernas de agentes:

A memória de longo prazo se divide ainda mais:
- Episódica: eventos passados específicos ("na terça-feira, o cluster PostgreSQL caiu")
- Semântica: fatos e conceitos ("PostgreSQL é um banco de dados relacional")
- Procedural: habilidades e fluxos de trabalho ("quando um usuário pede reembolso, primeiro verifique a data da compra")
A ponte entre episódico e semântico é a consolidação da memória: eventos específicos repetidos se destilando em conhecimento geral. Um agente que percebe "usuários consistentemente preferem resumos executivos" em dezenas de interações deve transformar isso em uma regra reutilizável. Sem consolidação, seu agente reproduz eventos individuais em vez de aprender com eles.

O agente mínimo e o que quebra primeiro
Tirando os frameworks, um agente é um loop: perceber, pensar, agir.
1class Agent:2 """Minimal AI agent: perceive, think, act"""3 def __init__(self):4 self.client = anthropic.Anthropic()5 self.model = "claude-sonnet-4-20250514"67 def run(self, user_input: str) -> str:8 response = self.client.messages.create(9 model=self.model,10 max_tokens=1024,11 messages=[{"role": "user", "content": user_input}],12 )13 return response.content[0].text
Diga "Tenho 4 maçãs", depois pergunte "Comi uma, quantas sobraram?" e ele não tem ideia de quais maçãs você está falando. Cada chamada existe isoladamente.
Camada 1: A lista em Python
A primeira correção que todo mundo faz:
1class Agent:2 def __init__(self):3 self.client = anthropic.Anthropic()4 self.messages = [] # The entire "memory" is a list56 def chat(self, user_input: str) -> str:7 self.messages.append({"role": "user", "content": user_input})8 response = self.client.messages.create(9 model="claude-sonnet-4-20250514",10 max_tokens=1024,11 messages=self.messages, # Full history sent every time12 )13 reply = response.content[0].text14 self.messages.append({"role": "assistant", "content": reply})15 return reply
Múltiplas interações funcionam agora. A pergunta das maçãs é respondida corretamente porque a conversa completa é reenviada a cada chamada.
Dois problemas aparecem rápido:
- A lista cresce sem limites. Por volta da interação 200, você atinge o teto de contexto e as mensagens mais antigas são descartadas silenciosamente. O nome do usuário da interação 1 desaparece muito antes de uma piada descartável de ontem. Sem priorização, apenas ordem cronológica estrita.
- Tudo vive na RAM. No momento em que o processo Python termina, seu agente não tem ideia de quem você é.
Camada 2: Arquivos Markdown para persistência
O próximo passo é escrever a memória em disco. Markdown é uma escolha natural: legível por humanos, compatível com Git, e o agente pode lê-lo de volta como texto simples. Claude Code usa exatamente esse padrão com arquivos CLAUDE.md e MEMORY.md.
1class MarkdownMemoryAgent:2 def __init__(self):3 self.client = anthropic.Anthropic()4 self.history_file = Path("memory/conversation_history.md")5 self.facts_file = Path("memory/known_facts.md")67 def save_to_disk(self, role: str, content: str) -> None:8 with open(self.history_file, "a") as f:9 f.write(f"### {role} at {datetime.now().isoformat()}\n{content}\n\n")1011 def load_history(self) -> str:12 if self.history_file.exists():13 return self.history_file.read_text()14 return ""1516 def chat(self, user_input: str) -> str:17 self.save_to_disk("user", user_input)18 history = self.load_history()19 response = self.client.messages.create(20 model="claude-sonnet-4-20250514",21 max_tokens=1024,22 system=f"Previous conversation:\n{history}",23 messages=[{"role": "user", "content": user_input}],24 )25 reply = response.content[0].text26 self.save_to_disk("assistant", reply)27 return reply
A persistência está resolvida. Reinicie o script e a conversa ainda está no disco. Você também pode manter um arquivo de fatos separado que o agente extrai ao longo do tempo:
1- O nome do usuário é Sarah2- Sarah gerencia o time de backend na Acme Corp3- Acme Corp é uma empresa de SaaS B2B4- Atualmente migrando o banco de dados de produção para uma nova região na AWS
Você pode abrir o arquivo em qualquer editor, ver exatamente o que o agente sabe e corrigir manualmente. Realmente útil para prototipagem.
Com 4 fatos, isso funciona perfeitamente. Carregue o arquivo inteiro no contexto e o LLM lida com qualquer pergunta sobre Sarah, sua empresa ou seu setor.
Agora avance três meses. Seu agente tem 2.000 fatos extraídos e 200 logs de conversa. São 500K+ tokens de markdown em disco, e sua janela de contexto é de 128K.
Você não pode mais carregar tudo. Precisa recuperar seletivamente apenas os fatos relevantes para a consulta atual. Com arquivos planos, sua única opção é busca por palavra-chave:
1# User asks: "What's the status of our cloud migration?"2grep("cloud migration", facts_file)3# Returns: []4# The fact on disk says "migrating production database to a new AWS region."5# The words "cloud migration" appear nowhere.67# User asks: "Which team is handling the database work?"8grep("database team", facts_file)9# Returns: []10# One fact says Sarah "manages the backend team." Another says the team11# is "migrating production database." But no single line contains12# both "database" and "team" together.
Em pequena escala, arquivos Markdown funcionam. Em escala real, eles forçam a recuperação por palavras-chave, e palavras-chave não conseguem lidar com sinônimos, paráfrases ou conexões entre fatos.
A informação está no disco. Mas você não pode carregar tudo, e a busca por palavra-chave é frágil demais para encontrar as peças certas.
Se você já usou OpenClaw, já viu isso acontecer. Ele armazena memória como arquivos de checkpoint em Markdown e, após semanas de uso diário, fatos anteriores escapam silenciosamente à medida que o contexto se acumula e é compactado. O armazenamento está lá. A recuperação não está.
Armazenamento sem recuperação inteligente é uma biblioteca sem catálogo.
Camada 3: Busca vetorial e o muro que ela encontra
Adicione embeddings. Divida seu Markdown em chunks, incorpore os chunks, busque por similaridade de cosseno. Agora "banco de dados" corresponde a "PostgreSQL" porque seus vetores vivem próximos no espaço de embeddings. O problema do sinônimo se dissolve.
Então você encontra um novo muro. Considere estes três fatos no seu banco vetorial:
1- "Alice é a líder técnica do Projeto Atlas"2- "Projeto Atlas usa PostgreSQL como armazenamento principal"3- "O cluster PostgreSQL sofreu uma queda na terça-feira"
Usuário pergunta: "O projeto de Alice foi afetado pela queda de terça-feira?"
A consulta menciona Alice e a queda de terça-feira, então a busca vetorial classifica o primeiro e o terceiro fatos como altos. Mas a ponte crítica, "Projeto Atlas usa PostgreSQL", não menciona nem Alice nem terça-feira. É a peça de conexão, e é a que não vai aparecer.
Cada fato é um ponto isolado no espaço de embeddings. O tecido conjuntivo que os liga é invisível para vetores.

Isso não é um caso extremo. É a forma normal das perguntas do mundo real. O conhecimento de negócios é inerentemente relacional: pessoas pertencem a times, times possuem projetos, projetos dependem de sistemas, sistemas têm incidentes. Qualquer pergunta que cruze dois ou mais saltos excede o que a recuperação vetorial plana pode responder.
A matriz de capacidades
Cada camada resolve a dor anterior, mas revela uma mais profunda:

Você precisa de persistência, compreensão semântica e raciocínio relacional em uma única camada de memória.
Construir isso sozinho significa colar um banco vetorial, um banco de grafos, um armazenamento relacional, um extrator de entidades, um pipeline de deduplicação e um sistema de ponderação de arestas. Isso são semanas de trabalho de infraestrutura antes de escrever uma única linha de lógica de agente.
Eu tenho usado uma solução que preenche essa lacuna de forma limpa. É totalmente open-source, lida com os três paradigmas de armazenamento sob o mesmo teto, e você pode colocá-la para funcionar em minutos. Vamos falar sobre Cognee.
Cognee: três armazenamentos, um motor, quatro chamadas
Cognee é um motor de conhecimento open-source construído para memória de agentes. Ele combina busca vetorial com grafos de conhecimento e uma camada relacional de proveniência em um único sistema.
Toda a superfície da API são quatro chamadas assíncronas:
1import cognee23await cognee.add("Your document here") # Ingest anything4await cognee.cognify() # Build knowledge graph + embeddings5await cognee.memify() # Self-improve the memory6await cognee.search("Your query") # Retrieve with reasoning
Por trás dessas quatro chamadas está uma arquitetura de três armazenamentos.

Por que três armazenamentos e não um?
Cada armazenamento captura uma dimensão do conhecimento que os outros não conseguem:
- Armazenamento relacional → proveniência: de onde os dados vieram, quando foram ingeridos, quem tem acesso
- Armazenamento vetorial → semântica: o que o conteúdo significa, a que é semelhante
- Armazenamento em grafo → relacionamentos: como as entidades se conectam, o que causa o quê, quem se reporta a quem
Achate qualquer um deles e você perde informações que importam para a precisão da recuperação.
A pilha padrão é SQLite + LanceDB + Kuzu, totalmente embarcada e baseada em arquivos. pip install cognee mais uma chave de API de LLM e você está rodando.
Sem Docker, sem serviços externos.
Para produção, troque SQLite por Postgres, LanceDB por Qdrant/Pinecone/pgvector e Kuzu por Neo4j/FalkorDB/Neptune.
Mesma API de quatro chamadas de qualquer forma.
O que cognify realmente faz?
cognee.cognify() executa um pipeline de múltiplos estágios que converte texto bruto em conhecimento estruturado e interconectado:
- Classificação de documentos por tipo e domínio
- Verificação de permissões para controle de acesso multi-inquilino
- Extração de chunks que respeita a estrutura de parágrafos (sem cortes de tamanho fixo)
- Extração de entidades e relacionamentos via LLM, com deduplicação automática através de hash de conteúdo
- Geração de resumos para recuperação eficiente
- Indexação dupla no armazenamento vetorial (embeddings) e no armazenamento em grafo (arestas)
A etapa de deduplicação é mais importante do que parece. Se a mesma entidade aparece em 50 documentos, Cognee a mescla em um único nó do grafo com 50 arestas de entrada. Seu agente não vê mais "Alice" como 50 estranhos diferentes. E o pipeline é incremental por padrão: apenas arquivos novos ou atualizados são reprocessados.

Cada nó do grafo tem um embedding correspondente. Essa representação dupla é o truque central: entre pelos vetores (encontre conteúdo semanticamente semelhante) e saia pelo grafo (siga relacionamentos para entidades conectadas), ou o inverso. É isso que torna consultas de múltiplos saltos viáveis sem sacrificar a busca semântica.
Memify: memória que aprende
memify() é o que diferencia Cognee de toda ferramenta de "ingerir e buscar". Ela executa uma passagem de otimização inspirada em RL sobre o grafo:
- Fortalecendo caminhos úteis que levaram a uma boa recuperação
- Podando nós obsoletos que não foram tocados
- Ajustando automaticamente os pesos das arestas com base no uso real
- Adicionando fatos derivados identificando relacionamentos implícitos
O grafo de um agente de suporte ao cliente naturalmente fortalece caminhos através de documentos de produto e políticas de reembolso, enquanto deixa as arestas de RH raramente consultadas se deteriorarem. O grafo desenvolve seu próprio senso de relevância ao longo do tempo.

Quatorze modos de recuperação
Cognee oferece 14 modos de busca. Os que você realmente vai usar:

Construindo um agente real com memória Cognee
Aqui está o padrão completo conectando Cognee ao loop perceber-pensar-agir:
1import cognee2from cognee import SearchType34class CogneeMemoryAgent:5 """Agent with graph-vector hybrid persistent memory."""67 def __init__(self, session_id: str = "default"):8 self.llm_client = OpenAI()9 self.session_id = session_id1011 async def ingest(self, text: str, dataset: str = "main"):12 await cognee.add(text, dataset)13 await cognee.cognify([dataset])1415 async def recall(self, query: str) -> str:16 results = await cognee.search(17 query_text=query,18 query_type=SearchType.GRAPH_COMPLETION,19 session_id=self.session_id,20 )21 return results[0] if results else ""2223 async def chat(self, user_input: str) -> str:24 context = await self.recall(user_input)25 messages = [26 {"role": "system", "content": "You are helpful. Use memory context."},27 {"role": "system", "content": f"Memory context:\n{context}"},28 {"role": "user", "content": user_input},29 ]30 response = self.llm_client.chat.completions.create(31 model="gpt-4o-mini", messages=messages32 )33 reply = response.choices[0].message.content34 await cognee.add(35 f"User: {user_input}\nAssistant: {reply}",36 "conversations"37 )38 await cognee.cognify(["conversations"])39 return reply
O ciclo da memória: ingerir, extrair, armazenar, recuperar, responder, armazenar novamente. Cada interação enriquece o grafo de conhecimento, e o processamento incremental significa que você só paga para indexar conteúdo novo.
A memória de sessão lida com a resolução de pronomes automaticamente:
1await cognee.search(query_text="Where does Alice live?", session_id="conv_1")2await cognee.search(query_text="What does she do for work?", session_id="conv_1")3# "she" resolves to Alice from session context
O multi-inquilino é nativo no nível do grafo com permissões por conjunto de dados (ler, escrever, deletar, compartilhar). Não é separação por namespace, é isolamento real no nível do grafo.
O caminho prático adiante
Se você está construindo um agente hoje, a verdadeira pergunta inicial é: "o que meu agente precisa lembrar e que tipo de perguntas ele vai responder?"
Se suas consultas precisam apenas de busca por similaridade ("encontre conversas como esta"), a memória apenas vetorial funciona. No momento em que as consultas cruzam limites de entidades ("O projeto de Alice foi afetado pela queda de terça-feira?"), você precisa de travessia de grafo.
Você pode conectar armazenamentos separados (vetorial, grafo e relacional) por conta própria. Times que seguem esse caminho geralmente queimam semanas em infraestrutura para uma camada de memória que ainda não aprende com seu próprio uso.
Cognee reduz isso a quatro chamadas de API. Os padrões embarcados fazem você rodar em minutos. Backends substituíveis (Postgres, Qdrant, Neo4j) levam você à produção sem alterar o código do seu agente.
Inteligência exige estrutura, não apenas armazenamento. Os três paradigmas de armazenamento (relacional, vetorial, grafo) não são opções concorrentes. São camadas complementares do mesmo sistema de memória. Tratá-los dessa forma é o que transforma um invólucro de LLM sem estado em algo que realmente aprende.
Qual é a próxima coisa que você gostaria que seu agente lembrasse amanhã e que ele esqueceu hoje? Comece por aí.
👉 **[Confira o Cognee no GitHub →**](https://github.com/topoteretes/cognee), dê uma estrela e tente conectá-lo ao seu próximo agente.
Quatro chamadas assíncronas, um pip install, e você está rodando.
É isso!
Se você gostou de ler este artigo:
Me encontre →@akshay_pachaar ✔️
Todos os dias, compartilho tutoriais e insights sobre IA, Machine Learning e melhores práticas de vibe coding.





