[!NOTE] Tradução automática Este artigo foi traduzido automaticamente a partir da versão original em inglês.

AI Arquitetura de Memória de Agente em 2026: Checkpoints, Armazenamentos Vetoriais e Memória Baseada em Ficheiros

Parte 2 da série de engenharia da pilha Agentic

Um reasoning loop só consegue persistir por um único pedido, a menos que o seu estado seja armazenado fora do worker. Sem memória de agente, o agente não consegue retomar um plano interrompido, recuperar-se após uma falha ou recuperar uma preferência de uma sessão anterior. Parte 1 Abordou o fluxo de controle. Este artigo indica qual estado é necessário em cada turno subsequente e onde esse estado deve ser armazenado.

Vou explicar em detalhe a arquitetura de memória do Agente de Análise de Mercado, Vou demonstrar como os armazenamentos vetoriais checkpoints quentes, frios e a memória de documentos baseada em ficheiros funcionam em conjunto para agentes que devem permanecer ativos por um longo período de tempo. Em seguida, abordarei em que situações o PostgreSQL, o Redis, o Qdrant, os armazenamentos chave-valor e os ficheiros Markdown simples são a solução mais adequada.

TL;DR: Separe a memória de acordo com o padrão de acesso. A memória quente representa o estado em nível de thread checkpoint, utilizado para pausas e retomadas. A memória fria armazena factos entre sessões num repositório de tipo chave-valor ou num armazenamento vetorial. A memória de documentos guarda o conhecimento do projeto em ficheiros passíveis de inspeção. Comece por identificar a falha que precisa de ser resolvida e, em seguida, escolha o tipo de armazenamento adequado. Não coloque factos exatos num sistema de recuperação baseado em semelhanças, nem trate um checkpoint como um registo de auditoria.


O que é a memória de agente AI?

AI memória do agente é a camada de estado que permite a um agente manter o progresso das tarefas, recuperar conhecimentos anteriores e atualizar o que sabe em diferentes execuções. Em produção, não se trata de uma única base de dados vetorial; trata‑se de uma combinação de armazenamentos checkpoints ativos, armazenamentos semânticos ou estruturados inativos, bem como de uma memória de documentos legível por humanos.

Preciso deMelhor valor predefinidoPorquê?
Pausar e retomar uma execuçãoPostgreSQL checkpoint de armazenamentoDados da aplicação duráveis, consultáveis e fáceis de operar
Estado transitório de baixa latênciaArmazenamento Redis checkpointRecuperação rápida e estado de curta duração, com compromissos relacionados à persistência
Recuperação semântica entre sessõesRecupera memórias com base no significado, e não apenas em chaves exatas
Dados estruturados do utilizadorPostgreSQL ou armazenamento de tipo chave-valorAtualizações determinísticas superam a recuperação fuzzy para preferências e identificadores.
Convenções de projeto e procedimentos estabelecidosFicheiros em Markdown ou JSONLegível por humanos, suscetível a diferenças e fácil de atualizar por agentes
Memória de relações entre múltiplas entidadesGráfico de conhecimentoÚtil quando as relações entre elementos são mais importantes do que os factos individuais.

Não comece a falar da memória, pois soa a algo inteligente. Comece com o erro visível para o utilizador: perda de progresso, esquecimento de uma preferência, repetição de pesquisas ou incapacidade de reutilizar uma convenção de projeto.

As falhas que exigem memória

Um agente sem estado consegue responder a uma pergunta isolada, mas esquece o pedido assim que a chamada termina. Esse tipo de design falha quando o produto requer algum dos comportamentos seguintes:

Na Agente de Análise de Mercado de Parte 1, O pedido “Analyze NVDA” gera um plano, cinco tool calls, dados recolhidos e um rascunho de relatório. Quando o utilizador responde “parece bom, mas adicione uma análise de concorrentes”, uma checkpoint store permite que o agente carregue o estado do nó anterior e inclua a etapa de análise de concorrentes. Sem um estado com pontos de verificação, não consegue determinar a que se refere o termo “parece bom” e é forçado a começar tudo de novo.

A memória de longo prazo lida com um caso diferente. Se o utilizador regressar uma semana depois e perguntar: “Atualize a minha análise da NVDA”, o agente poderá precisar de recuperar a preferência por avaliações de risco conservadoras e o interesse em ações de semicondutores. Um armazenamento de memória baseado em vetores consegue recuperar esses factos entre sessões, sem ser necessário pedi-los novamente.

O LangGraph divide os dados por escopo. Cada execução de grafo ocorre dentro de um thread, o que corresponde a uma conversa ou tarefa específica. O estado persistido nesse thread é considerado memória de curto prazo. Já o estado partilhado entre vários threads é classificado como memória de longo prazo. O contexto atual do modelo e as variáveis em processamento constituem a camada de memória de trabalho, localizada acima desses dois tipos de armazenamento.

Taxonomia de Memória


Uma taxonomia da memória de agente AI

Antes de avançarmos para a implementação, é útil classificar o que os agentes precisam de memorizar. O CoALA framework (Sumers, Yao et al., 2023) constitui a taxonomia padrão e baseia‑se na ciência cognitiva. Eu introduzi o conceito de escopo de memória no meu context engineering publicação; Aqui, expando-o em seis categorias:

Tipo de MemóriaEscopoVida útilExemploPadrão de Armazenamento
Em funcionamentoPasso atualMilissegundosTool call argumentos, resposta atual LLM
Curto prazoTópico atualMinutos–horasHistórico de conversação, progresso do plano, dados recolhidosCheckpoint armazenar
EpisódicoEntre threadsDias–mesesNa semana passada, o utilizador questionou sobre os resultados financeiros da NVDA.Armazém de vetores / Armazém KV
SemânticoEntre threadsMeses – permanente”O utilizador prefere investimentos conservadores”Armazém de vetores / Armazém KV
DocumentoEntre threadsDias – permanenteNotas de projeto, resumos de investigação, padrões identificadosArmazenamento de ficheiros (Markdown/JSON)
ProcessualEm todo o sistemaPermanente”Ao analisar ações, verifique sempre os documentos apresentados à SEC.”Configuração / system prompt

