Pular para o conteúdo
Menu

Produto

Soluções

Integrações

Desenvolvedores

Idioma

Produto

Um binário só, arquivos markdown, um índice derivado

Hooks capturam a atividade do agente. Markdown no git guarda as páginas; SQLite guarda o índice de busca e registros exclusivos do banco, como sessões, handoffs e contas. Faça backup dos dois. Veja abaixo o caminho da captura ao próximo briefing.

Um binário só, um diretório de dados.

O SQLite vai embutido, o libgit2 vai vendorizado e o embedder é Rust puro. Não tem sidecar, servidor de banco nem fila para manter rodando. Tudo o que ele sabe está numa pasta só.

<data_dir>/

wiki/Páginas markdown com frontmatter YAML, num repositório git. A fonte da verdade.
db/memory.sqliteÍndice de busca e registros exclusivos do banco: sessões, observações, handoffs, contas e auditoria. Modo WAL.
raw/Segmentos JSONL imutáveis e sanitizados das sessões gerenciadas de ai-memory run.
models/O modelo local de embeddings, all-MiniLM-L6-v2, cerca de 87 MB e com SHA-256 fixado.
logs/Saída de log com rotação diária.
config.tomlLido uma vez na inicialização. Todo valor tem um override AI_MEMORY_*.

Por padrão o servidor faz bind em 127.0.0.1:49374. Fazer backup é rodar ai-memory backup, ou um git push da wiki mais um rsync da pasta.

De um hook até o próximo briefing.

Sete destes oito passos rodam sem modelo e sem chave de API. O passo com LLM fica desligado até você configurar um provedor.

Hook

Sanitizar

Single writer

Observações

Observações

Página da sessão

LLM opcional

Wiki no git

Briefing

Wiki no git

Hook

Só a etapa tracejada precisa de LLM. A seta de retorno é o briefing injetado no próximo SessionStart.
  1. HookZero LLM

    A CLI do agente dispara um hook de ciclo de vida. É fire and forget com um orçamento de 200 ms: os hooks nativos guardam o evento num spool local e um helper destacado faz a entrega. O servidor responde 202, ou 429 quando está saturado.

  2. SanitizaçãoZero LLM

    O router de /hook remove segredos e limita tamanhos. Este é o único caminho de texto não confiável até o store.

  3. Writer únicoZero LLM

    O evento sanitizado entra numa fila só, drenada por uma thread só, que é dona da única conexão de escrita.

  4. ObservaçõesZero LLM

    Os eventos caem no SQLite como uma trilha de auditoria operacional. É uma projeção limitada da sessão, nunca uma transcrição completa.

  5. Fim da sessãoZero LLM

    Regras, sem modelo, transformam as observações em sessions/<id>.md e abrem uma linha de Handoff para o próximo agente, numa transação só.

  6. ConsolidaçãoLLM opcional

    Com um provedor configurado, um LLM reescreve o resumo ou o desdobra em concepts/, decisions/, gotchas/ e procedures/. Roda a partir de uma fila com retry, fora da latência dos hooks.

  7. Commit e indexaçãoZero LLM

    Cada escrita de página é atômica (tmp, rename, fsync), commitada no git e indexada na mesma transação SQLite que a linha dela.

  8. Consulta e briefingZero LLM

    memory_query busca no índice. No próximo SessionStart, o hook pega o handoff aberto e um briefing estruturado para aquele diretório.

Duas camadas, uma fonte da verdade.

Se os arquivos e o índice discordam, os arquivos vencem.

Writer

Markdown no git

Fonte da verdade

Índice SQLite

Derivado

Markdown no git

File watcher

Reindexar

Índice SQLite

  • Os arquivos são a verdade

    As páginas são markdown com frontmatter YAML em wiki///. Abra no Obsidian, passe um grep, faça push para um remote.

  • O índice de páginas é derivado

    Reindex reconstrói páginas, links e busca textual a partir do Markdown. Não recupera sessões, observações, handoffs, usuários, chaves ou auditoria que existem só no banco.

  • As escritas são do servidor

    As escritas normais passam pela camada da wiki, que atualiza junto o arquivo, o histórico git e o índice.

  • Um watcher pega o resto

    Edições feitas no vim ou no Obsidian são detectadas por um file watcher. Um diff completo a cada 30 segundos pega os eventos que ele perdeu.

Recuperação: cinco fluxos, um ranking.

Query

FTS5

