al-buddy-memory

Memória governada e portátil para agentes de IA: cada recuperação retorna quem afirmou um fato, desde quando ele é verdadeiro e o que o substituiu. Fatos nunca são sobrescritos. SQLite local, sem chave de API.

Documentação

al-buddy-memory

Al Buddy — isso é Al, um nome, pronunciado como "pal". Não é I.A.

Uma memória que é sua.

Memória portátil, governada e agnóstica de modelo para agentes de IA.

Um fato é invalidado, nunca sobrescrito; em um identificador governado, a eliminação passa por política e é auditada. O texto bruto é a fonte da verdade e não pode ser editado. Embeddings são um cache descartável, etiquetado por modelo. Cada fato e cada link exportam para um formato documentado. A memória sobrevive a qualquer modelo, runtime ou empresa que a produziu.


Por que isso existe

Todo produto de memória para agentes no mercado responde bem a uma pergunta: o que o agente recorda? Nenhum deles responde às quatro perguntas que decidem se você pode confiar e manter essa memória:

PerguntaEsta bibliotecaLettaMem0Zep
De onde veio este fato e quem o afirmou?Proveniência em cada nó e aresta (UserInput / AIInferred / GuardianAdded / SystemGenerated)Histórico git do arquivo de memóriaCampo de metadadosEpisódios de grafo
Quando era verdadeiro e o que o substituiu?Dois eixos consultáveis: validAt responde o que era verdadeiro em Y; getNodeAsOf / snapshotAsOf respondem o que o armazenamento acreditava em X a partir de versões completas antes/depois. Eles se combinam em uma única chamada (snapshotAsOf(X, { validAt: Y })), uma leitura que o histórico não pode garantir é marcada, e a eliminação também remove o históricoHistórico git de arquivos, não um modelo de fatoHistórico de alterações por memória (history(): valor antigo, valor novo, evento, timestamps) — histórico de transações, não tempo válidoGrafo temporal (sua verdadeira força): as arestas do Graphiti carregam valid_at / invalid_at junto com created_at / expired_at
Posso levá-la comigo, sem perdas, para outro runtime?Uma exportação JSON versionada com esquema publicado e testes de conformidade.af (estado do agente, moldado pelo framework, memória arquivada ainda não incluída)Exportação em nuvemSomente nuvem desde 2025
Funciona sem fornecedor, sem chave, sem servidor?SQLite em disco, embeddings no dispositivoAuto-hospedagem possível; a nuvem é o produtoAuto-hospedagem possível (Apache-2.0, armazenamentos vetoriais locais); precisa de um LLM para extração; a nuvem é o produtoZep é nuvem; Graphiti auto-hospeda (banco de grafo + chave de LLM necessária)

Fontes para as células acima, cada uma verificada contra o código ou anúncio do próprio projeto em 2026-09-18: o histórico por memória do Mem0 é uma tabela SQLite com old_memory, new_memory, event, created_at, updated_at (mem0/memory/storage.py); o Mem0 é Apache-2.0 e roda contra armazenamentos vetoriais locais, incluindo Qdrant, Chroma, pgvector e FAISS (documentação de armazenamento vetorial), com um LLM chamado para extrair fatos no caminho padrão add(). A Zep parou de manter a Community Edition em 2025-04-02, em suas próprias palavras: "decidimos parar de manter e lançar a Zep Community Edition." Graphiti, o motor por trás da Zep, é Apache-2.0 e auto-hospeda, e seus requisitos declarados são um banco de grafo (Neo4j, FalkorDB, Amazon Neptune ou o obsoleto Kuzu) mais uma chave de LLM — ele "padroniza para OpenAI para inferência e embedding de LLM."