Memória de trabalho é aquilo com o qual o LLM está a raciocinar ativamente neste momento: as variáveis em Python da função atual, o conteúdo da janela de contexto e os argumentos tool call em execução. Trata‑se da camada mais rápida, mas também a mais efémera; nada persiste para além do passo atual. A memória de trabalho é limitada pela janela de contexto do modelo, o que a torna o verdadeiro gargalo. Tudo o que o agente “sabe” no momento de tomar uma decisão tem de caber aqui, independentemente de ter vindo do armazenamento checkpoint, de uma consulta vetorial ou de uma leitura de ficheiro. As outras camadas existem para fornecer as informações corretas à memória de trabalho na altura certa.

Memória de curto prazo é o checkpoint que o LangGraph grava após cada nó. As memórias episódicas e semânticas permanecem ativas ao longo das threads. A memória de documento armazena notas de projeto, resumos de investigação e convenções aprendidas em ficheiros que podem ser consultados por pessoas e agentes. A memória procedural encontra-se nas instruções do sistema e nas definições de ferramentas, não sendo alterada para cada utilizador.

Para implementação, estas categorias reduzem‑se a três níveis. A memória quente armazena a sessão atual. A memória fria permite a recuperação de informações entre sessões. A memória de documentos mantém o conhecimento acumulado do projeto legível e diretamente editável.

O CoALA classifica a memória de trabalho, episódica, semântica e procedural. O Memória na Era dos Agentes AI: uma análise Dá ênfase aos repositórios vetoriais e aos grafos de conhecimento, ao mesmo tempo que o LangGraph documenta checkpoints e a sua interface Store. O conhecimento dos projetos armazenado em ficheiros fica fora dessas taxonomias, embora o Claude Code, o Cursor, o Windsurf e o Devin carreguem sempre ficheiros de projeto persistentes.

O mesmo padrão de armazenamento é observado noutros domínios. O Voyager guarda competências de jogos reutilizáveis como bibliotecas de código, as equipas do ECR3 trabalharam em documentos procedurais prompt, e a Memória de Fluxo de Trabalho do Agente permite gerar fluxos de trabalho web reutilizáveis a partir de episódios bem-sucedidos. Os ficheiros tornam esse conhecimento inspecionável e versionável, sem a necessidade de um serviço separado embedding.

A memória gerida por agente difere também de uma RAG pipeline fixa no que diz respeito à entidade responsável pela escrita. O agente ou o seu harness decide o que deve ser armazenado, atualizado ou eliminado, selecionando posteriormente o momento adequado para o recuperar.

O Artigo sobre Agentes Generativos (Park et al., 2023) demonstrou até que ponto isto é possível: agentes simulados conseguiram armazenar, refletir sobre e recuperar as suas próprias memórias. O seu fluxo de memória classificava os candidatos com base na recenteidade, importância e relevância, um design que continua a servir como um ponto de referência útil para a recuperação de memórias por parte dos agentes.


Memória de agente de curto prazo: o armazenamento checkpoint

Sempre que um nó do LangGraph é executado, o framework serializa todo o estado do grafo e grava‑o num checkpoint store. Essa é a base para funcionalidades de pausa/reinício, depuração com “time‑travel” e fluxos de trabalho HITL.

Fluxo de Memória Quente Checkpoint

Um checkpoint contém o estado do grafo necessário para retomar: o AgentState de Parte 1 (mensagens, identidade, perfil de utilizador, passos do plano, dados de investigação, modo de execução), além de metadados do LangGraph como o nó que o gerou e o seu ID checkpoint. Após uma interrupção HITL ou reinício do processo, o grafo carrega o limite mais recente registado e regressa ao nó seguinte. Não continua a partir de uma linha de Python arbitrária. Um checkpoint também difere de um registo de eventos ou rastreio de apenas adição; Parte 5 separa de forma explícita essas superfícies de observabilidade runtime.

Como funciona o checkpointing no LangGraph

do LangGraph BaseCheckpointSaver é uma interface simples: put() escreve um checkpoint, get_tuple() lê o mais recente para um fio de discussão. list() devolve o histórico. Cada checkpoint é indexado por (thread_id, checkpoint_ns, checkpoint_id), onde thread_id identifica a conversa. checkpoint_ns lida com o nomeação de subgrafos, e checkpoint_id trata-se de uma versão única.

A decisão importante é determinar qual backend deve ser utilizado como suporte. O PostgreSQL e o Redis são duas opções bastante comuns em ambientes de produção.

PostgreSQL vs Redis

Redis versus PostgreSQL

DimensãoPostgreSQL (langgraph-checkpoint-postgres)Redis (langgraph-checkpoint-redis)
Modelo de durabilidadeTransações ACID, WAL e replicaçãoPersistência configurável em AOF ou RDB
Checkpoint históricoHistórico duradouro para resumos e depuraçãoA retenção depende das definições de salvaguarda e de expulsão.
Restrição principalLatência de escrita na base de dados e crescimento da tabelaUso de RAM, expulsão de elementos e configuração de persistência
Adequação operacionalEquipes que já utilizam bases de dados relacionaisAs equipas que já utilizam o Redis com elevado débito de processamento
Melhor opção padrão paraRecuperação de estado duradoura e depuração reprodutívelEstado de sessão recuperável e sensível à latência

Bancos de dados genéricos benchmarks não permitem prever o desempenho checkpoint. É necessário medir o tamanho do estado serializado, a frequência de escrita, as configurações de persistência e a concorrência do seu próprio grafo.

PostgreSQL: o padrão duradouro por defeito

O PostgreSQL é a opção padrão mais segura para a maioria das equipas. Graças à capacidade de Checkpoints após falhas, dispõe-se de semânticas de transações completas, e o histórico de checkpoint torna a depuração por “viagem no tempo” extremamente simples.

De checkpointer_setup.py:

from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver

