Construindo uma base de conhecimento local-first com SQLite FTS5 e Markdown
O problema com bases de conhecimento na nuvem
Em 2024, o Omnivore encerrou as atividades. O serviço tinha 40.000 estrelas no GitHub. Sessenta dias depois de anunciar a descontinuação, desapareceu - e os usuários que perderam a janela de exportação de 60 dias perderam cada favorito, destaque e nota.
Isso não foi uma anomalia. Foi o resultado inevitável das ferramentas de conhecimento dependentes de nuvem:
| Risco | KB em nuvem | KB local (Geneziz) |
|---|---|---|
| Empresa vai à falência | Todos os dados perdidos | Você ainda tem tudo |
| Preço aumenta 10x | Pague ou perca | Compra única |
| API descontinuada | Integrações quebradas | Seus dados, suas regras |
| Vazamento de dados | Suas notas expostas | Arquivos na sua máquina |
| Internet necessária | Offline = sem acesso | Funciona sem rede |
Para desenvolvedores que curam centenas de favoritos ao longo de anos, a longevidade dos dados importa mais do que recursos sofisticados. A arquitetura do Geneziz é construída em torno deste princípio.
A camada de armazenamento: Markdown + YAML frontmatter
Cada item na sua base de conhecimento do Geneziz é um arquivo Markdown simples com um cabeçalho YAML frontmatter:
---
title: React Performance Optimization Techniques
type: tool
date_added: 2026-05-15
source: https://dev.to/react-perf
tags:
- react
- performance
- frontend
stars: 1420
via: @dan_abramov
---
# React Performance Optimization Techniques
## Content here...
Por que Markdown?
Interoperabilidade. Markdown é o formato de marcação mais amplamente suportado do planeta. Todo editor, IDE e visualizador consegue lê-lo. Se o Geneziz um dia desaparecer, seus arquivos de conhecimento continuam legíveis - sem lock-in de formato proprietário.
Amigável a Git. Diffs de Markdown são legíveis por humanos. Quando você edita a descrição de uma ferramenta ou adiciona tags, o git diff mostra o que mudou, não um blob JSON.
Amigável a IA. LLMs são treinados em mais Markdown do que qualquer outro formato, exceto código. Alimentar um assistente de IA com seu conhecimento é trivial quando ele já está em texto estruturado.
O esquema de frontmatter
O bloco YAML entre os delimitadores --- carrega metadados estruturados que o Geneziz usa para ordenação, filtragem e exibição:
interface ViewerIndexEntry {
file: string; // path relative to knowledge/
path: string; // URL slug
title: string; // display name
type: 'tool' | 'article';
date_added: string; // ISO date
source: string; // original URL
tags: string[]; // searchable tags
stars: number; // GitHub star count (for tools)
via: string; // who shared it
// Enrichment fields (computed at index time):
display_title: string;
category: string;
priority: number;
rating: number;
}
Este esquema é deliberadamente plano - sem objetos aninhados, sem arrays de tipos complexos. Isso torna os dados fáceis de consultar, fáceis de migrar e fáceis de inspecionar com um editor de texto.
A camada de busca: SQLite FTS5
A busca de texto completo é onde a maioria das bases de conhecimento falha. Ou não a têm de jeito nenhum (grep pelos arquivos), ou usam um serviço externo (Elasticsearch, Meilisearch, Algolia) que exige um servidor em execução.
O Geneziz usa SQLite FTS5 - o mesmo motor de banco de dados que roda em todo iPhone e dispositivo Android. Eis o porquê:
O que é o FTS5?
O FTS5 (Full-Text Search 5) é a extensão de busca embutida no SQLite. Ele cria um índice invertido tokenizado do seu conteúdo, viabilizando:
- Busca instantânea por prefixo/frase ("react perf" corresponde a "React Performance")
- Operadores booleanos (AND, OR, NOT)
- Ranking de relevância com pontuação estilo BM25
- Resultados quase instantâneos mesmo com mais de 100 mil documentos
Como o Geneziz constrói o índice
Quando você roda geneziz index, é isso que acontece por baixo dos panos:
1. Scan knowledge/tools/*.md and knowledge/articles/*.md
2. Parse YAML frontmatter → structured entries
3. Enrich each entry:
- Generate display_title (cleaned, shortened)
- Classify category from tags/content
- Compute priority & rating scores
4. Write viewer-index.json (camelCase, React-ready)
5. Create .state/search.db (SQLite + FTS5 virtual table):
- CREATE VIRTUAL TABLE USING fts5(...)
- INSERT all entries with title + description + tags + content preview
O search.db resultante normalmente fica abaixo de 5MB mesmo para milhares de entradas. Ele abre instantaneamente - sem servidor para iniciar, sem atraso de indexação.
Exemplo de consulta
Quando você roda geneziz search "react hooks", o fluxo é:
# 1. Open SQLite connection to .state/search.db
# 2. SELECT * FROM fts5_main WHERE knowledge MATCH 'react hooks'
# 3. Return ranked results with highlighted snippets
# 4. Format for CLI or web display
O FTS5 cuida da tokenização, da stemização (em inglês) e do ranking automaticamente. Sem necessidade de um serviço separado de tokenizer ou de um pipeline de ajuste de relevância.
O índice do visualizador: camelCase para a web
Uma escolha de arquitetura que vale discutir: o Geneziz escreve seu índice em camelCase, não em snake_case.
{
"displayTitle": "React Perf Techniques",
"dateAdded": "2026-05-15",
"category": "frontend",
"priority": 8,
"rating": 4.7
}
Por quê? Porque o consumidor é um aplicativo web em React escrito em TypeScript/JavaScript. Manter o índice em camelCase significa dispensar qualquer camada de transformação - o frontend lê o JSON e renderiza diretamente. Sem conversão snake_to_camel, sem risco de incompatibilidade.
O backend em Python gera camelCase por meio do utilitário _to_camel_dict(). É uma escolha deliberada: moldar os dados para o consumidor, não para o produtor.
Escritas atômicas: segurança contra falhas
Os arquivos de conhecimento usam padrões de escrita atômica (escrever-em-temporário + renomear) para evitar corrupção:
def atomic_write(path: str, content: str) -> None:
tmp = path + '.tmp'
with open(tmp, 'w', encoding='utf-8') as f:
f.write(content)
os.replace(tmp, path) # Atomic on POSIX, near-atomic on Windows
Se o processo quebrar no meio da escrita, você fica com o arquivo antigo ou com o novo - nunca um arquivo parcialmente escrito e corrompido. Esse padrão é usado em todo lugar: bookmarks.md, viewer-index.json, arquivos de estado.
O fluxo de dados completo
X.com / GitHub
│
▼
geneziz fetch / geneziz sync
│
▼
Raw data → AI processing (optional)
│
▼
Markdown files + YAML frontmatter
│ (knowledge/tools/*.md)
│ (knowledge/articles/*.md)
│
▼
geneziz index
│
├──→ viewer-index.json (camelCase, enriched)
└──→ .state/search.db (SQLite FTS5)
│
▼
Web viewer (React) reads both files
│
▼
User searches, browses, reads articles
Cada etapa neste pipeline é inspecionável com um editor de texto. Você pode abrir o viewer-index.json e ver exatamente o que o aplicativo web vê. Pode abrir o search.db com qualquer navegador SQLite e rodar consultas. Pode abrir qualquer arquivo .md e ler suas notas.
Por que essa arquitetura é vantajosa para desenvolvedores
1. Zero dependências em tempo de execução
Sem cluster Elasticsearch. Sem cache Redis. Sem processo worker em segundo plano. O banco de busca é um único arquivo SQLite que abre em milissegundos. A base de conhecimento inteira funciona offline.
2. Nativo para Git
Como tudo é Markdown + JSON, sua base de conhecimento é um repositório git. Histórico de versões, branches, revisões de PR - todas as ferramentas que você já usa para código.
3. Pronta para IA
LLMs leem Markdown nativamente. O servidor MCP fornece seu conhecimento ao Claude/GPT/Cursor sem nenhuma conversão de formato. Quando você pergunta "o que eu salvei sobre Rust?", a resposta vem diretamente dos seus arquivos. Você nem precisa de um desses assistentes: o Geneziz traz sua própria inteligência local - o Geneziz AI, cerca de 1,6 GB, offline, grátis - então o pipeline pronto para IA não precisa de nenhuma nuvem, a menos que você escolha uma.
4. À prova de migração
Texto simples não apodrece. Um arquivo Markdown de 2020 funciona identicamente em 2030. Não há migração de esquema necessária, nem conflitos de versão de ORM, nem "atualizamos e agora seus dados antigos são incompatíveis".
As concessões
Essa arquitetura não é mágica. Tem limitações intencionais:
- Sem edição colaborativa - um escritor por vez (bom para KBs pessoais)
- Sem sincronização em tempo real -
geneziz fetchmanual e depoisgeneziz process(intencional, não um bug) - A busca é somente em inglês - o FTS5 não lida bem com tokenização CJK (aceitável para a maioria do público de desenvolvedores)
- Grandes volumes de dados - se você tiver mais de 100 mil entradas, o FTS5 fica mais lento (mas a busca semântica embutida lida com consultas por significado em escala)
Esses não são bugs. São restrições de design que mantêm o sistema simples, rápido e sob seu controle.
Experimente o Geneziz
Tudo o que este post descreve já vem pronto no Geneziz - armazenamento em Markdown simples, busca FTS5 instantânea, busca semântica embutida e o Geneziz AI rodando localmente. Preço único, sem assinatura.
Notas de compartilhamento
Este post faz parte da série do blog do Geneziz. Se você achou útil, compartilhe com um desenvolvedor que esteja construindo sua própria base de conhecimento.
Posts relacionados
A página de IA do Geneziz amadurece — respostas em streaming, visão e tarefas que rodam enquanto você dorme
O Geneziz transforma a base de conhecimento em um assistente vivo: as respostas chegam em streaming conforme são geradas, você pode anexar imagens às perguntas, e tarefas agendadas interrogam o seu próprio corpus no piloto automático — e depois te notificam. Tudo local-first, com o modelo que você escolher.
Agentes que lembram — memória persistente para seus clientes de IA, com Geneziz
Todo assistente de IA esquece você no momento em que a sessão termina. O Geneziz resolve isso: o ask_kb responde perguntas sobre a sua própria base de conhecimento com fontes citadas, salva a conversa e a devolve para qualquer cliente MCP — Claude, Cursor, ZCode e companhia — num servidor local-first de 34 ferramentas. É assim que a memória de agentes deveria funcionar.