Pinky Brain — Plano de arquitetura (local)
Sistema de gestão de conhecimento para agentes + humanos, escalável a vários projetos durante 2+ anos. Evolução do quack-brain. Decisão tomada: modo LOCAL. SQLite embarcado, sem servidor, sem rede. Sem abstração de storage (um único backend real → não se abstrai, YAGNI).
1. Visão e objetivos
- Fonte da verdade legível e versionada: o conhecimento vive em markdown + git. É editado pelo agente com suas tools nativas e revisado por um humano com
git diff. - Retrieval real: busca híbrida (full-text + semântica), não
grepmanual. - Multiprojeto: conhecimento de projeto + conhecimento global entre projetos.
- Durável 2+ anos: o índice é 100% regenerável a partir do markdown; nenhum dado crítico vive apenas na DB.
- Zero ops: um binário + um arquivo
brain.db. Sem Docker, sem daemon, offline.
2. Princípios de design
- Markdown = fonte da verdade. SQLite = índice derivado, descartável. Se a DB se corromper →
pinky reindexa reconstrói a partir dos.md. - SOLID sem sobre-engenharia. Um módulo
storeconcreto sobre SQLite, **sem trait de abstração**: não há um segundo backend, então não se abstrai. Se algum dia aparecer (improvável), extrai-se o trait então — não antes. - Pull semântico sobre push cego. O agente busca quando precisa (tool MCP), em vez de injetar todo o contexto em cada hook.
- Offline-first. Funciona sem rede. Embeddings locais. A rede só entra no
git pull/pushdo brain global, e é opcional. - Append-only onde for possível (diary) para que o git faça merge sem conflitos.
3. Arquitetura
┌─ Fuente de verdad (git) ─────────────────────────────┐
│ <proyecto>/documentation/*.md │
│ ~/.pinky/brain/*.md (global cross-proyecto)│
└───────────────────────┬──────────────────────────────┘
│ notify (file watcher)
▼
┌─ pinky-core (lib Rust) ──────────────────────────────┐
│ parse(frontmatter) → chunk → embed → upsert │
│ search híbrido (BM25 + vector, fusión RRF) → rerank │
│ módulo `store`: SQLite (sqlite-vec + FTS5) │
└───────────────────────┬──────────────────────────────┘
┌───────────────┼────────────────┐
▼ ▼ ▼
pinky (CLI) pinky-mcp (server) pinky-hooks
reindex/search brain_search tool SessionStart/Stop
pinky-core: biblioteca com toda a lógica (parse, chunk, embed, search, store).store: módulo concreto sobrerusqlite+sqlite-vec+ FTS5. Sem trait.pinky-mcp: expõebrain_search,brain_save,brain_statscomo MCP server. O agente faz pull semântico em vez de grep.- CLI
pinky:reindex,search,doctor,stats,gc. pinky-hooks: os 4 hooks do quack-brain (SessionStart/PreRead/PreWrite/Stop), finos: o retrieval pesado é delegado ao core/MCP em vez de injetar tudo.
4. Modelo de dados (SQLite)
O frontmatter YAML é mapeado para metadata indexada para filtrar:
entry(
id TEXT PK, -- hash estable del path
path TEXT, -- ruta del .md (relativa al root)
scope TEXT, -- 'project:<name>' | 'global'
type TEXT, -- gotcha | pattern | bug | decision | diary | guide
project TEXT,
tags TEXT, -- JSON array
created TEXT, -- ISO date
last_verified TEXT, -- para staleness/decay (§9)
title TEXT,
body TEXT,
content_hash TEXT -- re-index incremental (skip si no cambió)
)
chunk_fts FTS5 virtual table (text) -- BM25
chunk_vec sqlite-vec virtual table (embedding float[384])
chunk(
id, entry_id, ord, text -- une fts + vec con metadata
)
backlink( -- grafo código ↔ conocimiento desde `// Brain: {slug}`
entry_id, file_path, line, repo
)
usage( -- telemetría: qué entradas se recuperan/usan de verdad
entry_id, retrieved_at, query, was_useful
)
5. Stack tecnológico
| Necessidade | Escolha | Por quê |
|---|---|---|
| Linguagem | Rust | Latência <50ms nos hooks, um binário, sem runtime |
| Índice | rusqlite + sqlite-vec + FTS5 | Um arquivo, híbrido em uma query, in-process |
| Embeddings | fastembed (ONNX, multilingual-e5-small) | Local, sem API, sem custo por hook, ES+IT |
| Rerank (opcional) | cross-encoder ONNX (bge-reranker-base) | Precisão após o retrieval híbrido |
| Frontmatter | gray_matter | YAML + body em um passo |
| File watching | notify | Re-index incremental |
| MCP | rmcp (SDK oficial Rust) | brain_search como tool |
| Async runtime | tokio | Server MCP + watcher |
Embeddings multilíngues: multilingual-e5-small (384 dims) cobre espanhol e italiano (cursos SGSVP) sem custo por query. Se mais adiante for preciso mais recall, bge-m3. O modelo é versionado na metadata do chunk → reindex global ao trocá-lo.
6. Retrieval híbrido
Não só vetor. BM25 (FTS5) + vetor (sqlite-vec), fundidos com Reciprocal Rank Fusion, depois rerank opcional:
- BM25: termos exatos (slugs, nomes de função, error strings).
- Vetor: similaridade semântica ("algo parecido com este problema").
- RRF: funde ambos os rankings sem calibrar pesos.
- Rerank (cross-encoder) sobre o top-N para precisão final.
- Filtros de metadata: por
scope,project,type,tags, frescor.
O stack híbrido (FTS5 + sqlite-vec + embeddings) vai desde a Fase 0, sem etapas intermediárias de só-texto.
7. Sync e multiprojeto
- Conhecimento de projeto: vive no repo do projeto (
documentation/), versionado com o código. - Conhecimento global: repo git dedicado clonado em
~/.pinky/brain. Sync entre suas máquinas =git pull/push(isto NÃO é "modo compartilhado": continua sendo local, o git é apenas o transporte). Append-only no diary → merges sem conflito. - O índice
brain.dbnunca é commitado; é reconstruído em cada máquina.
8. Melhorias que eu gostaria de fazer (sobre o quack-brain)
- Staleness decay: penalizar no ranking entradas com
last_verifiedantigo; lembretes de reverificação. O conhecimento que envelhece se degrada sozinho. - Grafo código ↔ conhecimento: indexar os breadcrumbs
// Brain: {slug}como backlinks. "Que código depende deste gotcha?" e vice-versa. - Dedup semântico: ao salvar, detectar entradas quase-duplicadas (cosine > limiar) e propor merge. Evita que o brain se encha de gotchas repetidos.
- Auto-tagging / classificação via LLM ao salvar (type + tags consistentes).
- Query rewriting / HyDE: expandir a query antes de buscar para melhorar o recall.
- Telemetria de uso: registrar que entradas são recuperadas e se foram úteis → podar conhecimento morto (as que nunca são usadas em 6 meses).
- Rollups de diary: resumos semanais/mensais autogerados a partir dos diários diários. O changelog de alto nível se mantém sozinho.
- Eval harness: conjunto de "golden queries" para medir a qualidade do retrieval ao longo do tempo (regressões de relevância ao trocar modelo/chunking).
- Citações/provenance: toda resposta do agente referencia o slug + path da entrada que usou. Rastreabilidade.
- CLAUDE.md evergreen automatizado: validador que rejeita dados voláteis (LOC, números de linha) no CLAUDE.md, como já pede a regra 7 do quack-brain.
9. Roadmap por fases
- Fase 0 — Scaffold + retrieval híbrido completo (workspace Cargo com 4 crates:
pinky-core,pinky,pinky-mcp,pinky-hooks). Modelo de dados +storeSQLite com FTS5 e sqlite-vec +fastembed(multilingual-e5-small) + parse de frontmatter + reindex incremental + busca híbrida (BM25 + vector, RRF). Embeddings desde o dia 1, sem etapa de só-texto. - Fase 1 — MCP + hooks:
pinky-mcpcombrain_search/brain_save/brain_stats. Os 4 hooks finos (SessionStart/PreRead/PreWrite/Stop) apoiados no core/MCP. - Fase 2 — Diary + rollups: diary automático no hook Stop, rollups semanais/ mensais, breadcrumbs
// Brain:→ backlinks. - Fase 3 — Melhorias de qualidade: dedup semântico, staleness decay, telemetria de uso, rerank cross-encoder, eval harness de golden queries.
10. Escalabilidade e performance (horizonte 2 anos)
- Volume real: vários projetos × diary diário × gotchas ≈ dezenas de milhares de entradas em 2 anos. É pouco dado: o SQLite lida com milhões de linhas e a busca vetorial sobre dezenas de milhares de vetores é trivial (<10ms). O gargalo não é o volume, é a organização e o retrieval.
- Re-index incremental por
content_hash: só se re-embeda o que mudou. - Cache de embeddings em disco para não recalcular.
- Particionamento por scope/projeto para restringir buscas.
- WAL mode no SQLite para leituras concorrentes (CLI + MCP + hooks) sem bloquear.
11. Riscos e mitigações
- Drift markdown ↔ índice → o índice é descartável + reindex idempotente + hash.
- Qualidade de embeddings multilíngue → eval harness mede regressões (melhoria 8).
- Ruído de conhecimento (entra lixo) → as 4 condições de salvamento + dedup + telemetria de poda.
- Lock-in do modelo de embeddings → versionar o modelo na metadata do chunk; reindex global ao trocá-lo.