async def create_postgres_checkpointer(connection_string: str) -> AsyncPostgresSaver:
    """Create a PostgreSQL-backed checkpoint store.

    PostgreSQL gives us ACID guarantees — if a checkpoint write succeeds,
    the state is durable even if the process crashes immediately after.
    """
    checkpointer = AsyncPostgresSaver.from_conn_string(connection_string)

    # Create the checkpoint tables if they don't exist.
    # This is idempotent — safe to call on every startup.
    await checkpointer.setup()

    return checkpointer

# Usage: wire into the graph compilation
checkpointer = await create_postgres_checkpointer(
    "postgresql://user:pass@localhost:5432/agent_memory"
)
graph = create_graph(checkpointer=checkpointer)

# Every invoke/stream call now persists state automatically
config = {"configurable": {"thread_id": "user-123-session-1"}}
result = await graph.ainvoke({"messages": [HumanMessage(content="Analyze NVDA")]}, config)

# Resume later — loads the latest checkpoint for this thread
result = await graph.ainvoke({"messages": [HumanMessage(content="approved")]}, config)

O AsyncPostgresSaver utiliza o langgraph-checkpoint-postgres package, que cria três tabelas: checkpoints (o estado serializado) checkpoint_blobs (dados binários de grande volume), e checkpoint_writes (Pendentes escritas para recuperação de falhas.) O esquema suporta acesso concorrente e utiliza bloqueios consultivos para evitar conflitos de escrita.

Redis: quando a latência é o gargalo

Quando uma latência de sub-milissegundo checkpoint é crítica (agentes de conversação em tempo real, ciclos de ferramentas de alta frequência), o Redis representa a escolha mais adequada.

De checkpointer_setup.py:

from langgraph.checkpoint.redis.aio import AsyncRedisSaver

async def create_redis_checkpointer(redis_url: str) -> AsyncRedisSaver:
    """Create a Redis-backed checkpoint store.

    Redis stores checkpoints in memory for sub-millisecond access.
    Trade-off: less durable than PostgreSQL unless AOF is enabled.
    """
    checkpointer = AsyncRedisSaver.from_conn_string(redis_url)

    # Initialize Redis data structures
    await checkpointer.setup()

    return checkpointer

# Usage: same graph API, different backend
checkpointer = await create_redis_checkpointer("redis://localhost:6379")
graph = create_graph(checkpointer=checkpointer)

O AsyncRedisSaver de langgraph-checkpoint-redis armazena checkpoints como documentos JSON indexados por identificador de thread. O v0.1.0 de redesenho substituímos várias operações de busca por uma única JSON.GET Chamadas de API, reduzindo significativamente a latência. O Redis 8.0+ inclui por padrão o RedisJSON e o RediSearch — não é necessário instalar módulos adicionais.

Em implementações com restrições de memória, ShallowRedisSaver Armazena apenas a versão mais recente do checkpoint por thread — sem histórico, mas com consumo mínimo de RAM. Utilize este método quando precisar de pausar/recontinuar o processo, sem necessidade de depuração que permita retroceder no tempo.

Quando utilizar cada um

Utilize o PostgreSQL quando:

Utilize o Redis quando:

Outras opções: langgraph-checkpoint-sqlite Funciona para desenvolvimento local e implementações em único processo. Para stacks nativas da AWS, langgraph-checkpoint-aws fornece um DynamoDBSaver Graças a um tratamento inteligente da carga útil — ficheiros pequenos checkpoints (<350 KB) permanecem no DynamoDB, enquanto os maiores são transferidos automaticamente para o S3. O modelo de preços serverless, aliado à ausência de infraestrutura a gerir, torna esta solução atrativa para implementações com cargas variáveis.


Memória de longo prazo: memorização entre sessões

A memória ativa lida com a conversa atual. Mas e o utilizador que regressar na próxima semana? A memória de longo prazo armazena factos, preferências e histórico de interações que permanecem válidos ao longo de várias sessões.

LangGraph oferece um Store interface para acesso à memória entre threads através da mesma BaseStore classe. Cada elemento de memória é um (namespace, key) emparelhar com um valor JSON e um vetor opcional embedding. O namespace codifica normalmente o utilizador ou a organização: ("user", "user-123", "preferences").

Fluxo de Memória de Longo Prazo

Armazenamento vetorial: recuperação semântica com o Qdrant

Quando o agente precisa recuperar factos não estruturados (“O que é que o utilizador disse sobre o seu cronograma de investimento?”), a busca vetorial permite uma recuperação semântica. Em vez de realizar buscas por palavras-chave exatas, o agente efetua consultas com base no significado.

Qdrant é uma base de dados vetorial desenvolvida especificamente em Rust, que lida com o armazenamento embedding, indexação (HNSW) e busca filtrada. Abordei em detalhe o HNSW e as suas contrapartidas no meu classificação de resultados de busca. Qdrant também disponibiliza um servidor MCP que atua como uma camada de memória semântica — útil se o seu agente framework suportar o Model Context Protocol.

De memory_store.py:

from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct, Distance, VectorParams
from langchain_anthropic import ChatAnthropic
import hashlib
import json

class UserMemoryStore:
    """Long-term memory backed by Qdrant vector search.

    Stores user facts as embedded vectors for semantic retrieval.
    Each fact is a short natural-language statement about the user.
    """

    def __init__(self, qdrant_url: str, collection_name: str = "user_memory"):
        self.client = QdrantClient(url=qdrant_url)
        self.collection_name = collection_name
        self._ensure_collection()

    def _ensure_collection(self):
        """Create the collection if it doesn't exist."""
        collections = [c.name for c in self.client.get_collections().collections]
        if self.collection_name not in collections:
            self.client.create_collection(
                collection_name=self.collection_name,
                vectors_config=VectorParams(
                    size=1536,  # text-embedding-3-small dimensions
                    distance=Distance.COSINE,
                ),
            )

    def store_fact(self, user_id: str, fact: str, embedding: list[float]):
        """Store a user fact with its embedding."""
        point_id = hashlib.md5(f"{user_id}:{fact}".encode()).hexdigest()
        self.client.upsert(
            collection_name=self.collection_name,
            points=[PointStruct(
                id=point_id,
                vector=embedding,
                payload={"user_id": user_id, "fact": fact},
            )],
        )

    def recall(self, user_id: str, query_embedding: list[float], top_k: int = 5):
        """Retrieve the most relevant facts for a user given a query."""
        results = self.client.query_points(
            collection_name=self.collection_name,
            query=query_embedding,
            query_filter={"must": [{"key": "user_id", "match": {"value": user_id}}]},
            limit=top_k,
        )
        return [hit.payload["fact"] for hit in results.points]