Os três locais, sem chave, que as pessoas citarão, lidos contra seu próprio código em 2026-09-21. Eles respondem à quarta pergunta da mesma forma que esta biblioteca — sua máquina, seus arquivos, sem conta — e são a comparação honesta, não os produtos em nuvem.

  • basic-memory (AGPL-3.0; notas Markdown em disco são a verdade, SQLite é um índice reconstruível). Ele tem tempo válido, e isso vale ser dito claramente: uma observação pode carregar @effective[2026-06-10,2026-07-27) na própria nota, analisada em um índice de tempo (models/knowledge.py) e reconstruída a partir do arquivo, então o arquivo permanece a fonte da verdade. O que ele não carrega em uma instalação local é quem afirmou um fato: created_by / last_updated_by existem e contêm um id de usuário em nuvem, nulos para uso local e CLI. A portabilidade é do tipo mais forte e menos especificado: as notas são o formato, então nada fica preso, e não há esquema versionado para importá-las em outra coisa. Captura e busca de texto completo não precisam de chave; busca semântica é opcional e precisa.
  • O servidor de memória MCP (a implementação de referência que a maioria dos agentes encontra primeiro; um arquivo JSONL). Entidades, relações e observações, e nada mais: sem campo para quem disse, sem timestamps, e deleteObservations filtra o valor antigo para fora do array, então o que era verdadeiro antes se foi em vez de fechado. O arquivo é sua própria exportação. Nada chama um modelo; nada sai da máquina.
  • Memori (Apache-2.0; seu próprio banco SQL — SQLite, Postgres, MySQL, Oracle). Um fato em memori_entity_fact tem conteúdo, um embedding, uma contagem e uma data de última visualização; quem o afirmou só é recuperável percorrendo as chaves estrangeiras de volta a uma mensagem de conversa, não é uma propriedade do fato, e não há tempo válido — date_updated substitui. "Seus dados permanecem em seu banco de dados" é a história de portabilidade, sem formato de intercâmbio publicado. É local no sentido que importa para seus dados, mas a captura não é sem chave: extração e embeddings chamam um LLM, e o caminho padrão do SDK espera uma conta Memori também.

Também vale citar: OpenMemory MCP (Mem0, lançado em maio de 2025) enviou a mesma ideia de distribuição — um armazenamento de memória local compartilhado entre clientes MCP — e o Mem0 o arquivou; seu README agora abre com "Este projeto foi arquivado." Seu esquema é o contraste desta tabela: models.py dá a uma memória content, created_at, updated_at, archived_at, deleted_at e um estado, sem campo para quem afirmou o fato e sem tempo válido, e content é reescrito no lugar na atualização. Ele mantém um histórico de transições de estado e um log de acesso, e tem uma exportação ZIP — então não é a ausência de portabilidade que separa os dois, é proveniência, tempo válido e um bruto imutável.

Benchmarks de recordação (LOCOMO, LongMemEval, DMR) medem o que um agente lembra. Nenhum deles pontua um sistema de memória em proveniência, invalidação ou portabilidade. Esta biblioteca é construída para esse eixo, e o avaliador de conformidade abaixo é uma tentativa de medi-lo. A tabela é nossa leitura do código e da documentação pública de cada projeto, datada acima; se tivermos uma célula errada, um PR com um link a corrige.

Comece aqui

Uma memória vazia não dá nada em que um assistente se apoiar. docs/STARTER.md semeia a sua em dez minutos: fixe quem a pessoa é e como ela quer ser tratada (incluindo "nunca um puxa-saco"), escolha as regras que o armazenamento impõe e deixe-o derivar o resto à noite.

Atualizando de uma versão anterior: CHANGELOG.md marca qualquer coisa que mude o que um chamador existente recebe de volta. 0.4.0 tem mudanças de quebra (conteúdo imutável, eliminação governada, um novo listNodes na interface do armazenamento, as exportações MCP movidas para al-buddy-memory/mcp), então leia essa entrada antes de atualizar.

