MÓDULO 3.2

🧬 Cérebro e conectores

Memória que aprende com você: o que o cérebro guarda, como buscar, salvar e corrigir, o vault que só grava com a sua aprovação, a consolidação da madrugada e os conectores que trazem Gmail, agendas e grupos do Telegram para dentro da memória.

6
Tópicos
75
Minutos
Prático
Nível
Mão na massa
Tipo
Progresso do módulo 3.2 0%
0 de 0
1

🧠 O que o cérebro guarda

O cérebro do v3 mora em src/cerebro/ e no banco store/openpcbotv3.db. Ele não é uma coisa só: são quatro depósitos com regras diferentes. A tabela memories guarda fatos; o vetor bge-m3 guarda o significado desses fatos; o conversation_log guarda os turnos da conversa; e os insights guardam o que a consolidação percebeu sozinha. Entender qual é qual é o que evita procurar no lugar errado.

sua mensagemmais de 20 caracteresmemoriestexto, setor, origem, saliência, FTS5vetor bge-m31024 floats em BLOB, cosseno em JSconversation_logturnos do chat e dos chats observadosinsightsaté 3 por chat, da consolidação das 4hcontextoteto de 600 tokens

Legenda: as setas roxas são a escrita, que acontece a cada mensagem sua. As setas cianas tracejadas são a leitura, que acontece no começo da resposta seguinte. Nada disso sai da máquina.

DepósitoO que entraQuem escreveQuando some
memories setor semanticfrase durável: "meu", "prefiro", "sempre", "nunca", "lembra", "moro em", "meu nome"cada mensagem sua com mais de 20 caracteres; /memoria salvar; a ingestãodecai 0,5 % por dia sem uso
memories setor episodico resto da conversa que não bateu na regra de durávelo mesmo classificador por regex, em PT-BR e inglêsdecai 2 % por dia sem uso
Vetor bge-m31024 floats por memória, num BLOBo cron indexar-memoria, a cada 15 minjunto com a memória
conversation_logturnos do seu chat e dos chats observados (agent_id = 'observado')o orquestrador, a cada turno/novo apaga os do chat
Insightsaté 3 por chat, texto curtoa consolidação das 4h, no Ollamagerados na consolidação das 4h

Legenda: pergunta nunca vira fato. E frase repetida não duplica: existe dedupe por hash da frase normalizada.

Novo aqui? Saliência

Saliência é o "peso" de uma memória. Toda memória nasce com 1.0. Cada vez que ela é usada numa resposta, ganha +0.1. Cada dia sem uso, perde: memória semântica 0,5 %, episódica 2 %. Quando cai abaixo de 0.05 e não é acessada há 30 dias, o cron decaimento (3h30) apaga. O efeito prático: o que você repete fica; o que foi só do momento se dissolve sozinho, sem você limpar nada.

✓ O que vira memória boa

  • "Prefiro reunião de manhã, nunca depois das 17h." Frase afirmativa, durável, curta.
  • "Meu servidor de casa chama <nome-da-sua-maquina> e roda Ubuntu." Dado de fato, com nome.
  • /memoria salvar <texto> quando você quer garantir: entra como semantic, origem manual.

✗ O que não vira (ou vira ruim)

  • Pergunta. "Qual meu horário preferido?" nunca vira fato, por desenho.
  • Mensagem com 20 caracteres ou menos: passa direto, não é registrada como memória.
  • Parágrafo de dez linhas: a memória guarda a frase inteira, então o trecho inteiro vai junto (tópico 6).
2

🔎 /memoria e o retrieval em 3 camadas

O comando /memoria é a porta de entrada do cérebro. Sem subcomando, ele assume lista. Cada subcomando tem uma forma de resposta fixa, e conhecer essa forma poupa tempo: é nela que está o #id que você usa para esquecer, aprovar ou descartar.

SubcomandoO que fazForma da resposta
/memoria listaas mais recentes deste chatcabeçalho com total e quantas são semânticas, depois até 15 linhas #id [setor·saliência] texto, texto cortado em 90 caracteres
/memoria buscar <termo>busca no cérebro deste chataté 8 linhas #id texto cortado em 120 caracteres, ou "Nada encontrado."
/memoria salvar <texto>grava à mão como semantic, origem manual"Guardado." e o convite para levar também ao vault, com o id da proposta
/memoria esquecer <id>apaga uma memóriaconfirma o id apagado, ou "Não encontrei."
/memoria propostas|aprovar <id>|descartar <id>fila do vaulttópico 3