O fluxo é o seguinte: (1) após cada conversa, um LLM extrai os factos-chave da interação (“o utilizador tem uma alta tolerância ao risco”, “o utilizador está interessado em ações de semicondutores”), (2) esses factos são incorporados e armazenados no Qdrant, (3) no início da próxima conversa, o agente faz uma consulta ao Qdrant com a nova mensagem do utilizador para recuperar o contexto relevante.

Pontuação de recuperação: para além da semelhança coseno

A semelhança coseno bruta é um ponto de partida, mas os sistemas de memória em produção exigem uma recuperação de informações mais sofisticada. Artigo sobre Agentes Generativos (Park et al., 2023) introduziram uma função de pontuação que combina três sinais:

A pontuação final de recuperação é uma soma ponderada: score = alpha * recency + beta * importance + gamma * relevance. Isso impede que factos recentes e importantes fiquem submersos sob informações obsoletas, mas semanticamente semelhantes. Para o Agente de Análise de Mercado, Eu atribuo maior peso à relevância (0.5), seguida pela recentesidade (0.3) e pela importância (0.2), uma vez que a intenção da consulta atual do utilizador é o fator mais determinante. Trata‑se de pesos de ponto de partida adaptados do artigo sobre Agentes Gerativos (que utilizava ponderações iguais); verifiquei que dar maior ênfase à relevância funciona melhor para consultas de análise financeira, mas estes valores baseiam‑se em intuição e não foram otimizados empiricamente.

Alternativas à busca vetorial

A busca vetorial é poderosa, mas nem sempre é a ferramenta adequada. Eis quando se devem utilizar alternativas:

AbordagemO melhor paraPrincipal custo operacional
Busca vetorial (Qdrant)Recuperação semântica de factos não estruturadosEmbedding e ciclo de vida do índice
Armazém de chave-valor (Redis)Perfis de utilizador e preferências estruturadasPolítica de utilização de memória e persistência
Armazém de documentos (ficheiros)Conhecimento do projeto e notas geridas pelo agenteConcorrência, permissões e busca
Pesquisa em texto completo (PostgreSQL) índice GIN)**Recuperação de palavras-chave a partir do histórico da conversaçãoCrescimento do índice e otimização de consultas
Gráfico de conhecimento (Neo4j)Relações entre entidades e consultas de vários saltosModelação de grafos e outro sistema de dados
Híbrido (vetor + palavra-chave)Lembre-se de quando a intenção da consulta variaDois caminhos de pontuação a ajustar e avaliar

Os armazenamentos chave-valor funcionam de forma eficaz para dados estruturados. Se a sua memória de longo prazo corresponder a um perfil de utilizador — tolerância ao risco, horizonte de investimento, setores preferidos —, um hash do Redis ou uma coluna JSONB do PostgreSQL é uma solução mais simples e rápida do que embedding e a consulta de vetores. Utilize a busca por vetores quando a memória for não estruturada e as consultas de recuperação variarem em termos de formulação.

A Loja integrada do LangGraph oferece uma interface de tipo chave-valor baseada em namespace, com busca vetorial opcional. O BaseStore API é simples: put(), get(), search(), e delete() com escopo de namespace hierárquico. Existem três implementações disponíveis:

O index A configuração permite a realização de busca vetorial em itens armazenados, recorrendo a um modelo embedding configurável. Para muitos casos de uso, este repositório integrado é suficiente, evitando a necessidade de recorrer a uma base de dados vetorial dedicada.

from langgraph.store.memory import InMemoryStore

# Create a store with vector search enabled
store = InMemoryStore(
    index={
        "dims": 1536,
        "embed": my_embedding_function,  # e.g., OpenAI text-embedding-3-small
    }
)

# Store a user preference (namespace scopes to user)
await store.aput(
    namespace=("user", "user-123", "preferences"),
    key="risk-profile",
    value={"risk_tolerance": "high", "horizon": "long-term"},
)

# Semantic search across user's memories
results = await store.asearch(
    namespace=("user", "user-123"),
    query="What is their investment style?",
    limit=5,
)

Escolha de uma estratégia de memória de longo prazo

Comece com estruturas de tipo chave-valor caso a sua memória esteja organizada e bem definida (perfis de utilizador, definições de configuração, entidades nomeadas). Adicione a busca vetorial quando precisar de recuperação semântica em dados não estruturados ou quando a formulação das consultas varia de forma imprevisível.

Graphos de conhecimento tornam-se úteis quando as relações entre entidades são relevantes, por exemplo: “Quais empresas sobre as quais o utilizador perguntou são concorrentes da NVDA?” O projeto mais interessante desenvolvido recentemente neste âmbito é Graphiti (por Zep), que constrói um gráfico de conhecimento com consciência temporal que regista quando os factos eram verdadeiros, e não apenas o que era verdadeiro. Cada aresta possui intervalos de validade, de modo que uma alteração na tolerância ao risco do utilizador invalida o valor antigo em vez de o substituir silenciosamente. O Graphiti reporta 94,8% de precisão no DMR benchmark, E o seu modelo bitemporal resolve o problema da memória obsoleta na camada de dados.

O problema reside no aspeto operacional. Executar uma base de dados gráfica não é tarefa simples, e, para a maioria das aplicações de agente, a busca vetorial com filtragem de metadados cumpre o mesmo objetivo com menos infraestrutura.