O que está na caixa

  • MemoryStore: uma interface agnóstica de armazenamento; SqliteMemoryStore e InMemoryStore são enviados, com uma suíte de conformidade contra a qual qualquer backend pode rodar.
  • ProjectMemory: um cérebro com escopo por projeto ou pessoa, cada um em seu próprio arquivo SQLite.
  • HybridRetriever: recordação lexical + semântica com confiança ciente de decaimento; embeddings no dispositivo via transformers.js (sem chave de API). A recordação pode ser escopada (tipo de memória, tags, confiança mínima, níveis de privacidade e retenção), e o escopo se aplica igualmente ao lado de palavras-chave e ao lado vetorial.
  • exportPortable / importPortable: o formato de intercâmbio sem perdas, versionado, com um JSON Schema.
  • PinnedBlocks: um nível de fatos com limite de tamanho que pertence a todo prompt, editável pelo próprio agente, sobre o armazenamento governado.
  • consolidate: uma passada em horário de sono que lê memória bruta recente e escreve novos fatos derivados com arestas de proveniência de volta às suas fontes; o bruto nunca é reescrito e nada é resumido.
  • verifyDerived: cada conclusão armazena as palavras exatas em que se apoia (evidence), verificadas quando é escrita; isso as re-verifica a qualquer momento e retrata, nunca exclui, uma cuja evidência não se sustenta mais.
  • Modelos mentais: perguntas permanentes com respostas mantidas atualizadas em segundo plano, então ler uma não custa chamada de modelo (defineMentalModel, refreshMentalModels, getMentalModel). Cada resposta cita os fatos em que se apoia, mantém seu histórico ("o que pensávamos em junho") e fica obsoleta no momento em que um desses fatos deixa de ser verdadeiro ou é eliminado.
  • explainFact: por que um fato é acreditado, em uma chamada — proveniência e origem, validade e o que o substituiu, a evidência de uma conclusão com cada citação verificada e seu histórico. Através de um identificador governado, uma fonte que o leitor não pode ver é nomeada como retida, nunca mostrada.
  • Conclusões vão com seus fatos: um fato que deixa de ser verdadeiro retrata o que foi concluído dele (mantido, marcado); um fato que é eliminado leva tudo construído a partir dele (SPEC §8a).
  • listConsolidations / undoConsolidation: revise o que cada passada concluiu, com a evidência para cada fato, e desfaça as conclusões de uma passada. Desfazer retrata (validTo) e registra quem retirou cada fato e por quê; nunca exclui, então o histórico ainda mostra o que era acreditado, quando foi retirado e o motivo.
  • buildSourceProvenance / readSourceProvenance, renderMemoryBlock, exportMemoryMarkdown, auxiliares de decaimento.
  • Integrações: al-buddy-memory/ai-sdk (ferramentas + middleware do Vercel AI SDK), al-buddy-memory/langchain (um armazenamento de memória de longo prazo LangGraph + ferramentas LangChain), al-buddy-memory/mastra (um processador de entrada + ferramentas). Os frameworks são dependências opcionais de pares; cada escrita é governada e registra qual agente a fez. Guias em docs/integrations.

Node ≥ 20. Uma dependência de runtime (better-sqlite3); transformers.js é opcional.

Trabalho anterior: o projeto Letta publicou a ideia de um nível de memória fixado e uma passada em segundo plano sobre a memória (blocos de memória; agentes em horário de sono). O que é diferente aqui: cada fato derivado deve citar o bruto em que se apoia ou é recusado, o bruto nunca é reescrito, e uma passada inteira pode ser revisada e desfeita, com o desfazer e seu motivo mantidos em registro.

import { SqliteMemoryStore, ProjectMemory, PinnedBlocks, exportPortable } from "al-buddy-memory";

const store = new SqliteMemoryStore("./brain.db");
await store.addNode({ provenance: "UserInput", memoryType: "Lesson", content: { text: "Chris prefers decisions over options." }, /* …governance fields… */ });
const pins = new PinnedBlocks(store);
await pins.pin({ text: "Never present options without a recommendation.", label: "rule" });
const snapshot = await exportPortable(/* … */);   // → docs/portable-format.schema.json

Contrato completo: docs/SPEC.md. Registro de design: docs/DECISION-2026-07-07.md.


Python e outras linguagens

O servidor MCP e o formato portátil são a superfície neutra de linguagem: um agente Python pode usar o servidor de governança hoje, e qualquer linguagem pode ler a exportação (é JSON simples com um esquema). Um pacote Python nativo está planejado; abra uma issue se precisar dele antes e diga o que usaria primeiro.

Licença

Apache-2.0. Veja LICENSE.

Governança é imposta, não implícita

O vocabulário — Público / Privado / Sensível / Selado, níveis de retenção, proveniência em cada fato e relação — vem com o armazenamento. govern() é o que o impõe: políticas na frente de cada escrita, atualização, leitura e exportação, e uma trilha de auditoria somente anexação de quem leu o quê e por quê.

import { SqliteMemoryStore, govern, personalDefaults, storeAudit } from "al-buddy-memory";

const inner = new SqliteMemoryStore("brain.db");
const store = govern(inner, {
  policies: [personalDefaults({ owner: "chris" })],
  context: () => ({ actor: currentActor() }),
  // The trail goes in the database, hash-chained, in the same transaction as
  // the fact it describes. `new ChainedAudit("audit.jsonl")` puts it in a file
  // instead — for a store that is not SQLite, or when you want it outside the
  // file it describes. See docs/GOVERNANCE.md for what each one proves.
  audit: storeAudit(inner),
});