Legenda: o setor aparece pela primeira letra (s de semântica, e de episódica) e a saliência com duas casas. Um [s·1.00] é memória durável nova; um [e·0.31] é conversa antiga a caminho do decaimento.

/memoria lista (recriação ilustrativa, não é captura real)
Memória (412 · 96 semânticas) #511 [s·1.00] prefiro reunião de manhã, nunca depois das 17h #508 [s·1.20] meu servidor de casa roda Ubuntu e chama estacao #504 [e·0.88] hoje o deploy do portal saiu às 14h #498 [s·1.10] o domínio inema.club vence em novembro

Legenda: o #id da esquerda é o que vai em /memoria esquecer <id>. A saliência acima de 1.00 mostra memória que já foi usada em resposta.

O contexto em 3 camadas é o que monta o pedaço de memória que entra no prompt de toda conversa, com teto de 600 tokens. O README descreve o conteúdo assim: 3 por palavra-chave, 3 por vetor, 3 mais importantes, 3 mais recentes, sem repetir, mais 3 insights.

BlocoQuantosComo é escolhido
Palavra-chave3FTS5 sobre o texto da memória, com os termos da sua mensagem
Vetor3cosseno entre o vetor bge-m3 da sua mensagem e o das memórias
Mais importantes3maior saliência
Mais recentes3ordem de chegada
Insights3o que a consolidação das 4h escreveu para este chat

Legenda: os blocos se sobrepõem de propósito. Um item que apareceria em dois blocos entra uma vez só. Por isso a conta raramente fecha em 15 itens.

Copiar e rodar: ensinar um fato e recuperar

Objetivo: gravar um fato seu, achar de novo pela busca e ver a memória aparecer na contagem, tudo no chat do Telegram com o bot.

/memoria salvar meu servidor de casa chama <nome-da-sua-maquina> e roda Ubuntu
/memoria buscar <nome-da-sua-maquina>
/memoria lista

Como verificar: a primeira resposta confirma que guardou e oferece o vault com um id de proposta. A busca precisa devolver uma linha com #id e o seu texto. Na lista, ela aparece no topo com [s·1.00]. Se a busca não achar, confira se a frase tem mais de 20 caracteres e se você usou salvar e não buscar.

Dica prática

Memória recém-criada ainda não tem vetor: o cron indexar-memoria só passa a cada 15 minutos. Nesse intervalo ela é achável por palavra-chave, não por significado. Se você acabou de ensinar um fato e a busca por sinônimo não acha, não é bug: espere o próximo ciclo ou busque pelas palavras exatas. Para medir quanto do prompt é memória, use /context detail (Trilha 4), que lê sem inflar a saliência.

3

📓 Vault: MEMORY.md e USER.md

O banco é automático: enche sozinho e decai sozinho. O vault é o contrário: dois arquivos de texto em ~/vault/ que só mudam quando você aprova. MEMORY.md é o caderno do que importa; USER.md é quem você é, e entra no prompt de toda conversa. Nada é escrito sem aprovação, nem pelo bot, nem por um agente.