Entidades

Grafo

Observações

Vetores

RRF k=60

Autoridade

Resultados

A faixa de vetores é tracejada porque a busca funciona sem ela.
  • Full-text

    SQLite FTS5 sobre títulos e corpos das páginas, com stopwords filtradas nas consultas simples.

  • Match de entidades

    Um índice léxico de nomes vindos das entidades e tags do frontmatter, com peso pela frequência inversa de páginas.

  • Vizinhos no grafo

    Um salto pela tabela de links: wikilinks, links markdown, arestas tipadas e links entre projetos. SQL puro, sem banco de grafos.

  • Observações

    Full-text sobre as observações brutas do projeto. Cada sessão que casa vota uma vez, pela melhor observação, na própria página de sessão, então o detalhe que nunca chegou à página ainda a encontra. Ligado por padrão desde a 3.0.

  • Vetores, opcionais

    Similaridade de cosseno sobre embeddings do modelo local que roda dentro do processo. Ligado por padrão desde a 2.0, nunca obrigatório, e força bruta de propósito.

Depois da fusão

  • Reciprocal Rank Fusion com k=60 junta os fluxos pela posição no ranking, então nenhum fluxo precisa de calibração de score.
  • Um multiplicador de autoridade com limite empurra então as disputas apertadas para o lado de regras, decisões, procedimentos e gotchas mantidos. Páginas episódicas e históricas continuam aparecendo na busca, e nada é excluído de cara.
  • Rerank opcional por LLM: uma chamada por consulta sobre até 30 títulos e trechos. Qualquer falha mantém a ordem local. Ainda não existe um reranker local.
  • Se as páginas compiladas não trouxerem nada, uma busca limitada nas observações brutas devolve raw_hits.
  • Os filtros entram antes do ranking: kind, tag, tier e um prefixo de caminho restringem todas as faixas, então uma página rara não é empurrada para fora por uma comum.
  • Dois sinais opcionais começam em zero: um reduz a autoridade de uma página com feedback de desatualizada ou errada em aberto; o outro favorece uma página que a mesma sessão leu ou marcou como útil para uma consulta parecida. Uma reescrita zera os dois.
  • Passe explain=true para ver, em cada resultado, as posições por fluxo, as contribuições do RRF e o multiplicador.

Japonês, coreano e chinês

  • Sequências de Han, Kana e Hangul sem separadores de palavras precisam de correspondência por caracteres. Um segundo índice de sequências de um e dois caracteres é gravado na mesma transação da página.
  • É um índice do SQLite, não um modelo. Buscar em japonês ou coreano continua sem chamada de API e sem embedding.
  • Texto latino segue pelo caminho de sempre, sem mudança. Um banco vindo de uma versão antiga preenche o novo índice antes de aceitar tráfego.

Tempo: as_of

  • Passe uma data ISO para perguntar o que a wiki dizia sobre um assunto naquela época.
  • Ele registra só o tempo de ingestão: quando o ai-memory aprendeu um fato e quando o substituiu, nunca quando ele era verdade no mundo.
  • Ele não reproduz o ranking que uma busca teria devolvido naquela data.
Validade temporal na documentação

Arestas tipadas

  • O relations: do frontmatter aceita um conjunto fechado: causes, fixes, contradicts. Um erro de digitação não consegue criar um tipo novo.
  • contradicts alimenta o lint sem LLM e continua sendo reportado até alguém resolver a contradição.
  • Na busca elas só aparecem no explain: entram no grafo como links comuns e não mexem no ranking, porque o benchmark não deu base para um peso. O memory_read_page consegue percorrê-las para listar páginas relacionadas.
Arestas tipadas na documentação

Um só assume, um só escreve.

Duas regras seguram a maior parte da corretude: um handoff só pode ser assumido uma vez, e só uma thread escreve no SQLite.

Handoff é um protocolo

  • Um handoff é um registro tipado: agente de origem e de destino, projeto, cwd, resumo, perguntas em aberto, arquivos tocados, próximos passos.
  • Aceitar é um compare and set atômico. Um segundo agente que pedir não recebe nada.
  • O cwd casa por fronteira de caminho: /repo cobre /repo/api e nunca /repo-other.
  • Um handoff manual ganha do automático. Aceitar expira os candidatos automáticos mais antigos na mesma transação.
  • Num servidor compartilhado, um handoff pertence ao dono dele, a menos que seja enviado com shared=true.