Para garantir que nada seja eliminado por acidente, adicione memoryLock() às políticas: enquanto estiver lá, a eliminação é recusada para todos, incluindo o proprietário, até que você a remova (ou alterne o interruptor que ela lê). Invalidar um fato ainda funciona; isso não é eliminação. O armazenamento bruto e o arquivo de banco de dados estão fora de qualquer política, então mantenha backups.

E para tornar uma exclusão algo que você pode desfazer, passe recentlyDeleted: { days: 14 } para govern(): uma exclusão então move o fato para fora da recordação por 14 dias, restoreDeleted o traz de volta, e purgeDeleted o elimina de vez quando os dias terminam (perguntando às políticas novamente, então o bloqueio ainda vence). Nada roda em um temporizador, e até ser purgado, o fato ainda está em exportações e backups. Verifique a trilha com al-buddy-memory verify-audit brain.db. Ela nomeia o primeiro evento que foi editado, removido, inserido ou reordenado. O que ela estabelece, e as duas coisas que ela não estabelece, estão escritos em docs/GOVERNANCE.md — versão curta: a cadeia é à prova de adulteração evidente; uma cauda cortada de um arquivo de log é invisível para ela, uma cauda cortada da tabela é detectada desde que ninguém redefina o contador da própria tabela, e apenas uma cabeça que você ancora em outro lugar detecta uma reescrita ou um backup restaurado.

Três políticas acompanham para copiar: padrões pessoais (segredos automaticamente classificados como Sensíveis; fatos Sensíveis nunca chegam, saem ou são apagados por ninguém além do proprietário em pessoa; apenas o proprietário altera um fato), modo guardião (apenas um guardião pode escrever ou alterar um fato de um guardião), auditoria empresarial (inferências de baixa confiança ocultas de não revisores; exportações restritas a exportadores). Uma política é um objeto simples com cinco ganchos opcionais; veja docs/GOVERNANCE.md.

Sem qualquer política, o armazenamento ainda garante: fatos selados nunca aparecem em uma busca, a menos que solicitados por classificação; provenance, nodeId, encryptionKeyRef, content bruto e a trilha de âncora são imutáveis após a escrita, por todos os caminhos, incluindo importação; cada instante é armazenado em uma única grafia UTC canônica. Duas coisas que ele não faz, ditas claramente: proveniência é o que o escritor afirma (imutável uma vez escrita, não verificada — vincule atores à proveniência em uma política); e encryptionKeyRef nomeia uma chave que você gerencia, não criptografa o arquivo.

O identificador governado é o limite. govern(store, …) coloca políticas na frente de cada operação que pode alterar um fato ou revelar um — incluindo apagamento, que é recusado a menos que uma política o permita explicitamente. Quem quer que detenha o armazenamento interno não é governado por nada, então entregue o governado.

As regras às quais um assistente nesta memória está sujeito são publicadas em docs/policies: comportamento ético, soberania e privacidade do usuário, ciclo de vida e guardiões, administração de dados — e um registro honesto do que o código impõe, o que um prompt carrega, e o que ainda é uma decisão de uma pessoa. Elas mudam abertamente.

Limites, medidos

Um arquivo SQLite, um processo, um escritor. Medido em um laptop M1 Pro com 100.000 fatos (bench/bench.mjs, better-sqlite3, WAL):

