Uma análise fundamentada sobre memória de agentes: de listas em Python a arquivos markdown, busca vetorial, híbridos de grafos e vetores, e, por fim, uma solução limpa e open-source para tudo isso.

Um LLM é inerentemente stateless. Cada chamada de API começa do zero. A "memória" que você sente ao conversar com o ChatGPT é uma ilusão criada ao reenviar todo o histórico da conversa a cada requisição.
Esse truque funciona para conversas casuais. Ele desmorona no momento em que você tenta construir um agente real.
Aqui estão 7 modos de falha que aparecem assim que você ignora a memória:
- Amnésia de contexto: o agente pergunta por 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 acontecem para sempre
- Nenhum acúmulo de conhecimento: cada 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 é "jogar 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 de conversas e saída competem pelos mesmos tokens.
Mesmo com 100K tokens, a ausência de persistência, priorização e saliência torna o comprimento do contexto bruto insuficiente.

Memória não é sobre enfiar mais texto no prompt. É sobre estruturar o que o agente lembra para que ele possa encontrar o que importa.
O framework da ciência cognitiva que realmente ajuda
A formulação de Lilian Weng em 2023 tornou-se o framework padrão:
Agente = LLM + Memória + Planejamento + Uso de Ferramentas.
Os quatro pilares de igual importância.
Sua taxonomia empresta 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 aproximadamente 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 lembrar 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 um reembolso, primeiro verifique a data da compra")
A ponte entre o episódico e o 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 """Agente de IA mínimo: perceber, pensar, agir"""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 a ele "Tenho 4 maçãs", então 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 todos tentam:
1class Agent:2 def __init__(self):3 self.client = anthropic.Anthropic()4 self.messages = [] # Toda a "memória" é uma lista56 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, # Histórico completo enviado a cada vez12 )13 reply = response.content[0].text14 self.messages.append({"role": "assistant", "content": reply})15 return reply
Múltiplas voltas funcionam agora. A pergunta das maçãs é respondida corretamente porque a conversa completa é reenviada a cada chamada.
Dois problemas aparecem rapidamente:
- A lista cresce sem limites. Por volta da volta 200, você atinge o teto de contexto e as mensagens mais antigas são descartadas silenciosamente. O nome do usuário da volta 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 faz ideia de quem você é.
Camada 2: Arquivos Markdown para persistência
O próximo passo é escrever a memória no disco. Markdown é uma escolha natural: legível por humanos, amigável ao Git, e o agente pode lê-lo de volta como texto simples. O Claude Code usa exatamente este 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"Conversa anterior:\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 da AWS
Você pode abrir o arquivo em qualquer editor, ver exatamente o que o agente sabe e corrigi-lo manualmente. Genuinamente ú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 registros de conversas. Isso são 500K+ tokens de markdown no disco, e sua janela de contexto é de 128K.
Você não pode mais carregar tudo. Você precisa recuperar seletivamente apenas os fatos relevantes para a consulta atual. Com arquivos planos, sua única opção é a busca por palavra-chave:
1# Usuário pergunta: "Qual é o status da nossa migração para a nuvem?"2grep("migração para a nuvem", facts_file)3# Retorna: []4# O fato no disco diz "migrando banco de dados de produção para uma nova região da AWS."5# As palavras "migração para a nuvem" não aparecem em lugar nenhum.67# Usuário pergunta: "Qual time está cuidando do trabalho com o banco de dados?"8grep("time do banco de dados", facts_file)9# Retorna: []10# Um fato diz que Sarah "gerencia o time de backend." Outro diz que o time11# está "migrando o banco de dados de produção." Mas nenhuma linha contém12# "banco de dados" e "time" juntos.
Em pequena escala, arquivos markdown funcionam. Em escala real, eles forçam a recuperação por palavra-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 o OpenClaw, já viu isso acontecer. Ele armazena a memória como arquivos de checkpoint em markdown e, após semanas de uso diário, fatos anteriores desaparecem 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 embedding. O problema do sinônimo se dissolve.
Então você encontra um novo muro. Considere estes três fatos em seu banco de dados vetorial:
1- "Alice é a líder técnica do Projeto Atlas"2- "Projeto Atlas usa PostgreSQL como armazenamento de dados principal"3- "O cluster PostgreSQL teve uma interrupção na terça-feira"
Usuário pergunta: "O projeto da Alice foi afetado pela interrupção de terça?"
A consulta menciona Alice e a interrupção de terça, 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 embedding. O tecido conjuntivo que os liga é invisível para os vetores.