Memória gerida frameworks como Mem0 e Letta (O anteriormente conhecido como MemGPT) trata por si da extração, consolidação e recuperação pipeline. A abordagem do Mem0 é notável: um LLM extrai as memórias candidatas, um motor de decisão compara cada novo fato com as entradas existentes no armazenamento vetorial, e um resolvedor decide se deve adicionar, atualizar ou eliminar essas informações, o que mantém o armazém de memórias coerente e sem redundâncias. O Letta adota uma perspetiva inspirada em sistemas operativos: os agentes gerem a sua própria janela de contexto utilizando ferramentas de gestão de memória, movendo autonomamente dados entre a “memória principal” (dentro do contexto) e a “memória de arquivamento” (fora do contexto). Ambas as soluções merecem ser avaliadas caso pretenda acelerar o tempo até à produção sem necessidade de controlo total sobre a gestão da memória pipeline.


Memória de documento: o armário de arquivamento do agente

A memória baseada em ficheiros apresenta uma adoção por parte de mais produtos do que a cobertura nas taxonomias de memória mencionadas anteriormente. Numa avaliação LoCoMo realizada por um fornecedor, Letta informou 74,0% devido à abordagem baseada em ficheiro sistema que utiliza. É possível manter esse resultado dentro das condições do seu modelo, benchmark e harness, mas a vantagem operacional é facilmente perceptível: os desenvolvedores podem ler, editar e comparar diferenças no conhecimento armazenado diretamente.

Janelas de contexto mais extensas também tornam viável a leitura de ficheiros inteiros para alguns documentos de projeto. A recuperação em blocos continua a ser adequada para grandes corpora, mas um ficheiro curto com convenções ou informações de transferência pode frequentemente ser carregado diretamente. A escolha depende do tamanho do documento, da precisão da recuperação, do orçamento de contexto e da frequência com que as pessoas precisam de rever ou editar essa memória.

Os armazenamentos vetoriais e os backends de tipo chave-valor lidam bem com a recuperação semântica e com buscas estruturadas. No entanto, existe uma terceira categoria de conhecimento dos agentes que nenhum desses sistemas consegue tratar de forma eficaz: o contexto acumulado do projeto, que inclui convenções, notas de pesquisa e decisões de que o agente necessita ao longo das sessões, e que se beneficia de ser legível por humanos e estar sujeito a controlo de versões.

Isto é memória de documento: o agente lê e escreve ficheiros estruturados (Markdown, JSON, YAML) num diretório conhecido. Sem embeddings, sem base de dados, sem infraestrutura. Apenas ficheiros no disco que tanto o agente como o desenvolvedor podem aceder. cat, grep, git diffe editar manualmente.

Porquê ficheiros?

Em fluxos de trabalho de agentes de longa duração, o padrão mais eficaz que já observei não é uma base de dados vetorial. Trata-se de um diretório com notas bem organizadas. Pense no que acontece quando um agente de programação trabalha num projeto ao longo de semanas:

Estes factos têm uma estrutura demasiado complexa para serem pesquisados por vetores (é necessária uma recuperação exata, e não uma semelhança difusa), e são demasiado numerosos para serem armazenados num repositório tipo chave-valor (eles formam documentos interligados, e não factos isolados). Além disso, são factos que o desenvolvedor deseja visualizar e editar diretamente. Se o agente aprender algo incorretamente, basta abrir o ficheiro e corrigi-lo.

Isto é como do Claude Code CLAUDE.md e .claude/ Trabalho com diretórios. O agente lê o nível do projeto CLAUDE.md arquivos para convenções e instruções, e efetua escritas em ~/.claude/MEMORY.md para o aprendizado entre sessões. Os ficheiros são em Markdown puro: você lê-os, edita-os, faz commit no Git e partilha-os com a sua equipa. cursor do .cursorrules e Windsurfar .windsurfrules ficheiros de texto puro que o agente carrega na inicialização para obter o contexto do projeto.

Implementação de um armazenamento em memória para ficheiros

A implementação é deliberadamente simples. O agente dispõe de quatro operações: escrever um documento, ler um documento, listar os documentos disponíveis e realizar uma busca em vários documentos por palavra-chave.

De file_memory.py:

from pathlib import Path
import json
import fnmatch

class FileMemory:
    """Document memory backed by the local filesystem.

    Stores agent knowledge as human-readable files organized by topic.
    No embeddings, no database — just files that both the agent and
    the developer can read, edit, and version-control.
    """

    def __init__(self, base_dir: str | Path):
        self.base_dir = Path(base_dir)
        self.base_dir.mkdir(parents=True, exist_ok=True)

    def write_doc(self, path: str, content: str, metadata: dict | None = None):
        """Write or overwrite a document at the given path.

        Paths are relative to base_dir. Directories are created automatically.
        Metadata (if provided) is stored as a JSON sidecar file.
        """
        full_path = self.base_dir / path
        full_path.parent.mkdir(parents=True, exist_ok=True)
        full_path.write_text(content, encoding="utf-8")

        if metadata:
            meta_path = full_path.with_suffix(full_path.suffix + ".meta")
            meta_path.write_text(json.dumps(metadata, indent=2), encoding="utf-8")

    def read_doc(self, path: str) -> str | None:
        """Read a document by path. Returns None if not found."""
        full_path = self.base_dir / path
        if full_path.exists():
            return full_path.read_text(encoding="utf-8")
        return None

    def list_docs(self, pattern: str = "**/*") -> list[str]:
        """List documents matching a glob pattern."""
        return [
            str(p.relative_to(self.base_dir))
            for p in self.base_dir.glob(pattern)
            if p.is_file() and not p.name.endswith(".meta")
        ]

    def search_docs(self, query: str, pattern: str = "**/*.md") -> list[dict]:
        """Search documents by keyword. Returns matching files with context.

        This is intentionally simple — grep-style keyword search.
        For semantic search, use a vector store instead.

        NOTE: This is a sketch for demonstration. A simple substring check
        won't scale beyond a few hundred documents. For production with 500+
        documents, use TF-IDF/BM25 scoring (e.g., rank_bm25) or a full-text
        search backend (PostgreSQL GIN index, Elasticsearch).
        """
        results = []
        for path in self.base_dir.glob(pattern):
            if not path.is_file() or path.name.endswith(".meta"):
                continue
            content = path.read_text(encoding="utf-8")
            if query.lower() in content.lower():
                # Return the paragraph containing the match for context
                for paragraph in content.split("\n\n"):
                    if query.lower() in paragraph.lower():
                        results.append({
                            "path": str(path.relative_to(self.base_dir)),
                            "match": paragraph.strip()[:500],
                        })
        return results

