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:
| Pergunta | Esta biblioteca | Letta | Mem0 | Zep |
|---|---|---|---|---|
| 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ória | Campo de metadados | Episó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órico | Histórico git de arquivos, não um modelo de fato | Histórico de alterações por memória (history(): valor antigo, valor novo, evento, timestamps) — histórico de transações, não tempo válido | Grafo 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 nuvem | Somente nuvem desde 2025 |
| Funciona sem fornecedor, sem chave, sem servidor? | SQLite em disco, embeddings no dispositivo | Auto-hospedagem possível; a nuvem é o produto | Auto-hospedagem possível (Apache-2.0, armazenamentos vetoriais locais); precisa de um LLM para extração; a nuvem é o produto | Zep é 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_byexistem 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
deleteObservationsfiltra 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_facttem 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_updatedsubstitui. "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;SqliteMemoryStoreeInMemoryStoresã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 chamada | 5.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 10 | 0,5–1 ms |
| Obter por id | 0,1 ms |
| Invalidar um fato | 0,5 ms |
Reconstruir snapshotAsOf | 2,19 s com 100.001 versões |
| Tamanho do arquivo | 69 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 cresce | um 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 conectado | 20.000 fatos | 100.000 fatos |
|---|---|---|
| Tamanho do arquivo (fatos + vetores) | 172 MB | 864 MB |
| Dos quais vetores | 160 MB | 800 MB |
| Por vetor, no disco | ~8,0 KB | ~8,0 KB |
| Recuperação semântica, top 10 — primeira de uma sessão | 765 ms | 3.200 ms |
| Recuperação semântica, top 10 — depois | 36 ms mediana | 187 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 fatos | Medido |
|---|---|
| Leitura SQL da tabela de vetores | 978–1.182 ms |
JSON.parse dessas linhas | 1.418–1.744 ms |
| Varredura de cosseno de todos os 100.000 vetores | 85–127 ms |
| A chamada inteira, fria, de ponta a ponta | 3.650–4.170 ms |
| Por vetor, armazenado como texto JSON | 8.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-vecnão é — neste tamanho. Armazenar o vetor como um BLOBFloat32Arrayremove a análise inteiramente e reduz a leitura em aproximadamente 5×, que é onde 95% do custo está. Entregar a busca aosqlite-vecatacaria 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-walebrain.db-shmantes de copiar o backup no lugar. Um-waldeixado 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"eVACUUM 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 .dumptambém funciona, mas não carregauser_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 porcp-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 falharPRAGMA 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ção | Proveniência | Desde quando | Aposentar sem apagar | Confiança | Relações | Portabilidade | Nota |
|---|---|---|---|---|---|---|---|
| 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.1enquanto 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.