Operação (100.000 fatos)Medido
Inserir, um fato por chamada5.400–5.900 fatos/s (17–18 s para todos os 100k)
Recuperação por palavra-chave, top 10 (FTS5 + reclassificação por decaimento)30–50 ms mediana, ~150 ms pior de cinco termos; primeira consulta após abrir ~320–380 ms (cache frio)
Recuperação por palavra-chave através de um identificador governado, top 10~75 ms para uma palavra em 10% dos fatos, ~135 ms para duas dessas palavras, ~850 ms para uma palavra em todos os fatos — veja abaixo
Recuperação apenas por filtros, top 100,5–1 ms
Obter por id0,1 ms
Invalidar um fato0,5 ms
Reconstruir snapshotAsOf2,19 s com 100.001 versões
Tamanho do arquivo69 MB para os 100.000 fatos; cada alteração registrada adiciona ~738 bytes (140 MB após uma atualização em cada fato)
Evento de auditoria em audit_events, na própria transação do fato+0,04 ms por escrita governada, +0,5 ms por leitura governada (uma leitura também é auditada, então ela assume brevemente o bloqueio de escrita)
Verificando a cadeia — verify-audit <db>linear, ~3 µs/evento: 83 ms em 20.000 eventos, 325 ms em 100.000, 1,5 s em 500.000. Cada processo paga uma vez, antes de sua primeira escrita governada e fora da transação de escrita, então atrasa esse processo e não bloqueia nenhum outro. Memória constante (a varredura flui)
Quão rápido a trilha cresceum evento por escrita governada, um ou dois por leitura governada — um remember é +2, um recall +1. Nada a poda. Em 500.000 eventos, a trilha tem ~128 MB, o que pode exceder os fatos que descreve; se você dirigir um armazenamento tão intensamente, fique de olho nele

As faixas são três execuções do mesmo script em 0.4.0. A paginação é exata: uma página de dez é a primeira dez da leitura ordenada completa. Quando os fatos realmente decaíram, o armazenamento pode ter que ler além de seu pool de candidatos de 200 linhas para manter essa promessa — o pior caso é uma leitura completa dos fatos correspondentes (~300 ms em 100k), e isso só acontece quando um fato decaído e um mais recente trocariam de lugar.

A medição de tempo de transação é uma execução no mesmo M1 Pro: 100.000 fatos, um reforço registrado por fato e uma invalidação anterior. Reconstruir todos os 100.000 fatos de 100.001 versões levou 2,19 s. É uma reconstrução linear em memória, não uma consulta de ponto indexada.

Uma busca governada por palavra-chave é mais lenta de propósito. Ela lê cada correspondência, mantém as que o ator pode ver e as classifica com raridade de palavras contada apenas sobre essas correspondências visíveis: o BM25 do armazenamento conta raridade em todos os fatos, incluindo os ocultos, então permitiria que um fato oculto reordenasse resultados visíveis. No tamanho de uma memória pessoal (alguns milhares de fatos), a diferença de velocidade não aparece, e um teste mantém sua qualidade: doze fatos solicitados em perguntas simples ("qual é o login do wifi") entre duzentos distratores todos caem na primeira página.

O que isso significa: um assistente pessoal ou um serviço de locatário único não notará o armazenamento; um SaaS multi-locatário precisa do backend Postgres no roteiro. Node/TypeScript apenas por enquanto; o incorporador opcional no dispositivo é um download de modelo de 25 MB.

O caminho semântico custa mais, e é o ponto fraco honesto

Tudo acima é o caminho de palavra-chave. A recuperação com um incorporador conectado passa por uma varredura linear de força bruta: cada vetor armazenado é lido, analisado e pontuado contra a consulta. Medido da mesma forma (bench/bench-vectors.mjs, vetores de 384 dimensões, a largura do modelo padrão no dispositivo), no mesmo laptop:

Com um incorporador conectado20.000 fatos100.000 fatos
Tamanho do arquivo (fatos + vetores)172 MB864 MB
Dos quais vetores160 MB800 MB
Por vetor, no disco~8,0 KB~8,0 KB
Recuperação semântica, top 10 — primeira de uma sessão765 ms3.200 ms
Recuperação semântica, top 10 — depois36 ms mediana187 ms mediana

Leia isso como um teto, não como uma vitória de benchmark.

E a varredura de força bruta não é o que custa. Isso vale a pena afirmar claramente, porque é o suspeito óbvio e está errado. Cronometrando uma chamada fria estágio por estágio em 100.000 fatos, duas execuções no mesmo laptop (a segunda 2026-09-19):

Estágio de uma recuperação semântica fria, 100.000 fatosMedido
Leitura SQL da tabela de vetores978–1.182 ms
JSON.parse dessas linhas1.418–1.744 ms
Varredura de cosseno de todos os 100.000 vetores85–127 ms
A chamada inteira, fria, de ponta a ponta3.650–4.170 ms
Por vetor, armazenado como texto JSON8.003 bytes