Estrutura de pastas

A maior parte do valor da memória de documento advém da forma como o diretório é estruturado. Eis a estrutura que utilizo para o Agente de Análise de Mercado:

.agent-memory/
    README.md                  # What this directory is, for human readers
    user-profiles/
        user-123.md            # Preferences, history, risk profile
        user-456.md
    research/
        NVDA-2026-02.md        # Research notes from recent analysis
        TSLA-2026-01.md
    conventions/
        analysis-format.md     # How to structure analysis reports
        data-sources.md        # Preferred data sources and API patterns
    learnings/
        common-errors.md       # Mistakes the agent has learned to avoid
        tool-patterns.md       # Effective tool call sequences

Cada ficheiro é em formato Markdown. A finalidade de cada ficheiro é evidente a partir do seu caminho. Pode git diff o diretório inteiro de memória para ver o que o agente aprendeu numa sessão, git revert Um mau processo de aprendizagem pode ocorrer se o diretório for copiado para outro projeto. Experimente realizar qualquer uma dessas ações com uma coleção Qdrant.

Quando utilizar memória de documento, vetores ou estrutura chave-valor

Os três backends de memória suportam padrões de acesso diferentes:

DimensãoArmazém de VetoresArmazém Chave-ValorArmazém de Documentos
Padrão de consulta”Encontrar factos semelhantes a X”Obter o valor para a chave”Leia o documento localizado no caminho indicado.”
Ideal paraRecuperação não estruturada e variadaConsultas estruturadasContexto do projeto, notas
Legível para humanosNão (embeddings)Parcialmente (JSON)Sim (Markdown)
Possível de depurarDifícil (escores de similaridade)Trivial (abrir o ficheiro)
Versão controlávelNãoPossível
Embedding infraestruturaObrigatórioNão é necessárioNão é necessário
Escala paraMilhões de factosMilhões de chavesMilhares de documentos
Capacidade de buscaSemelhança semânticaCorrespondência exataBaseado em palavra-chave/caminho

Utilize a memória de documento quando:

Utilize armazenamentos vetoriais quando:

Utilize armazenamentos de tipo chave-valor quando:

Na prática, os agentes de produção costumam combinar os três tipos. O Agente de Análise de Mercado Utiliza o PostgreSQL checkpoints para armazenamento em memória rápida, o Qdrant para recuperação eficiente de factos semânticos relacionados aos utilizadores, e um repositório de documentos baseado em ficheiros para guardar as convenções do projeto e as notas de investigação.

Exemplos do mundo real

O padrão já está amplamente difundido em assistentes de codificação AI:

O fator comum: todos eles armazenam o conhecimento do agente como ficheiros de texto legíveis por humanos, com operações de leitura/escrita explícitas. Sem embeddings. Sem infraestrutura vetorial. O agente decide o que escrever, o desenvolvedor pode visualizar e editar tudo, e todo o sistema cabe em um git diff.

Para além dos assistentes de programação

A memória de documentação não se limita aos agentes de programação. Este padrão é observado em domínios de agentes muito diversos:

O Workshop MemAgents no ICLR 2026 É um sinal de que a comunidade de investigação está a alcançar o que os profissionais já desenvolveram. O Document Memory superou claramente as suas origens como ferramenta de assistência à programação.

As competências utilizam documentos para embalar instruções procedimentais. O Padrão de Habilidades de Agente armazena essas instruções em SKILL.md ficheiros com frontmatter em YAML e um corpo em Markdown. Isso assemelha-se à memória de documento a nível da camada de armazenamento, mas o seu papel é diferente: uma habilidade indica ao agente como realizar um determinado tipo de tarefa, enquanto a memória regista factos aprendidos com um projeto ou com execuções anteriores. Parte 3 aborda essa distinção do ponto de vista da ferramenta.

MCP (Protocolo de Contexto do Modelo) segue na mesma direção: as definições de ferramentas são ficheiros JSON Schema que qualquer agente pode descobrir e utilizar. O protocolo possui 97 milhões de downloads mensais SDK é suportado pela OpenAI, Google, Microsoft e AWS. O MCP não é específico de programação. Os mesmos servidores conectam os agentes a bases de dados, APIs internos e sistemas empresariais.

Ambos apontam para o mesmo padrão: conhecimento procedural armazenado como documentos sujeitos a esquemas, com operações de leitura/escrevimento explícitas. MCP, agora regulado pelos Agentic AI Fundação, É o que existe de mais próximo de um padrão de interoperabilidade no ecossistema de agentes.

Escalonamento da memória de documento para produção

A implementação baseada em ficheiros apresentada acima funciona bem em portáteis de desenvolvedores individuais e em implementações em pequena escala. Uma produção multi-tenant com centenas de utilizadores e milhares de documentos exige uma arquitetura completamente diferente.

O limite de ficheiros em nodo único torna-se evidente: não é possível escalar as operações de E/S de ficheiros horizontalmente, as escritas concorrentes exigem bloqueio, e gerir permissões entre diferentes utilizadores é complexo. A produção necessita de um armazenamento de suporte que gere corretamente a concorrência, a pesquisa e o modelo de multi-tenant.

Três abordagens comuns:

Abordagem A: híbrida com uma camada de base de dados leve