Banco (memories)Vault (~/vault/*.md)
Como entrasozinho, a cada mensagem suao bot propõe, você aprova com /memoria aprovar <id>
Formatolinha por fato, com setor e saliênciamarkdown que você pode abrir e editar no editor
Decaisim, por dia sem usonão, fica até você tirar
No promptpelo retrieval, dentro dos 600 tokensUSER.md inteiro, em toda conversa
Onde aparece/memoria listaos primeiros 500 caracteres de MEMORY.md vão no /daily

Legenda: use o banco para o volume e o vault para o que você não aceita perder. O vault é pequeno de propósito: ele custa tokens em toda mensagem.

✓ O que merece o vault

  • Seu nome, seu fuso, como você quer ser tratado: isso é USER.md.
  • Regras que valem sempre, do tipo "publicar é commit e push, nunca mexer no painel do provedor".
  • Rever a fila com /memoria propostas uma vez por semana e decidir tudo de uma vez.
  • Abrir o arquivo no editor quando quiser reorganizar: é markdown comum, seu.

✗ O que não põe no vault

  • Fato de um dia só ("hoje o deploy saiu às 14h"): isso é episódico, deixa decair.
  • Senha, token ou chave. A guarda de exfiltração redige na saída, mas o arquivo em disco não é lugar de segredo.
  • Aprovar toda proposta que aparece: USER.md inflado entra em cada prompt e come contexto de graça.
  • Esperar que /memoria esquecer limpe o vault: ele age no banco. Vault se edita no arquivo ou se descarta antes de aprovar.

Copiar e rodar: aprovar uma entrada no vault

Objetivo: ver a fila de propostas, aprovar uma e confirmar que o arquivo mudou.

/memoria propostas
/memoria aprovar <id-da-proposta>
/daily

Como verificar: as propostas vêm no formato #id → arquivo: texto. Ao aprovar, o bot confirma em qual arquivo gravou. Fora do chat, cat ~/vault/MEMORY.md mostra a linha nova, e o /daily passa a trazer o começo do arquivo no fim do resumo. Para recusar, /memoria descartar <id>.

4

🌙 Consolidação noturna

Memória que só cresce vira lixo. Toda madrugada o v3 passa três vezes pelo cérebro, em ordem, e sempre no Ollama local: custo zero. O resultado é um banco menor, mais coerente e com o que é novo por cima do que ficou velho.

1

3h30: decaimento

Reduz a saliência de tudo que passou o dia sem ser usado: 0,5 % para semântica, 2 % para episódica. O que ficou abaixo de 0.05 e não é acessado há 30 dias é apagado. É a poda antes da limpeza.

2

4h: consolidacao-noturna

Funde duplicatas e fica com a mais recente. Detecta contradições por data: a memória antiga recebe superseded_by apontando para a nova e nunca é apagada, então dá para auditar o que mudou. Por fim escreve até 3 insights por chat.

3

4h15: backup-noturno

Copia o banco já consolidado com VACUUM INTO, cifra com age (ou comprime com gzip quando não há destinatário) e guarda em store/backups/, com retenção de 14. Backup sempre depois da limpeza, nunca antes.

4

Quando você não quer esperar

/consolidar enfileira a consolidação agora, como job na lane ollama, com uma tentativa só. O bot devolve o número do job; /status <id> acompanha. Como a lane ollama roda um por vez, ela espera a indexação ou a ingestão que estiver em curso.

Copiar e rodar: forçar a consolidação e conferir

Objetivo: rodar a consolidação fora do horário e ver o efeito na contagem de memórias.

/memoria lista
/consolidar
/status <numero-do-job-que-o-bot-devolveu>
/memoria lista

Como verificar: anote o total do cabeçalho da primeira lista. O /consolidar responde com o job enfileirado. Quando o /status mostrar done, a segunda lista precisa ter total igual ou menor, nunca maior. Se o job falhar, quase sempre é Ollama fora de ar: confira com /health e /ollama status.

Dica prática

Contradição não é erro: é como você corrige o bot sem comando nenhum. Se a memória diz "moro em Porto Alegre" e você escreve "mudei para Florianópolis", a consolidação marca a antiga com superseded_by e a nova passa a valer. O caminho rápido de correção é esse, e o caminho cirúrgico é /memoria esquecer <id> (tópico 6).

5

🔌 Conectores: Gmail, agendas e Telegram observado

Conector é o que liga o bot às suas fontes de informação. A documentação separa de propósito três coisas que costumam ser confundidas, e a diferença entre elas é a diferença entre um bot que lê e um bot que aprende.

O que éComo se liga
Acessoo bot consegue ler a fonte quando você pedetoken OAuth por conta (Google) ou o próprio bot (Telegram)
Observaçãoo bot vê o que passa, sem responderlista de chats observados no .env
Ingestãoo que passou vira fato na memóriacron que lê o novo e extrai fatos no Ollama, custo zero

Legenda: acesso sem ingestão é o comportamento do v2: o bot lê seu e-mail quando você manda, e esquece depois. A ingestão é o que o v3 acrescenta.

Gmail: N contasCalendar: N agendasTelegram observadoingestão em loteOllama local, custo zeromemoriesorigem = a fonte, FTS5 + vetorconsolidação das 4h: duplicatas e contradiçõescontexto de toda conversa sua

Legenda: nada nesse caminho sai da máquina. A extração roda no modelo local, e por isso um grupo movimentado ou uma caixa de entrada cheia não geram custo nenhum.

Google, uma credencial para todas as contas. Você cria um projeto no Google Cloud, ativa Gmail API e Google Calendar API, configura a tela de consentimento como External com todos os seus e-mails em Test users, gera um OAuth client ID do tipo Desktop app e salva o JSON como ~/.config/google/credentials.json com permissão 600. Essa credencial serve todas as contas. Cada conta ganha um apelido em ~/.config/google/contas.json e um token próprio, criado uma vez com auth. Os scripts moram no repo, em conectores/google/; os segredos nunca entram no repo.

PassoComando ou arquivoResultado
Credencial, uma vez~/.config/google/credentials.json (chmod 600)o "app" que serve todas as contas
Declarar as contas~/.config/google/contas.json: apelido para e-maila primeira do arquivo é a padrão; GOOGLE_CONTA_PADRAO fixa outra
Autenticar cada umapython3 gmail.py --conta <apelido> auth~/.config/google/token_gmail_<apelido>.json, renovado sozinho
Agendapython3 gcal.py --conta <apelido> authmesmo arquivo de contas, consentimento separado porque o escopo é outro
Usar--conta aceita um apelido, vários por vírgula, ou todassaída sempre em JSON; conta com token quebrado avisa e não derruba as outras
Ligar a ingestão/cron on gmail-ingestao · /cron on agenda-ingestaoe-mail a cada 2 h (últimas 3 h); agenda às 7h05 (próximos 14 dias)

Legenda: agenda compartilhada entra sozinha, sem apelido novo: o token da conta enxerga tudo que aquela conta enxerga no Google Calendar. Memória vinda do e-mail fica com origem gmail:<apelido>; da agenda, gcal:<apelido>.

Telegram observado. Um bot vê o que acontece na frente dele, dali para frente. Ele lê mensagens dos chats onde está, lê todo o texto de um grupo se a privacidade estiver desligada, lê legenda de foto e vídeo e recebe encaminhadas. Ele não lê suas conversas privadas com outras pessoas, não lê histórico anterior à entrada dele, não lista seus contatos e não lê canal onde não é membro. Para grupo funcionar, desligue Group Privacy no BotFather e remova e adicione o bot de volta em cada grupo: a configuração só vale a partir da nova entrada.

Variável no .envO que defineRegra
TELEGRAM_CHATS_RESPONDERchats onde o bot conversapadrão: só o ALLOWED_CHAT_ID
TELEGRAM_CHATS_OBSERVARchats que ele só observa: grava e nunca respondeaceita lista, ou todos para qualquer chat em que ele for adicionado
Chat nas duas listasvale como chat de conversa
Chat fora das duasignorado, nem grava
As duas vaziaso bot responde a quem falar com ele

Legenda: para descobrir o id, mande /chatid no chat ou olhe o log, onde um chat desconhecido aparece uma vez justamente para isso. Grupo tem id negativo; conversa pessoal tem id positivo. Depois de editar o .env, aplique com bash scripts/instalar-servico.sh e confirme com /fontes.

O que acontece com uma mensagem observada: ela entra no conversation_log com agent_id = 'observado', o autor no início do texto e o nome do grupo junto. Nenhuma resposta, nenhum modelo, nenhum custo nesse momento. A cada 30 minutos o cron ingestao-observados pega o que chegou desde a última passagem, monta um bloco por grupo e faz uma única chamada no Ollama residente pedindo os fatos duráveis: decisão, compromisso, prazo, preferência, dado de pessoa, valor combinado. Cada fato vira memória com origem telegram:<chatId>, entra no FTS5 e ganha vetor no ciclo seguinte. O marcador ingestao_estado garante que rodar duas vezes não reprocessa; se o Ollama estiver fora, o bloco não é marcado e volta na próxima passagem.

Copiar e rodar: conferir as fontes e forçar a ingestão

Objetivo: ver quais chats o bot observa, quanto de cada fonte já virou memória e rodar a ingestão sem esperar o cron.

/fontes
/fontes ingerir
/status <numero-do-job-que-o-bot-devolveu>
/memoria buscar <palavra-que-apareceu-no-grupo>

Como verificar: o /fontes mostra em quais chats o bot responde, quais observa e, por fonte, quantos itens já foram ingeridos e quando foi o último. O /fontes ingerir devolve o job; quando ele fica done, a busca precisa achar o fato. Sem argumento, ingerir roda só os chats observados; /fontes ingerir gmail e /fontes ingerir agenda fazem o mesmo para os conectores Google. Ingestão que devolve zero fato costuma ser conversa sem nada durável, e isso é o esperado.

Dica prática

Grupo observado é conversa de outras pessoas, e elas não sabem que um bot está guardando fatos. Em grupo de trabalho isso costuma ser aceitável; em grupo de família ou de terceiros, pergunte antes. Vale lembrar o limite técnico do outro lado: importar o seu Telegram inteiro, com DMs e histórico, exigiria uma sessão de usuário via MTProto, que não está implementada no v3 e cujo arquivo de sessão equivale à senha da sua conta. A documentação descreve o desenho e diz de forma explícita que isso só é ligado se você pedir.

6

🧹 O que a memória ainda não faz

O README é direto sobre o limite principal: a memória guarda a frase inteira, não um fato extraído. Quando você conversa com o bot, a sua mensagem é classificada e armazenada como veio. Não há um passo que a reduza a "usuário prefere reunião de manhã". Repare no contraste com o tópico anterior: na ingestão de e-mail, agenda e grupo observado existe extração, feita em lote no Ollama. Na sua conversa, não.

CaminhoPassa por extraçãoO que fica gravado
Você conversa com o botnãoa frase inteira, classificada em semântica ou episódica
Chat observado do Telegramsim, a cada 30 minfatos duráveis extraídos do bloco do grupo
Gmail e Google Calendarsim, no cron de cada umfatos duráveis, com a conta na origem

Legenda: a consequência prática é simples. Frase longa vira memória longa, come parte dos 600 tokens do contexto e pode trazer junto um detalhe que já não vale.

✓ Como corrigir uma memória errada

  • /memoria buscar <termo-errado> para achar o #id, depois /memoria esquecer <id>. É o corte cirúrgico.
  • Afirmar o fato novo e deixar a consolidação das 4h marcar o antigo com superseded_by. O histórico fica auditável.
  • /memoria salvar <frase-curta-e-correta>: escrever você mesmo a versão enxuta que queria que a extração tivesse feito.
  • Para o que não pode errar nunca, levar ao vault com /memoria aprovar: lá não decai e não se contradiz sozinho.

✗ O que não resolve

  • Pedir em prosa "esquece aquilo": a frase vira mais uma memória, e a errada continua lá.
  • /novo: apaga sessão e histórico do chat, não toca em memories nem no vault.
  • Esperar /retry ou /undo: estão anotados como fase 10, não existem hoje.
  • Apagar o banco para "limpar": a tabela jobs nunca é purgada e cresce cerca de 1,5 mil linhas por dia, mas isso é histórico de fila, não memória.

Novo aqui? Apagar uma fonte inteira

Esquecer um fato é /memoria esquecer <id>. Desfazer uma fonte inteira, por exemplo tudo que veio de um grupo do Telegram, é um DELETE FROM memories WHERE origem = 'telegram:<id-do-grupo>' no banco, com o serviço parado. Essa não tem comando de chat, e é assim de propósito: apagar em lote não deve ser fácil de fazer sem querer.

Dica prática

Duas proteções que valem saber antes de ligar tudo. Os escopos do Gmail hoje são leitura, modificação e envio, para o bot poder marcar como lida e responder; se você quer só leitura, tire gmail.send e gmail.modify de SCOPES_GMAIL em conectores/google/comum.py e refaça o auth de cada conta. E toda resposta que sai passa pela guarda de exfiltração: token, chave de API, JWT e os valores das variáveis sensíveis do .env viram [REDIGIDO], inclusive quando vieram de um e-mail ingerido.

Resumo do módulo

Quatro depósitos - memories com setor e saliência, vetor bge-m3, conversation_log e insights; pergunta nunca vira fato
/memoria é a porta - lista traz 15, buscar traz 8, salvar grava e propõe o vault, esquecer apaga pelo #id
Retrieval com teto de 600 tokens - palavra-chave, vetor, mais importantes, mais recentes e insights, sem repetir item
Vault só com aprovação - MEMORY.md e USER.md em ~/vault/; USER.md entra em toda conversa
Madrugada em três passos - 3h30 decai, 4h consolida (duplicatas, superseded_by, insights), 4h15 copia; /consolidar antecipa
Conectores e o limite - uma credencial Google para N contas, Telegram observado sem resposta, /fontes; sua conversa guarda a frase inteira, não o fato

Próxima trilha:

Trilha 4 - Configurar e estender: interruptores, modos de fila, persona, custo do prompt, agentes e skills com a sua cara.