Memory
Memória permite conservar informação útil entre etapas ou interações, mas também pode perpetuar erros e dados fora do escopo. Nesta semana você desenhará uma política de armazenamento, recuperação e esquecimento. A questão não é guardar tudo, e sim guardar o que tem finalidade e origem claras.
O caso é um assistente de suporte que lembra preferência de idioma e fatos de atendimentos anteriores. Preferência durável, relato de evento e estado financeiro atual têm regras diferentes. Você aprenderá a não transformar histórico em verdade permanente e a impedir mistura entre usuários ou empresas.
JavaScriptAo terminar esta aula
- Memória durável exige política de dados.
- Vetores e relações atendem operações diferentes.
- Esquecimento precisa considerar cópias e escopos.
Antes de continuar: Laboratório: RAG evals e citações
Short-term e long-term memory
FundamentosShort-term memory acompanha continuidade da interação ou execução, como histórico e estado de uma thread. Long-term memory conserva informação entre interações conforme a aplicação. Esses conceitos não correspondem automaticamente a uma capacidade interna ilimitada do modelo. A persistência, o namespace e o método de recuperação precisam ser implementados ou configurados. Uma sessão continuada por API não oferece, por si só, uma política completa de memória durável.
↗ LangChain Memory overview↗ LangGraph Persistence↗ LangGraph Memory
No suporte, o pedido atual e suas pendências pertencem ao atendimento. Preferência de idioma pode ser reutilizada em atendimentos futuros com finalidade explícita. Pagamento confirmado é um estado que pode mudar e costuma exigir consulta atual, não reprodução cega de uma nota antiga. Separe fatos declarados, observados e inferidos. Registre origem e tempo. A memória útil aumenta continuidade, mas a memória indiscriminada amplia exposição e risco de aplicar informação antiga à tarefa errada.
↗ LangChain Memory overview↗ LangGraph Persistence↗ LangGraph Memory
Memória episódica e semântica
FundamentosMemória episódica registra eventos ou experiências, como um atendimento em que uma consulta falhou. Memória semântica armazena informação generalizada, como preferência ou fato de perfil. Essa nomenclatura é uma organização de dados do sistema; não prova que o modelo possui memória humana. Um evento específico pode informar uma preferência, mas a generalização precisa de critério. Um pedido em inglês não demonstra necessariamente preferência permanente por inglês.
↗ LangChain Memory overview↗ Effective context engineering for AI agents
Defina campos por tipo: episódio tem eventoId, data, escopo e referência; preferência tem chave, valor, origem, atualização e política de validade. Consolidação pode transformar registros repetidos em informação resumida, mas deve preservar correções e conflitos. Não faça uma votação de frases repetidas para definir verdade. Duas notas copiadas do mesmo erro não são duas evidências independentes. Quando a origem é ambígua, mantenha incerteza ou peça confirmação em vez de gravar um fato forte.
↗ LangChain Memory overview↗ Effective context engineering for AI agents
Vector versus relational memory
FundamentosMemória vetorial ajuda a recuperar conteúdo por proximidade, útil para episódios narrativos e descrições. Memória relacional organiza fatos estruturados, chaves, versões e relações, útil para preferência e escopo. Essas abordagens podem coexistir. Um vetor não oferece sozinho atualização exata de preferência ou autorização por usuário. Um campo relacional facilita consultar idioma vigente, enquanto um índice vetorial pode localizar um episódio relacionado ao problema atual.
↗ pgvector README↗ PostgreSQL Row Security Policies↗ LangChain Memory overview
Use identificadores e filtros confiáveis em toda recuperação. PostgreSQL pode aplicar políticas de row-level security conforme configuração, com particularidades de papéis e propriedade das tabelas que precisam ser consideradas. Uma coluna tenantId não é proteção automática. Teste usuário comum, papel de serviço e caminhos que possam contornar a política. Em pgvector, proximidade funciona dentro do conjunto elegível. Buscar globalmente e filtrar só depois pode expor dados ao modelo. O desenho de memória retoma os contratos de acesso vistos nas ferramentas.
↗ pgvector README↗ PostgreSQL Row Security Policies↗ LangChain Memory overview
Summaries, consolidation e retrieval
FundamentosResumos reduzem histórico, preservando objetivo, decisões, fatos com origem e pendências. Consolidação aplica uma política de atualização ou combinação para reduzir redundância e manter informação útil. Não existe algoritmo universal adequado a todo domínio. Para preferência de idioma, uma confirmação nova pode substituir a antiga. Para episódios, você pode agrupar eventos relacionados mantendo referências, sem apagar datas ou transformar tentativa em conclusão.
↗ LangGraph Memory↗ Effective context engineering for AI agents↗ OpenAI Retrieval
Retrieval seleciona memórias relevantes para a tarefa e o escopo. Inclua prioridade, vigência e confiabilidade, além de similaridade. Um episódio de outro produto pode ser semanticamente próximo, mas inadequado ao pedido atual. Contexto recuperado deve informar que se trata de histórico, não de consulta ao estado presente. Avalie perguntas com correção e dois usuários semelhantes. Se a compactação perde uma restrição, recupere o artefato original. O orçamento de contexto limita quanto será enviado, mas não justifica misturar informações incompatíveis para criar um resumo curto.
↗ LangGraph Memory↗ Effective context engineering for AI agents↗ OpenAI Retrieval
TTL, forgetting e privacidade
FundamentosTTL define duração ou expiração de um registro conforme o armazenamento. No Redis, EXPIRE estabelece um timeout para uma chave, sujeito ao contrato do comando. Expirar em um sistema não elimina automaticamente cópias em logs, backups, vetores ou outro banco. Forgetting precisa de um fluxo que identifique onde a informação foi replicada e qual política de remoção se aplica. Não prometa esquecimento completo baseado em um único campo expiresAt.
↗ Redis EXPIRE↗ PostgreSQL Row Security Policies↗ Safety in building agents↗ LangGraph Memory
Privacidade começa na finalidade: por que guardar, para qual usuário ou empresa e por quanto tempo? Evite persistir segredos e dados desnecessários. Implemente atualização, inspeção e remoção conforme o produto. Uma memória com informação sensível deve ter acesso controlado e retenção explícita. Conteúdo externo ou gerado pode tentar criar instruções persistentes; valide o que pode ser gravado. A aceitação inclui recuperação no escopo certo, correção de preferência, expiração e remoção coordenada. Lembrar corretamente e esquecer corretamente são partes do mesmo contrato.
↗ Redis EXPIRE↗ PostgreSQL Row Security Policies↗ Safety in building agents↗ LangGraph Memory
Exemplo comentado e limites
FundamentosOs tempos são unidades fictícias para tornar a expiração determinística. A função filtra escopo e validade, mas não remove registros da lista. Isso distingue não recuperar um dado expirado de apagá-lo fisicamente. Uma implementação real precisa de política de armazenamento e remoção.
const memorias = [
{usuario:"u1",tipo:"preferencia",chave:"idioma",valor:"pt",origem:"confirmacao",expiresAt:2000},
{usuario:"u2",tipo:"preferencia",chave:"idioma",valor:"en",origem:"confirmacao",expiresAt:3000}
];
function recuperar(usuario,agora) {
return memorias.filter(m=>m.usuario===usuario && m.expiresAt>agora);
}
console.log("u1 vigente",recuperar("u1",1000));
console.log("u1 expirado",recuperar("u1",2500));A origem confirmação explica por que a preferência foi gravada. O programa não conclui preferência permanente a partir de uma conversa isolada. Ele também não oferece autorização real: o usuário passado à função precisa vir de contexto autenticado, não dos argumentos livres do modelo.
Exercício aplicado
Armazene preferência de idioma com origem e expiração e impeça que outro usuário a recupere.
- Tente classificar informações e finalidade.
- Execute filtros e correções de preferência.
- Consolide episódios preservando referências.
- Implemente remoção local e mapeie cópias externas.
Abrir resolução comentada
A solução mantém sessão, preferências, episódios, resumo e índice derivado em coleções separadas. O namespace usa empresa e usuário. corrigirPreferencia aceita somente idioma confirmado, substitui a versão anterior e atualiza o registro derivado. Os asserts comprovam correção com origem, isolamento de usuários e isolamento de usuários com o mesmo identificador em empresas distintas.
registrarEpisodio usa uma allowlist de campos, descartando texto bruto e informação desnecessária. consolidar mantém referências aos episódios, datas, estados e origem, sem transformar consulta-falhou em acesso concluído. O índice derivado do exemplo é textual e local: não foram gerados embeddings nem consultado um banco vetorial. Ele representa uma cópia que também precisa ser atualizada e removida.
recuperar aplica validade sem apagar os dados. O assert seguinte mostra a preferência expirada ainda no Map. removerExpirados realiza remoção física das coleções locais e atualiza referências. esquecerEscopo remove preferências, episódios, índice derivado, resumo e sessão apenas do namespace autorizado. Os testes comprovam que outros usuários e empresas permanecem intactos.
O relatório de esquecimento enumera cópias locais removidas e registra backups ou serviços externos como fora do exemplo, exigindo política própria. A memória em Map não persiste entre reinícios e não implementa RLS: uma integração real precisa de armazenamento, autorização e remoção coordenada documentados. O exemplo executa correção, consolidação, expiração e exclusão locais sem prometer apagamento de sistemas que não acessou.
import assert from "node:assert/strict";
const preferencias=new Map();const episodios=new Map();const resumos=new Map();
const indiceDerivado=new Map();const sessoes=new Map();
const ns=ctx=>JSON.stringify([ctx.empresa,ctx.usuario]);
const chave=(ctx,nome)=>ns(ctx)+"|"+nome;
function corrigirPreferencia(ctx,{nome,valor,origem,expiresAt}){
if(nome!=="idioma"||!['pt','en','es'].includes(valor)||origem!=="confirmacao-usuario")
throw new Error("MEMORIA_NAO_CONFIRMADA");
const id=chave(ctx,nome),anterior=preferencias.get(id);
const item={id,namespace:ns(ctx),tipo:"preferencia",nome,valor,origem,
versao:(anterior?.versao??0)+1,expiresAt};
preferencias.set(id,item);indiceDerivado.set(id,{namespace:ns(ctx),origemId:id,texto:nome+":"+valor});
return item;
}
function registrarEpisodio(ctx,entrada){
const id=chave(ctx,entrada.eventoId);
// Allowlist: não armazenar entrada.textoBruto, senha ou outros campos livres.
const evento={id,namespace:ns(ctx),tipo:"episodio",eventoId:entrada.eventoId,
atendimentoId:entrada.atendimentoId,status:entrada.status,origem:entrada.origem,
data:entrada.data,expiresAt:entrada.expiresAt};
episodios.set(id,evento);indiceDerivado.set(id,{namespace:ns(ctx),origemId:id,
texto:evento.atendimentoId+":"+evento.status});return evento;
}
function consolidar(ctx){
const itens=[...episodios.values()].filter(x=>x.namespace===ns(ctx));
const resumo={namespace:ns(ctx),referencias:itens.map(x=>x.id),
fatos:itens.map(x=>({eventoId:x.eventoId,data:x.data,status:x.status,origem:x.origem}))};
resumos.set(ns(ctx),resumo);return resumo;
}
function recuperar(ctx,agora){
return [...preferencias.values(),...episodios.values()]
.filter(x=>x.namespace===ns(ctx)&&x.expiresAt>agora);
}
function removerRegistro(ctx,id){
const item=preferencias.get(id)??episodios.get(id);
if(!item) return false;
if(item.namespace!==ns(ctx)) throw new Error("SEM_PERMISSAO");
preferencias.delete(id);episodios.delete(id);indiceDerivado.delete(id);
// Reconstruir resumo a partir dos episódios restantes remove referências obsoletas.
consolidar(ctx);return true;
}
function removerExpirados(ctx,agora){
const ids=[...preferencias.values(),...episodios.values()]
.filter(x=>x.namespace===ns(ctx)&&x.expiresAt<=agora).map(x=>x.id);
ids.forEach(id=>removerRegistro(ctx,id));return ids.length;
}
function esquecerEscopo(ctx){
const ids=[...preferencias.values(),...episodios.values()]
.filter(x=>x.namespace===ns(ctx)).map(x=>x.id);
ids.forEach(id=>removerRegistro(ctx,id));resumos.delete(ns(ctx));sessoes.delete(ns(ctx));
return {removidos:ids.length,locais:["preferencias","episodios","indiceDerivado","resumo","sessao"],
externos:[{sistema:"backups-ou-servicos-externos",status:"fora-do-exemplo; requer política própria"}]};
}
const a={empresa:"A",usuario:"u1"},b={empresa:"B",usuario:"u1"},c={empresa:"A",usuario:"u2"};
sessoes.set(ns(a),{objetivo:"consultar acesso",pendencias:["consultar matrícula"]});
const p1=corrigirPreferencia(a,{nome:"idioma",valor:"pt",origem:"confirmacao-usuario",expiresAt:2000});
corrigirPreferencia(b,{nome:"idioma",valor:"en",origem:"confirmacao-usuario",expiresAt:4000});
corrigirPreferencia(c,{nome:"idioma",valor:"es",origem:"confirmacao-usuario",expiresAt:4000});
assert.equal(recuperar(a,1000).length,1);assert.equal(recuperar(b,1000)[0].valor,"en");
const p2=corrigirPreferencia(a,{nome:"idioma",valor:"en",origem:"confirmacao-usuario",expiresAt:2000});
assert.equal(p2.versao,2);assert.equal(preferencias.size,3);
assert.equal(recuperar(a,1000)[0].origem,"confirmacao-usuario");
assert.throws(()=>corrigirPreferencia(a,{nome:"idioma",valor:"pt",origem:"inferida",expiresAt:2000}),
/MEMORIA_NAO_CONFIRMADA/);
const e=registrarEpisodio(a,{eventoId:"ev1",atendimentoId:"at1",status:"consulta-falhou",
origem:"ferramenta-autorizada",data:10,expiresAt:3000,textoBruto:"senha fictícia desnecessária"});
const resumo=consolidar(a);assert.deepEqual(resumo.referencias,[e.id]);
assert.equal(resumo.fatos[0].status,"consulta-falhou");
assert.equal(JSON.stringify([...episodios.values()]).includes("senha"),false);
assert.equal(recuperar(a,2500).some(x=>x.tipo==="preferencia"),false);
assert.ok(preferencias.has(p1.id)); // Expiração lógica não remove fisicamente.
assert.equal(removerExpirados(a,2500),1);
assert.equal(preferencias.has(p1.id),false);assert.equal(indiceDerivado.has(p1.id),false);
assert.throws(()=>removerRegistro(b,e.id),/SEM_PERMISSAO/);
const relatorio=esquecerEscopo(a);
assert.equal(recuperar(a,1000).length,0);assert.equal(resumos.has(ns(a)),false);
assert.equal(sessoes.has(ns(a)),false);
assert.equal([...indiceDerivado.values()].some(x=>x.namespace===ns(a)),false);
assert.equal(recuperar(b,1000).length,1);assert.equal(recuperar(c,1000).length,1);
console.log({status:"aprovado",relatorio,memoriasRestantes:preferencias.size});Como conferir seu resultado
- Usuários e empresas permanecem isolados.
- Preferência pode ser corrigida com origem.
- Expiração e remoção física são distintas.
Aplique em um problema novo
Primeiro resolva sem consultar a resposta. Explique suas decisões e guarde a evidência. A conclusão de leitura é independente desta autoavaliação.
Confira seus pré-requisitos
- Distinguir preferência/episódio/sessão.
- Aplicar TTL/namespace/origem.
Idioma pt corrigido es, expira 50; outro tenant usa mesmo userId. Em 60, recuperar/apagar o quê?
Conferir raciocínio e critérios de domínio
Correção es pertence somente ao namespace certo e conserva origem.
Em 60 está fora de recuperação; expiração não prova remoção física.
Remover cópias/derivados do escopo sem afetar homônimo; backups precisam política própria.
Evidências para autoavaliação ou revisão por pares
- Correção: Correção es pertence somente ao namespace certo e conserva origem.
- Tempo: Em 60 está fora de recuperação; expiração não prova remoção física.
- Esquecimento: Remover cópias/derivados do escopo sem afetar homônimo; backups precisam política própria.
Um erro frequente
TTL apaga todas cópias.
TTL pode apenas filtrar recuperação; exclusão física é outra operação.
Teste sua compreensão
Responda com suas palavras antes de abrir o comentário. Saber explicar uma decisão é parte do domínio.
1. Histórico inteiro deve virar memória durável?
2. TTL apaga automaticamente cópias em todos os sistemas?
3. Memória vetorial substitui autorização?
Não.
Filtros e permissões controlam acesso independentemente da proximidade.
Seu progresso fica salvo neste navegador. Concluir a leitura não substitui demonstrar o domínio nos exercícios.
Referências e aprofundamento
Documentação oficial e trabalhos originais. As referências registram o escopo e as limitações para você conferir o que sustentam.
- LangChain Memory overview
LangChain • consulta: 2026-10-06
FundamentosMemória curta e longa, semântica e episódica, perfis/coleções, atualizações e recuperação.
Limites: Taxonomia funcional, não equivalência com cognição humana; consolidar apenas fatos com proveniência e política definida.
- LangGraph Persistence
LangChain • consulta: 2026-10-06
LangGraphAgentesEstado, checkpoints, retomada e execução durável.
Limites: Reexecutar nós pode repetir efeitos; combinar persistência com idempotência e versões do estado.
- LangGraph Memory
LangChain • consulta: 2026-10-06
LangGraphAgentesPersistência de memória por thread/namespace, trimming, delete e summarization.
Limites: Memória deve ter identidade/tenant; apagamento efetivo precisa incluir índices, backups e traces conforme política da aplicação.
- pgvector README
pgvector contributors • consulta: 2026-10-06
PostgreSQLPostgreSQL com vetores, distâncias cosine/L2, índices HNSW/IVFFlat, filtragem e busca híbrida.
Limites: README atual mostra versão 0.8.7; performance, recall e ordem de filtros exigem benchmark no dataset.
- PostgreSQL Row Security Policies
PostgreSQL Global Development Group • consulta: 2026-10-06
PostgreSQLSegurançaProgramaçãoPolíticas por linha para isolamento de acesso à memória relacional.
Limites: Superusers e donos da tabela podem contornar RLS; testar roles, FORCE ROW LEVEL SECURITY e acesso real da aplicação.
- Effective context engineering for AI agents
Anthropic • consulta: 2026-10-06
AgentesIA & MLSeleção de contexto, orçamento de attention, context rot, recuperação just-in-time, compactação, notas e handoffs.
Limites: Context rot descreve efeito empírico, sem limite universal; compactação pode perder detalhes necessários.
- OpenAI Retrieval
OpenAI • consulta: 2026-10-06
OpenAIIngestion, chunking/overlap, metadata/filtros, ranking e reescrita de consultas em vector stores.
Limites: Implementação gerenciada OpenAI; defaults não são melhores escolhas universais para pgvector.
- Redis EXPIRE
Redis • consulta: 2026-10-06
RedisTTL, expiração de chaves e renovação de prazo.
Limites: TTL em Redis não apaga automaticamente cópias em vector store, banco, backups ou logs.
- Safety in building agents
OpenAI • consulta: 2026-10-06
OpenAIAgentesPrompt injection, vazamento de dados, entradas não confiáveis e aprovação de tools.
Limites: Prompt de segurança não substitui permissões da aplicação; guia pode mostrar ferramentas legadas, aplicar os princípios em stack atual.