Isso não é um caso extremo. É a forma normal das perguntas do mundo real. O conhecimento empresarial é inerentemente relacional: pessoas pertencem a equipes, equipes 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 corrige 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 de dados vetorial, um banco de dados de grafo, um armazenamento relacional, um extrator de entidades, um pipeline de desduplicação e um sistema de ponderação de arestas. 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 todos 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 mecanismo, quatro chamadas
Cognee é um mecanismo de conhecimento open-source construído para memória de agentes. Ele combina busca vetorial com grafos de conhecimento e uma camada de proveniência relacional em um único sistema.
Toda a superfície da API são quatro chamadas assíncronas:
1import cognee23await cognee.add("Seu documento aqui") # Ingerir qualquer coisa4await cognee.cognify() # Construir grafo de conhecimento + embeddings5await cognee.memify() # Automelhorar a memória6await cognee.search("Sua consulta") # Recuperar com raciocínio
Por trás dessas quatro chamadas, há 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, ao que é semelhante
- Armazenamento de 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 o SQLite por Postgres, o LanceDB por Qdrant/Pinecone/pgvector e o Kuzu por Neo4j/FalkorDB/Neptune.
A mesma API de quatro chamadas de qualquer forma.
O que o 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-tenant
- Extração de chunks que respeita a estrutura de parágrafos (não cortes de tamanho fixo)
- Extração de entidades e relacionamentos via LLM, com desduplicação automática através de hashing de conteúdo
- Geração de resumos para recuperação eficiente
- Indexação dupla no armazenamento vetorial (embeddings) e no armazenamento de grafo (arestas)
A etapa de desduplicação é mais importante do que parece. Se a mesma entidade aparecer em 50 documentos, o 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. Esta 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 as consultas de múltiplos saltos possíveis sem sacrificar a busca semântica.
Memify: memória que aprende
memify() é o que diferencia o Cognee de toda ferramenta de "ingerir e pesquisar". Ele 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
- Autoajustando pesos de 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 permite que arestas de RH raramente consultadas decaiam. O grafo desenvolve seu próprio senso de relevância ao longo do tempo.

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

Construindo um agente real com a memória do Cognee
Aqui está o padrão completo conectando o Cognee ao loop perceber-pensar-agir:
1import cognee2from cognee import SearchType34class CogneeMemoryAgent:5 """Agente com memória persistente híbrida de grafo e vetor."""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": "Você é útil. Use o contexto da memória."},27 {"role": "system", "content": f"Contexto da memória:\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"Usuário: {user_input}\nAssistente: {reply}",36 "conversas"37 )38 await cognee.cognify(["conversas"])39 return reply
O ciclo da memória: ingerir, extrair, armazenar, recuperar, responder, armazenar novamente. Cada turno enriquece o grafo de conhecimento, e o processamento incremental significa que você só paga para indexar novo conteúdo.
A memória de sessão lida com a resolução de pronomes automaticamente:
1await cognee.search(query_text="Onde a Alice mora?", session_id="conv_1")2await cognee.search(query_text="O que ela faz para trabalhar?", session_id="conv_1")3# "ela" resolve para Alice a partir do contexto da sessão
O multi-tenancy é incorporado no nível do grafo com permissões por conjunto de dados (ler, escrever, deletar, compartilhar). Não é separação de namespace, é isolamento real no nível do grafo.
O caminho prático a seguir
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 só precisam de busca por similaridade ("encontre conversas como esta"), a memória apenas com vetores funciona. No momento em que as consultas cruzam limites de entidades ("O projeto da Alice foi afetado pela interrupção de terça-feira?"), você precisa de travessia de grafo.
Você pode conectar armazenamentos separados de vetor, grafo e relacional por conta própria. Equipes que seguem esse caminho tipicamente perdem semanas em infraestrutura para uma camada de memória que ainda não aprende com seu próprio uso.
O 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 requer 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 se 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 aí!
Se você gostou de ler isso:
Me encontre → @akshay_pachaar ✔️
Todos os dias, compartilho tutoriais e insights sobre IA, Machine Learning e as melhores práticas de vibe coding.