Mantenha os ficheiros para criação (os desenvolvedores editam o Markdown localmente), mas sirva-os a partir de uma base de dados em runtime. Durante a implementação, sincronize os ficheiros com as linhas da PostgreSQL. O agente lê a informação diretamente da base de dados, e não do disco. Isto permite-lhe:

Abordagem B: armazenamento de objetos + sidecar de índice vetorial

Os documentos são armazenados no S3/GCS na forma de objetos, sendo que existe uma coleção Qdrant responsável por indexar os seus embeddings. O agente consulta o Qdrant em busca dos IDs dos documentos relevantes e, em seguida, retira o conteúdo a partir do armazenamento de objetos. Esta arquitetura permite escalabilidade horizontal e suporta pesquisas semânticas, mas acarreta alguma complexidade: é necessário gerir dois sistemas distintos, realizar uma embedding pipeline constante para mantê-los sincronizados, e lidar com a consistência eventual entre o armazenamento e o índice.

Abordagem C: armazenamento estruturado de documentos com PostgreSQL (recomendado)

Armazene documentos como linhas JSONB no PostgreSQL, aproveitando a busca por texto completo (índice GIN) e, opcionalmente, vetores embeddings (pgvector). Isto permite-lhe realizar uma busca híbrida (por palavras-chave + semântica), utilizar transações ACID e contar com um único sistema operacional.

Esboço da Abordagem C:

from typing import Optional
import asyncpg

class ProductionDocumentMemory:
    """PostgreSQL-backed document memory with hybrid search.

    Schema:
        CREATE TABLE documents (
            id SERIAL PRIMARY KEY,
            tenant_id TEXT NOT NULL,
            path TEXT NOT NULL,
            content TEXT NOT NULL,
            metadata JSONB,
            embedding vector(1536),  -- pgvector extension
            ts_vector tsvector GENERATED ALWAYS AS (to_tsvector('english', content)) STORED,
            created_at TIMESTAMPTZ DEFAULT NOW(),
            UNIQUE(tenant_id, path)
        );
        CREATE INDEX ON documents USING GIN(ts_vector);
        CREATE INDEX ON documents USING ivfflat(embedding vector_cosine_ops);
    """

    def __init__(self, pool: asyncpg.Pool):
        self.pool = pool

    async def write(
        self,
        tenant_id: str,
        path: str,
        content: str,
        metadata: Optional[dict] = None,
        embedding: Optional[list[float]] = None,
    ):
        """Write or update a document."""
        async with self.pool.acquire() as conn:
            await conn.execute(
                """
                INSERT INTO documents (tenant_id, path, content, metadata, embedding)
                VALUES ($1, $2, $3, $4, $5)
                ON CONFLICT (tenant_id, path) DO UPDATE
                SET content = EXCLUDED.content,
                    metadata = EXCLUDED.metadata,
                    embedding = EXCLUDED.embedding
                """,
                tenant_id, path, content, metadata, embedding,
            )

    async def search(
        self,
        tenant_id: str,
        query: str,
        embedding: Optional[list[float]] = None,
        limit: int = 5,
    ) -> list[dict]:
        """Hybrid search: full-text + optional vector similarity."""
        async with self.pool.acquire() as conn:
            if embedding:
                # Hybrid scoring: 0.6 * text relevance + 0.4 * vector similarity
                rows = await conn.fetch(
                    """
                    SELECT path, content, metadata,
                           (0.6 * ts_rank(ts_vector, plainto_tsquery('english', $2)) +
                            0.4 * (1 - (embedding <=> $3))) AS score
                    FROM documents
                    WHERE tenant_id = $1
                      AND ts_vector @@ plainto_tsquery('english', $2)
                    ORDER BY score DESC
                    LIMIT $4
                    """,
                    tenant_id, query, embedding, limit,
                )
            else:
                # Full-text search only
                rows = await conn.fetch(
                    """
                    SELECT path, content, metadata,
                           ts_rank(ts_vector, plainto_tsquery('english', $2)) AS score
                    FROM documents
                    WHERE tenant_id = $1
                      AND ts_vector @@ plainto_tsquery('english', $2)
                    ORDER BY score DESC
                    LIMIT $3
                    """,
                    tenant_id, query, limit,
                )
            return [dict(row) for row in rows]

O que recebe:

Os ficheiros são ideais para fluxos de trabalho com um único desenvolvedor. Em ambientes de produção com vários utilizadores, um armazenamento de documentos estruturado no PostgreSQL costuma oferecer o equilíbrio ideal entre simplicidade, desempenho e maturidade operacional.


Montagem final: a arquitetura completa

Aqui está como os três níveis de memória funcionam em conjunto no Agente de Análise de Mercado. O diagrama ilustra o fluxo completo, desde o pedido do utilizador até à resposta, com todas as camadas de memória ativas.

Arquitetura de Memória Total

A arquitetura possui três caminhos de memória:

  1. Caminho crítico (checkpoint de armazenamento): Cada nó no LangGraph grava o seu estado de grafo suscetível a retomada no checkpoint de armazenamento. Quando o grafo atinge um interrupt_before node (tal como o relator em Parte 1), Pausas na execução. O utilizador pode fechar a aplicação e, quando regressar, o gráfico é retomado a partir do checkpoint. Os registos de eventos e os rastreios Runtime representam questões distintas de produção.

  2. Caminho frio (armazenamento de longo prazo): No início de cada conversa, o agente consulta o armazenamento de longo prazo em busca de contexto do utilizador relevante. No final, ele extrai e guarda novos factos. Este processo é executado de forma assíncrona — não deve, em nenhum caso, bloquear o reasoning loop principal.

  3. Caminho do documento (armazenamento de ficheiros): Na inicialização, o agente carrega as convenções do projeto e as notas de investigação relevantes a partir do armazenamento de documentos. Durante a execução, ele grava novos resumos de investigação e padrões aprendidos de volta no disco. Ao contrário do caminho “frio”, as leituras de documentos são síncronas (pois fornecem informações para a tarefa atual), enquanto as escritas podem ser adiadas.