Ler as linhas e analisá-las é 95–96% desses três estágios; a varredura que todos assumem ser o gargalo é cerca de 4%. Três coisas se seguem:

  • Vetores são armazenados como texto JSON, então um vetor de 384 floats custa 8.003 bytes em vez dos ~1,5 KB que os mesmos floats ocupam como binário. É aí que o tamanho do arquivo vai — os mesmos 100.000 fatos são 69 MB sem vetores e 864 MB com eles — e, conforme a tabela, é também aí que o tempo vai, porque essas linhas de 8 KB têm que ser lidas e analisadas.
  • Então o armazenamento BLOB é o movimento, e sqlite-vec não é — neste tamanho. Armazenar o vetor como um BLOB Float32Array remove a análise inteiramente e reduz a leitura em aproximadamente 5×, que é onde 95% do custo está. Entregar a busca ao sqlite-vec atacaria a varredura de 85–127 ms, que não é o problema ainda. Ambos os números são uma projeção da tabela acima, não um resultado alcançado: nenhum está construído, e nenhum número neste README vem de uma implementação BLOB.
  • O cache é descartável e marcado por modelo. Vetores vivem em sua própria tabela chaveada por (nodeId, model); excluí-los não perde nada além de tempo, e um vetor de um modelo diferente é pulado em vez de comparado. Atualizar o incorporador é uma reindexação, nunca uma migração. Os tamanhos apenas de fatos acima são o que a memória realmente pesa.

A varredura ainda é linear no número de fatos, e a primeira chamada de uma sessão paga pela tabela de vetores inteira; chamadas posteriores reutilizam um cache em processo de 60 segundos e ainda pontuam cada vetor. Se você está conectando um incorporador sobre dezenas de milhares de fatos, dimensione a máquina para a tabela acima, ou mantenha o caminho de palavra-chave até o trabalho BLOB chegar.

Backups, restaurações e pastas sincronizadas

Um banco de dados SQLite em modo WAL é três arquivos — brain.db, brain.db-wal, brain.db-shm — e dois hábitos comuns silenciosamente perderão a memória de uma pessoa:

  • Restaurando um backup: pare o servidor primeiro, então exclua brain.db-wal e brain.db-shm antes de copiar o backup no lugar. Um -wal deixado ao lado de um arquivo restaurado é reproduzido sobre ele na próxima abertura, então a restauração parece ter sucesso, não relata erro e deixa você com os dados que estava tentando substituir. Verificado, 2026-09-19.
  • Qual comando de backup: sqlite3 brain.db ".backup out.db" e VACUUM INTO 'out.db' são os seguros — ambos tiram um instantâneo consistente de um banco de dados ao vivo, verificado enquanto outro processo estava escrevendo. sqlite3 .dump também funciona, mas não carrega user_version; antes de 0.4.2 um despejo restaurado não podia ser aberto de forma alguma (duplicate column name: valid_from), e agora migra limpo. Nunca faça backup por cp-ando um banco de dados ao vivo: uma cópia tirada no meio de uma escrita pode ser ilegível, e uma legível ainda pode falhar PRAGMA integrity_check.
  • Pastas sincronizadas: nunca coloque o banco de dados no iCloud, Dropbox, OneDrive ou Google Drive. O modo WAL assume uma máquina coordenando seus próprios bloqueios; um cliente de sincronização copiando os três arquivos independentemente, ou duas máquinas escrevendo através de uma pasta, corrompe o arquivo em vez de conflitar visivelmente. Faça backup da pasta por todos os meios — copie-a em um cronograma, ou use .backup/VACUUM INTO — mas não deixe um cliente de sincronização possuir o arquivo ao vivo.

A pontuação de conformidade

Experimente: albuddy.com — cole qualquer exportação de memória, nada sai do seu navegador.

Os benchmarks de recuperação estão saturados. Ninguém pontua se um sistema de memória pode dizer quem afirmou um fato, desde quando, se ainda é verdadeiro, e se o fato sobrevive à saída do fornecedor. Este faz, em qualquer exportação que você colar:

npx al-buddy-memory conformance my-export.json          # format auto-detected
npx al-buddy-memory conformance agent.af --format blocks     # block-style agent files
npx al-buddy-memory conformance memories.json --format records  # flat memory records
npx al-buddy-memory conformance --demo                  # a small governed store, for comparison

Sete dimensões, cada uma 0–100% com o motivo explicado; uma dimensão que a amostra não pode provar (sem fatos aposentados presentes, por exemplo) é relatada como não comprovada e deixada de fora do total em vez de contada como uma falha. A pontuação de referência:

ExportaçãoProveniênciaDesde quandoAposentar sem apagarConfiançaRelaçõesPortabilidadeNota
al-buddy-memory (armazenamento de demonstração)100%100%100%100%100%100%A

Pontue sua própria exportação da mesma forma: --format blocks para arquivos de agente em bloco, --format records para registros de memória planos, ou cole-a na demonstração em albuddy.com.

De onde os números vêm, dito claramente, porque este é um pontuador com o qual também nos pontuamos. Cinco dos sete — proveniência, desde quando, aposentar sem apagar, confiança, relações — são contados dos registros no arquivo que você cola; mude a amostra e eles se movem. Dois não são. Se um sistema mantém um fato aposentado, se seu esquema é publicado e se ele itemiza fatos são propriedades do sistema, que nenhuma exportação única pode provar, então o autor do adaptador as declara em src/conformance/adapters.ts e elas aparecem textualmente na linha de motivo do relatório. A única parte que é executada em vez de afirmada é o round-trip: para nosso próprio formato, o pontuador importa seu artefato em um armazenamento novo, exporta-o novamente e compara — em seu arquivo, e diz lossy se isso falhar. O livro de regras, incluindo qual dimensão é qual e o que a pontuação não mede (qualidade de recall, veracidade, latência), está em docs/SCORING.md. Os adaptadores são escritos contra formatos de exportação, não fornecedores. Se um sistema começar a registrar proveniência, sua pontuação aumenta — esse é o objetivo. Adicione um adaptador para o seu formato e abra um PR; se você achar que declaramos uma característica errada para o seu, isso também é um PR de uma linha.

O servidor MCP de governança

A maioria dos servidores MCP de memória entrega um fato ao agente. Este entrega um fato que ele pode avaliar: todo resultado de recall carrega provenance, validFrom, validTo, current, confidence, qual assistente o escreveu e qual o aposentou, e — para um fato substituído — o id do que o substituiu. invalidate encerra a validade de um fato e mantém o registro; o servidor não tem ferramenta de apagar. Ele serve um armazenamento governado: o personalDefaults do proprietário com o cliente de IA como público, então um segredo que um agente escreve é classificado como Sensível e mantido fora do recall de qualquer IA, e toda chamada é auditada — dentro do próprio banco de dados, na mesma transação que o fato.

{ "mcpServers": { "memory": { "command": "npx",
    "args": ["-y", "--package=al-buddy-memory@0.6.0", "al-buddy-memory-mcp"] } } }

al-buddy-memory-mcp é um executável dentro do pacote al-buddy-memory, não um pacote próprio, então --package= é o que diz ao npx onde encontrá-lo — npx al-buddy-memory-mcp looks for a package by that name and gets a 404. Drop the @0.6.0 para acompanhar o lançamento mais recente em vez daquele que você testou.

Lançando? Este pin é uma versão documentada e fica desatualizado no momento em que uma nova é publicada — o exemplo então instalaria um servidor mais antigo do que a página descreve. Avance-o no mesmo commit do aumento de versão — foi feito, em 0.4.2 e 0.5.0, e estava @0.4.1 enquanto 0.4.1 era atual. Executar o comando fixado contra um lançamento mais novo retorna o handshake do servidor mais antigo, que é como um leitor acaba lendo documentação que não corresponde ao que acabou de instalar (revisão de lançamento Astra, 2026-09-19).

A memória é armazenada em ~/.al-buddy-memory/brain.db; defina AL_BUDDY_MEMORY_DB para colocá-la em outro lugar. Dê a ela um caminho absoluto — uma configuração JSON não é um shell, e um ~ nela é expandido por este servidor, mas não por tudo o mais que possa ler o valor. A trilha de auditoria fica dentro desse banco de dados, em audit_events, anexada na mesma transação que o fato que descreve — uma cadeia de hash, não importa quantos assistentes estejam rodando. Verifique-a com al-buddy-memory verify-audit ~/.al-buddy-memory/brain.db. Um servidor de antes da tabela deixava um log por processo em brain.db.audit/, e um antes disso um único brain.db.audit.jsonl; esses não são adotados nem estendidos (eles atestam um período que a tabela não pode, e vice-versa) e o mesmo comando os relata junto com a tabela. AL_BUDDY_MEMORY_AUDIT ainda nomeia um arquivo JSONL para usar em vez disso — um processo por arquivo se você fizer isso.

