Memória
A memória do OpenClaw é Markdown simples no workspace do agente. Os arquivos são a fonte da verdade; o modelo só “lembra” do que é gravado em disco. As ferramentas de busca de memória são fornecidas pelo plugin de memória ativo (padrão:memory-core). Desative plugins de memória com plugins.slots.memory = "none".
Arquivos de memória (Markdown)
O layout padrão do workspace usa duas camadas de memória:memory/YYYY-MM-DD.md- Log diário (somente append).
- Lê hoje + ontem no início da sessão.
MEMORY.md(opcional)- Memória de longo prazo curada.
- Carregada apenas na sessão principal e privada (nunca em contextos de grupo).
agents.defaults.workspace, padrão
~/.openclaw/workspace). Veja Agent workspace para o layout completo.
Quando escrever memória
- Decisões, preferências e fatos duráveis vão para
MEMORY.md. - Notas do dia a dia e contexto em andamento vão para
memory/YYYY-MM-DD.md. - Se alguém disser “lembre disso”, escreva (não mantenha apenas na RAM).
- Esta área ainda está evoluindo. Ajuda lembrar o modelo de armazenar memórias; ele saberá o que fazer.
- Se você quer que algo persista, peça ao bot para escrever na memória.
Limpeza automática de memória (ping pré-compactação)
Quando uma sessão está próxima da auto-compactação, o OpenClaw dispara um turno agêntico silencioso que lembra o modelo de escrever memória durável antes que o contexto seja compactado. Os prompts padrão dizem explicitamente que o modelo pode responder, mas normalmenteNO_REPLY é a resposta correta para que o usuário nunca veja esse turno.
Isso é controlado por agents.defaults.compaction.memoryFlush:
- Limite suave: a limpeza dispara quando a estimativa de tokens da sessão cruza
contextWindow - reserveTokensFloor - softThresholdTokens. - Silencioso por padrão: os prompts incluem
NO_REPLYpara que nada seja entregue. - Dois prompts: um prompt de usuário mais um prompt de sistema anexam o lembrete.
- Uma limpeza por ciclo de compactação (rastreada em
sessions.json). - O workspace precisa ser gravável: se a sessão rodar em sandbox com
workspaceAccess: "ro"ou"none", a limpeza é ignorada.
Busca de memória vetorial
O OpenClaw pode construir um pequeno índice vetorial sobreMEMORY.md e memory/*.md para que
consultas semânticas encontrem notas relacionadas mesmo quando a redação difere.
Padrões:
- Ativado por padrão.
- Observa arquivos de memória para mudanças (com debounce).
- Configure a busca de memória em
agents.defaults.memorySearch(não no nível superiormemorySearch). - Usa embeddings remotos por padrão. Se
memorySearch.providernão estiver definido, o OpenClaw seleciona automaticamente:localse ummemorySearch.local.modelPathestiver configurado e o arquivo existir.openaise uma chave da OpenAI puder ser resolvida.geminise uma chave do Gemini puder ser resolvida.voyagese uma chave da Voyage puder ser resolvida.- Caso contrário, a busca de memória permanece desativada até ser configurada.
- O modo local usa node-llama-cpp e pode exigir
pnpm approve-builds. - Usa sqlite-vec (quando disponível) para acelerar a busca vetorial dentro do SQLite.
models.providers.*.apiKey ou variáveis
de ambiente. O OAuth do Codex cobre apenas chat/completions e não atende
embeddings para busca de memória. Para Gemini, use GEMINI_API_KEY ou
models.providers.google.apiKey. Para Voyage, use VOYAGE_API_KEY ou
models.providers.voyage.apiKey. Ao usar um endpoint OpenAI-compatível personalizado,
defina memorySearch.remote.apiKey (e opcional memorySearch.remote.headers).
Backend QMD (experimental)
Definamemory.backend = "qmd" para trocar o indexador SQLite embutido por
QMD: um sidecar de busca local-first que combina
BM25 + vetores + reranking. O Markdown continua sendo a fonte da verdade; o OpenClaw
chama o QMD para recuperação. Pontos-chave:
Pré-requisitos
- Desativado por padrão. Opte por config (
memory.backend = "qmd"). - Instale a CLI do QMD separadamente (
bun install -g https://github.com/tobi/qmdou baixe um release) e garanta que o binárioqmdesteja noPATHdo gateway. - O QMD precisa de um build do SQLite que permita extensões (
brew install sqliteno macOS). - O QMD roda totalmente local via Bun +
node-llama-cppe faz download automático de modelos GGUF do HuggingFace no primeiro uso (não é necessário um daemon Ollama separado). - O gateway executa o QMD em um home XDG autocontido sob
~/.openclaw/agents/<agentId>/qmd/definindoXDG_CONFIG_HOMEeXDG_CACHE_HOME. - Suporte a SO: macOS e Linux funcionam imediatamente após instalar Bun + SQLite. Windows é melhor suportado via WSL2.
- O gateway grava um home QMD autocontido sob
~/.openclaw/agents/<agentId>/qmd/(config + cache + DB sqlite). - Coleções são criadas via
qmd collection adda partir dememory.qmd.paths(mais os arquivos de memória padrão do workspace), depoisqmd update+qmd embedrodam no boot e em um intervalo configurável (memory.qmd.update.interval, padrão 5 m). - O gateway agora inicializa o gerenciador QMD na inicialização, para que os temporizadores de atualização periódica
sejam ativados mesmo antes da primeira chamada
memory_search. - A atualização no boot agora roda em segundo plano por padrão para não bloquear
o início do chat; defina
memory.qmd.update.waitForBootSync = truepara manter o comportamento bloqueante anterior. - As buscas rodam via
qmd query --json. Se o modo selecionado rejeitar flags na sua build do QMD, o OpenClaw tenta novamente comqmd query. Se o QMD falhar ou o binário estiver ausente, o OpenClaw retorna automaticamente ao gerenciador SQLite embutido para que as ferramentas de memória continuem funcionando. - O OpenClaw não expõe ajuste de batch-size de embeddings do QMD hoje; o comportamento de batch é controlado pelo próprio QMD.
- A primeira busca pode ser lenta: o QMD pode baixar modelos GGUF locais (reranker/expansão
de consulta) na primeira execução de
qmd query.-
O OpenClaw define
XDG_CONFIG_HOME/XDG_CACHE_HOMEautomaticamente quando executa o QMD. -
Se você quiser pré-baixar modelos manualmente (e aquecer o mesmo índice que o OpenClaw
usa), execute uma consulta única com os diretórios XDG do agente.
O estado do QMD do OpenClaw fica no seu diretório de estado (padrão
~/.openclaw). Você pode apontarqmdpara exatamente o mesmo índice exportando as mesmas variáveis XDG que o OpenClaw usa:
-
O OpenClaw define
memory.qmd.*)
command(padrãoqmd): sobrescreve o caminho do executável.includeDefaultMemory(padrãotrue): indexa automaticamenteMEMORY.md+memory/**/*.md.includeDefaultMemory(padrãotrue): indexa automaticamenteMEMORY.md+memory/**/*.md.paths[]: adiciona diretórios/arquivos extras (path, opcionalpattern, opcional estávelname).sessions: opta por indexação de JSONL de sessão (enabled,retentionDays,exportDir).update: controla a cadência de atualização e execução de manutenção: (interval,debounceMs,onBoot,waitForBootSync,embedInterval,commandTimeoutMs,updateTimeoutMs,embedTimeoutMs).limits: limita o payload de recall (maxResults,maxSnippetChars,maxInjectedChars,timeoutMs).scope: mesmo esquema desession.sendPolicy. O padrão é apenas DM (denytodos,allowchats diretos); afrouxe para expor resultados do QMD em grupos/canais.match.keyPrefixcorresponde à chave de sessão normalizada (em minúsculas, com qualquer prefixoagent:<id>:removido). Exemplo:discord:channel:.match.rawKeyPrefixcorresponde à chave de sessão bruta (em minúsculas), incluindoagent:<id>:. Exemplo:agent:main:discord:.- Legado:
match.keyPrefix: "agent:..."ainda é tratado como um prefixo de chave bruta, mas prefirarawKeyPrefixpara maior clareza.
- When
scopedenies a search, OpenClaw logs a warning with the derivedchannel/chatTypeso empty results are easier to debug. - Trechos originados fora do workspace aparecem como
qmd/<collection>/<relative-path>nos resultados dememory_search;memory_getentende esse prefixo e lê a partir da raiz da coleção QMD configurada. - Quando
memory.qmd.sessions.enabled = true, o OpenClaw exporta transcrições de sessão higienizadas (turnos de Usuário/Assistente) para uma coleção QMD dedicada sob~/.openclaw/agents/<id>/qmd/sessions/, para quememory_searchpossa relembrar conversas recentes sem tocar no índice SQLite embutido. - Os trechos de
memory_searchagora incluem um rodapéSource: <path#line>quandomemory.citationséauto/on; definamemory.citations = "off"para manter os metadados de caminho internos (o agente ainda recebe o caminho paramemory_get, mas o texto do trecho omite o rodapé e o prompt de sistema alerta o agente para não citá-lo).
memory.citationsse aplica independentemente do backend (auto/on/off).- Quando
qmdroda, marcamosstatus().backend = "qmd"para que os diagnósticos mostrem qual mecanismo serviu os resultados. Se o subprocesso do QMD sair ou a saída JSON não puder ser analisada, o gerenciador de busca registra um aviso e retorna o provedor embutido (embeddings Markdown existentes) até o QMD se recuperar.
Caminhos de memória adicionais
Se você quiser indexar arquivos Markdown fora do layout padrão do workspace, adicione caminhos explícitos:- Os caminhos podem ser absolutos ou relativos ao workspace.
- Diretórios são varridos recursivamente por arquivos
.md. - Apenas arquivos Markdown são indexados.
- Symlinks são ignorados (arquivos ou diretórios).
Embeddings Gemini (nativo)
Defina o provedor comogemini para usar a API de embeddings do Gemini diretamente:
remote.baseUrlé opcional (padrão é a URL base da API do Gemini).remote.headerspermite adicionar headers extras, se necessário.- Modelo padrão:
gemini-embedding-001.
remote com o provedor OpenAI:
memorySearch.provider = "local" ou defina
memorySearch.fallback = "none".
Fallbacks:
memorySearch.fallbackpode seropenai,gemini,localounone.- O provedor de fallback só é usado quando o provedor primário de embeddings falha.
- Ativada por padrão para embeddings OpenAI e Gemini. Defina
agents.defaults.memorySearch.remote.batch.enabled = falsepara desativar. - O comportamento padrão aguarda a conclusão do batch; ajuste
remote.batch.wait,remote.batch.pollIntervalMseremote.batch.timeoutMinutesse necessário. - Defina
remote.batch.concurrencypara controlar quantos jobs de batch enviamos em paralelo (padrão: 2). - O modo batch se aplica quando
memorySearch.provider = "openai"ou"gemini"e usa a chave de API correspondente. - Jobs de batch do Gemini usam o endpoint assíncrono de batch de embeddings e exigem disponibilidade da Gemini Batch API.
- Para grandes backfills, a OpenAI geralmente é a opção mais rápida que suportamos porque podemos enviar muitas requisições de embedding em um único job de batch e deixar a OpenAI processá-las de forma assíncrona.
- A OpenAI oferece preços com desconto para workloads da Batch API, então grandes execuções de indexação costumam ser mais baratas do que enviar as mesmas requisições de forma síncrona.
- Veja a documentação e preços da OpenAI Batch API para detalhes:
memory_search— retorna trechos com arquivo + intervalos de linha.memory_get— lê o conteúdo de um arquivo de memória pelo caminho.
- Defina
agents.defaults.memorySearch.provider = "local". - Forneça
agents.defaults.memorySearch.local.modelPath(GGUF ou URIhf:). - Opcional: defina
agents.defaults.memorySearch.fallback = "none"para evitar fallback remoto.
Como as ferramentas de memória funcionam
memory_searchfaz busca semântica em chunks Markdown (~alvo de 400 tokens, sobreposição de 80 tokens) deMEMORY.md+memory/**/*.md. Retorna texto do trecho (limitado a ~700 caracteres), caminho do arquivo, intervalo de linhas, score, provedor/modelo e se houve fallback de embeddings local → remoto. Nenhum payload de arquivo completo é retornado.memory_getlê um arquivo Markdown específico de memória (relativo ao workspace), opcionalmente a partir de uma linha inicial e por N linhas. Caminhos fora deMEMORY.md/memory/são rejeitados.- Ambas as ferramentas só são habilitadas quando
memorySearch.enabledresolve como true para o agente.
O que é indexado (e quando)
- Tipo de arquivo: apenas Markdown (
MEMORY.md,memory/**/*.md). - Armazenamento do índice: SQLite por agente em
~/.openclaw/memory/<agentId>.sqlite(configurável viaagents.defaults.memorySearch.store.path, suporta token{agentId}). - Atualidade: watcher em
MEMORY.md+memory/marca o índice como sujo (debounce 1,5s). A sincronização é agendada no início da sessão, na busca ou em um intervalo e roda de forma assíncrona. Transcrições de sessão usam limiares de delta para disparar sync em segundo plano. - Gatilhos de reindexação: o índice armazena provedor/modelo de embedding + fingerprint do endpoint + parâmetros de chunking. Se qualquer um mudar, o OpenClaw reseta e reindexa automaticamente todo o armazenamento.
Busca híbrida (BM25 + vetor)
Quando ativada, o OpenClaw combina:- Similaridade vetorial (correspondência semântica, a redação pode diferir)
- Relevância por palavra-chave BM25 (tokens exatos como IDs, env vars, símbolos de código)
Por que híbrida?
Busca vetorial é ótima para “isso significa a mesma coisa”:- “Mac Studio gateway host” vs “a máquina rodando o gateway”
- “debounce de atualizações de arquivo” vs “evitar indexar a cada gravação”
- IDs (
a828e60,b3b9895a…) - símbolos de código (
memorySearch.query.hybrid) - strings de erro (“sqlite-vec unavailable”)
Como mesclamos resultados (design atual)
Esboço de implementação:- Recuperar um pool de candidatos de ambos os lados:
- Vetor: top
maxResults * candidateMultiplierpor similaridade de cosseno. - BM25: top
maxResults * candidateMultiplierpor rank BM25 do FTS5 (quanto menor, melhor).
- Converter o rank BM25 em um score tipo 0..1:
textScore = 1 / (1 + max(0, bm25Rank))
- Unir candidatos por id de chunk e calcular um score ponderado:
finalScore = vectorWeight * vectorScore + textWeight * textScore
vectorWeight+textWeighté normalizado para 1,0 na resolução de config, então os pesos se comportam como percentuais.- Se embeddings estiverem indisponíveis (ou o provedor retornar um vetor zero), ainda rodamos BM25 e retornamos correspondências por palavra-chave.
- Se o FTS5 não puder ser criado, mantemos busca somente vetorial (sem falha dura).
Cache de embeddings
O OpenClaw pode armazenar em cache embeddings de chunks no SQLite para que reindexações e atualizações frequentes (especialmente transcrições de sessão) não re-embedem texto inalterado. Configuração:Busca de memória de sessão (experimental)
Você pode opcionalmente indexar transcrições de sessão e expô-las viamemory_search.
Isso fica atrás de uma flag experimental.
- A indexação de sessão é opt-in (desligada por padrão).
- Atualizações de sessão têm debounce e são indexadas de forma assíncrona quando cruzam limiares de delta (best-effort).
memory_searchnunca bloqueia aguardando indexação; os resultados podem ficar levemente desatualizados até a sincronização em segundo plano terminar.- Os resultados ainda incluem apenas trechos;
memory_getpermanece limitado a arquivos de memória. - A indexação de sessão é isolada por agente (somente os logs de sessão daquele agente são indexados).
- Logs de sessão ficam em disco (
~/.openclaw/agents/<agentId>/sessions/*.jsonl). Qualquer processo/usuário com acesso ao filesystem pode lê-los, então trate o acesso ao disco como o limite de confiança. Para isolamento mais rigoroso, execute agentes sob usuários de SO separados ou hosts distintos.
Aceleração vetorial SQLite (sqlite-vec)
Quando a extensão sqlite-vec está disponível, o OpenClaw armazena embeddings em uma tabela virtual SQLite (vec0) e executa consultas de distância vetorial no
banco de dados. Isso mantém a busca rápida sem carregar todos os embeddings em JS.
Configuração (opcional):
enabledé true por padrão; quando desativado, a busca faz fallback para similaridade de cosseno em processo sobre embeddings armazenados.- Se a extensão sqlite-vec estiver ausente ou falhar ao carregar, o OpenClaw registra o erro e continua com o fallback em JS (sem tabela vetorial).
extensionPathsobrescreve o caminho do sqlite-vec empacotado (útil para builds personalizados ou locais de instalação não padrão).
Download automático de embeddings locais
- Modelo padrão de embedding local:
hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf(~0,6 GB). - Quando
memorySearch.provider = "local",node-llama-cppresolvemodelPath; se o GGUF estiver ausente, ele faz download automático para o cache (oulocal.modelCacheDirse definido) e então carrega. Downloads retomam na tentativa seguinte. - Requisito de build nativo: execute
pnpm approve-builds, escolhanode-llama-cpp, depoispnpm rebuild node-llama-cpp. - Fallback: se a configuração local falhar e
memorySearch.fallback = "openai", alternamos automaticamente para embeddings remotos (openai/text-embedding-3-smallsalvo sobrescrita) e registramos o motivo.
Exemplo de endpoint OpenAI-compatível personalizado
remote.*tem precedência sobremodels.providers.openai.*.remote.headersse mescla com os headers da OpenAI; o remoto vence em conflitos de chave. Omitaremote.headerspara usar os padrões da OpenAI.