A estrutura de conexões no LangGraph é bastante simples — o checkpoint store e o long-term store são transmitidos durante a compilação do grafo, enquanto o document store é inserido como uma dependência. O esboço local abaixo utiliza InMemoryStore Assim, o trecho permanece compacto; a topologia Docker de referência utiliza o Qdrant para desempenhar a mesma função de recuperação semântica.

from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
from langgraph.store.memory import InMemoryStore

# Hot memory: PostgreSQL for durable checkpoints
checkpointer = await create_postgres_checkpointer(pg_connection_string)

# Cold memory: local sketch with vector search
# (The reference Docker topology uses Qdrant for persistent recall.)
memory_store = InMemoryStore(
    index={"dims": 1536, "embed": embedding_function}
)

# Document memory: file-based store for project knowledge
doc_memory = FileMemory(base_dir=".agent-memory")

# Checkpoint store and long-term store wired into the graph
graph = create_graph(
    checkpointer=checkpointer,
    store=memory_store,
)

# The store is accessible inside any node via the store parameter
def planner_node(state: AgentState, *, store: BaseStore) -> dict:
    """Plan with user context from long-term memory."""

    # Recall relevant user facts from vector store
    user_memories = store.search(
        namespace=("user", state.user_id),
        query=state.messages[-1].content,
        limit=5,
    )

    # Load project conventions from document memory
    conventions = doc_memory.read_doc("conventions/analysis-format.md")

    # Inject both into planning context
    memory_context = "\n".join(m.value["fact"] for m in user_memories)
    # ... rest of planning logic with personalized context and conventions

O fluxo completo

O que acontece quando um utilizador recorrente envia “Analyze TSLA” para o Agente de Análise de Mercado:

  1. Carga de memória dos documentos: Na inicialização, o agente lê as convenções do projeto armazenadas no repositório de documentos: preferências de formato de análise, fontes de dados preferidas e padrões de utilização de ferramentas. Estes fatores definem o comportamento de base.

  2. Recuperação de memória a frio: Antes de o nó roteador ser executado, o grafo consulta o armazenamento de longo prazo em busca da mensagem do utilizador. É recuperada a seguinte informação: “O utilizador tem uma alta tolerância ao risco”, “O utilizador prefere análises detalhadas dos concorrentes” e “O utilizador já investigou a NVDA e a AMD”.

  3. Router + Planner: A classe de router classifica isto como DEEP_RESEARCHO planeador cria um plano de investigação composto por 5 passos, personalizado de acordo com as preferências identificadas. Inclui uma etapa de análise da concorrência, uma vez que o histórico do utilizador indica que este deseja esse tipo de análise. O plano segue o formato definido no documento de convenções.

  4. Loop do executor (memória ativa): Cada passo é executado através do padrão ReAct a partir de Parte 1. Após cada nó (roteador, planeador e cada passo do executor), o LangGraph escreve um checkpoint no PostgreSQL. Se o processo falhar após o passo 3 de um total de 5, basta reiniciá‑lo para continuar a partir do passo 4.

  5. Interrupção HITL: o gráfico atinge o reporter nó com interrupt_before. O rascunho do relatório encontra-se no checkpoint. O utilizador analisa-o algumas horas depois, e o gráfico carrega o checkpoint e continua a ser exibido.

  6. Atualizações de memória: Após o término da conversa: (a) um processo assíncrono extrai novos factos do utilizador (“o utilizador está agora a seguir a TSLA”, “o utilizador aprovou o formato do relatório”) e armazena-os no repositório de vetores de longo prazo, e (b) o agente escreve um resumo da investigação no repositório de documentos.research/TSLA-2026-02.md) para referência futura.

O padrão de três camadas separa as responsabilidades de forma clara. O checkpoint store gere a durabilidade e a retomada de operações; trata-se da infraestrutura. O armazenamento de longo prazo é responsável pela personalização; corresponde à lógica do produto. O armazenamento de documentos contém o conhecimento acumulado sobre os projetos; funciona como o caderno de anotações do agente.


Compromissos e considerações

A memória agrega valor, mas também aumenta os custos e a complexidade. Seja honesto quanto aos compromissos envolvidos:


Principais conclusões

  1. A memória do agente é composta por vários repositórios com padrões de acesso distintos. É necessário manter separados o checkpoints recuperável, os factos estruturados, a recuperação semântica e os documentos do projeto.
  2. Implementar funcionalidades de pausa e retomada antes da personalização. A perda do progresso das tarefas é o primeiro tipo de falha de memória que um agente em execução prolongada apresenta.
  3. Armazenar factos determinísticos em estruturas organizadas. Utilizar busca vetorial quando a consulta for imprecisa ou a formulação variar.
  4. Utilizar ficheiros para armazenar conhecimentos de projetos que precisem de ser inspecionados, editados, versionados ou comparados por diferenças.
  5. Definir uma data de expiração, regras de conflito e regras de eliminação para cada tipo de memória. Uma memória que o sistema não consegue corrigir torna-se dívida técnica.
  6. Limitar o que é retornado ao modelo. A memória armazenada só tem valor quando a sua recuperação fornece as evidências adequadas para o contexto atual.

A próxima camada corresponde à ação

Parte 3, AI Agente Tool Use em 2026, Passa do estado armazenado para uma ação. Aborda como um agente descobre e utiliza ferramentas, bem como como os limites dessas ferramentas retornam erros que o reasoning loop pode processar. As partes 5 e 6 voltam a tratar da memória do ponto de vista operacional: o runtime restaura um checkpoint, enquanto o harness decide quais elementos devem ser transmitidos para a próxima sessão do modelo.

Referências

Artigos Científicos

Documentação do LangGraph

Checkpoint backends

Bases de dados vetoriais e ferramentas de memória

Memória baseada em documentos e ficheiros

Memória frameworks

Benchmarks

Workshops

Projeto de demonstração


O código completo do Market Analyst Agent, incluindo a arquitetura de memória descrita neste artigo, encontra-se em GitHub se quiser seguir a leitura.

Série: Engenharia da pilha Agentic