Um resultado de recall se parece com isto — cada campo que um agente precisa para decidir o quanto confiar no fato:

{ "id": "…", "text": "Lives in Tokyo", "provenance": "UserInput", "validFrom": "2026-06-01T00:00:00Z",
  "validTo": null, "current": true, "confidence": 1, "supersededBy": null, "derivedFrom": [],
  "recordedAt": "2026-06-01T00:00:00Z",
  "origin": { "app": "claude-desktop", "appVersion": "1.2.3", "via": "mcp" }, "retiredBy": null }

origin é qual assistente escreveu o fato, tirado do handshake MCP em vez de do modelo, e retiredBy é qual o encerrou — então, em memória genuinamente compartilhada entre assistentes, um fato aposentado é um evento com um ator. Ambos são null quando o host não sabia nada.

remember responde "o que isso pode substituir?" Ele retorna o fato que armazenou mais mayConflictWith: até três fatos atuais que se parecem com o novo, cada um com seu id, texto e validFrom.

{ "id": "…", "text": "Lives in Berlin", "…": "…",
  "mayConflictWith": [ { "id": "…", "text": "Lives in Tokyo", "validFrom": "2026-06-01T00:00:00Z" } ] }

Nada é aposentado automaticamente — o cliente os lê e chama invalidate naqueles que deixaram de ser verdadeiros. Esse é todo o loop de invalidar-nunca-sobrescrever, e até 0.4.2 nada na superfície jamais o solicitou. Trate a lista como fatos a ler: eles são as melhores correspondências lexicais, não conflitos que foram comprovados.

O primeiro recall de uma conexão também retorna o nível fixado — as regras permanentes da pessoa — como um segundo bloco de conteúdo, então o nível que afirma estar em todo prompt chega lá sem gastar nenhum dos 512 caracteres do handshake.

Ferramentas: remember, recall, history, explain, invalidate, pin, unpin, pinned, mental_model, define_mental_model. mental_model lê a resposta pré-escrita de uma pergunta permanente com sua atualidade e evidência (o host atualiza as respostas em sua própria programação). explain responde por que um fato é acreditado: quem o afirmou, quando era verdadeiro, o que o encerrou e, para uma conclusão, as palavras exatas em que se apoia, cada uma verificada agora. remember aceita no máximo 4.000 caracteres e pin 500; uma consulta recall 1.000, um motivo invalidate 500, e um id 128. pin recusa texto que pareça um segredo, porque o servidor o armazenaria como Sensível e nenhum assistente poderia vê-lo. O replacedBy de invalidate deve nomear um fato que o chamador possa ver. SQLite em disco, sem serviço, sem chave. Os corpos das ferramentas são uma função simples sobre um MemoryStore (governanceTools(...), exportado de al-buddy-memory/mcp), então eles rodam contra qualquer backend e testam sem transporte.

Roadmap

  • A especificação e o formato portátil, publicados e versionados (este repositório)
  • al-buddy-memory conformance <export>: pontuar qualquer exportação de memória em proveniência, invalidação e portabilidade, com adaptadores para arquivos de agente em estilo de bloco e registros de memória planos (v0.2.0)
  • O servidor MCP de governança: um servidor de memória que retorna proveniência e validade com cada fato (v0.2.0)
  • Ganchos de governança com trilha de auditoria e três políticas de exemplo; proveniência imutável em tempo de execução; limites medidos em 100 mil fatos (v0.3.0)
  • O evento de auditoria confirmado na mesma transação que o fato que descreve, como uma cadeia que muitos processos compartilham (v0.4.2)
  • Uma tabela de comparação e uma demonstração ao vivo de cole-e-cole sua exportação (albuddy.com)
  • Tempo de transação, a segunda metade do bi-temporal: "o que acreditávamos em X", incluindo um fato mantido incorretamente e depois corrigido (v0.5.0)
  • Um backend Postgres por trás da mesma interface MemoryStore, para implantações multi-tenant e hospedadas (SQLite continua sendo o padrão local-first; a interface é pequena e a suíte de conformidade é o que um backend deve passar)
  • Integrações de frameworks (LangChain, CrewAI, Vercel AI SDK)

Desenvolvimento

npm ci
npm run check   # typecheck, tests, and the browser bundle — exactly what CI runs

Os testes incluem uma suíte de conformidade comportamental que todo backend executa contra si mesmo.