A regra do writer único, medida

Todas as escritas passam por uma fila limitada a 1024 até uma única thread do sistema operacional. As leituras usam um pool separado, só de leitura. Um pico desacelera os produtores, e nenhuma escrita é descartada.

  1. 1 writer42/s23,9 ms
  2. 8 writers295/s3,4 ms
  3. 32 writers698/s1,43 ms
  4. 128 writers700/s1,43 ms
  • O teto fica em torno de 700 escritas por segundo, estável de 32 writers para cima. Com um writer só, o gargalo é o fsync, não a CPU.
  • Medido num disco local rápido. Um volume de rede ou lento vai dar bem menos.
  • O teste exercita o store direto e pula a porta de entrada HTTP.
  • Reproduza com cargo test -p ai-memory-store --test writer_throughput -- --ignored --nocapture.

Páginas de sessão antigas recebem uma pontuação, e as frias são descartadas, compactadas ou mescladas. Como a memória envelhece.

O código, crate por crate.

Dez crates do produto, cada um com uma função e uma API tipada, sem dependências circulares.

CrateResponsabilidade
ai-memory-coreTipos de domínio, erros, ids. Sem IO.
ai-memory-storeSQLite, o actor de escrita, o pool de leitura, a matemática do decaimento.
ai-memory-wikiEscritas atômicas de markdown, o file watcher, git.
ai-memory-mcpTransporte MCP e router de ferramentas.
ai-memory-webPáginas web e API JSON somente leitura. Ações de conta e aprovação chamam rotas autenticadas.
ai-memory-hooksSchemas de payload, o sanitizador, a entrada por /hook.
ai-memory-llmFronteira de autenticação dos provedores, traits de LLM e de embedder.
ai-memory-consolidateIngestão, lint, sweep e o pipeline de auto-improve.
ai-memory-workstreamAdapters somente leitura para transcrições nativas e execuções gerenciadas.
ai-memory-cliO binário ai-memory e seus subcomandos HTTP enxutos.

25 ferramentas MCP, poucas de propósito

Os hooks fazem a captura de rotina, então os agentes raramente precisam chamar isso na mão.

Recuperação (7)

  • memory_query
  • memory_recent
  • memory_read_page
  • memory_read_session_observations
  • memory_briefing
  • memory_explore
  • memory_status

Handoffs (4)

  • memory_handoff_begin
  • memory_handoff_list
  • memory_handoff_accept
  • memory_handoff_cancel

Mensagens entre projetos (4)

  • memory_message_send
  • memory_message_list
  • memory_message_pop
  • memory_message_cancel

Escrita e manutenção (10)

  • memory_write_page
  • memory_patch_page
  • memory_delete_page
  • memory_restore_page
  • memory_consolidate
  • memory_auto_improve
  • memory_feedback
  • memory_lint
  • memory_forget_sweep
  • memory_install_self_routing

ARCHITECTURE.md, com todas as 16 invariantesDecisões de design e opções rejeitadas

Perguntas e respostas

Onde o ai-memory guarda os dados?

Num único diretório de dados: um repositório git de páginas markdown em wiki/, um índice SQLite derivado em db/, segmentos sanitizados de workstream em raw/, o modelo local de embeddings em models/ e os logs.

O ai-memory precisa de um LLM?

Não. Captura, resumos de sessão, handoffs, indexação, busca e o briefing rodam sem nenhum provedor configurado. Consolidação por LLM, auto-improve e rerank são opt-in.

O que acontece se o índice SQLite for perdido ou corrompido?

Reindex reconstrói páginas, links e busca textual a partir do Markdown. Não recupera sessões, observações, handoffs, usuários, chaves ou auditoria que existem só no banco.

Como a recuperação ranqueia os resultados?

Cinco fluxos de candidatos são fundidos: busca full-text FTS5, correspondências de entidades, vizinhos no grafo, observações e vetores opcionais. Reciprocal Rank Fusion com k=60 os combina, seguido de um ajuste limitado pela autoridade da fonte. O reranking com LLM é opcional.

Quantas escritas por segundo ele aguenta?

O store mede 42 escritas por segundo com um writer, 295 com 8 e um teto perto de 700 de 32 writers para cima. Os números vêm de um disco local rápido, e o teste exercita o store direto, sem a porta de entrada HTTP.

Leia os arquivos que ele escreve.

Instale, rode uma sessão e depois abra a pasta da wiki no seu editor.