MÓDULO 5.3

🔗 Integrando Vault Obsidian com Intelecto

Da teoria à prática: USER.md com contexto do vault, script de bridge, ingestão pelo Telegram, consulta híbrida e sincronização automática com watcher.

6
Tópicos
50
Minutos
Prático
Nível
Build
Tipo
1

📄 Configurando USER.md com o Contexto do Vault

A integração mais simples e imediata: sem código. Adicione ao USER.md um catálogo do vault e o Intelecto passará a ter consciência do seu segundo cérebro em todas as conversas.

USER.md sem vault (antes)
# Contexto do Usuário

Nome: João Silva
Profissão: Engenheiro de Software
Linguagem favorita: Python
Preferências:
- Respostas diretas e curtas
- Exemplos de código práticos

Intelecto não sabe nada sobre o vault

USER.md com vault (depois)
# Contexto do Usuário

Nome: João Silva
Profissão: Engenheiro de Software

## Segundo Cérebro (Obsidian)
Vault: ~/vault | Páginas: ~150

### Domínios principais
- Python, FastAPI, SQLite
- LLMs e Agentes de IA
- Gestão de Conhecimento

### Projetos ativos
- api-gateway (produção)
- intelecto-bridge (dev)

### Conceitos-chave no vault
[[bm25]], [[fts5]], [[rag-vs-wiki]]
[[agente-ingestao]], [[index-estrutura]]

Ao mencionar tópicos do vault,
referencie as páginas relevantes.

Intelecto sabe do vault e sugere páginas

💡 Resultado imediato

Com o USER.md atualizado, o Intelecto começa a responder: "Você tem uma página sobre BM25 no seu vault — quer que eu expanda a comparação com embeddings para adicionar lá?". Zero código, zero infraestrutura. Apenas edição de Markdown.

2

🔧 Script de Bridge

O bridge.py é o componente central da integração: lê o vault e injeta conhecimento compilado no SQLite do Intelecto. Cerca de 80 linhas Python que fecham o loop entre as duas memórias.

bridge.py — Script completo
#!/usr/bin/env python3
"""
bridge.py — Sincroniza vault Obsidian com memória SQLite do Intelecto
Uso: python bridge.py [--incremental]
"""
import sqlite3, pathlib, re, hashlib, sys
from datetime import datetime

VAULT_DIR = pathlib.Path.home() / "vault"
WIKI_DIR = VAULT_DIR / "wiki"
DB_PATH = pathlib.Path.home() / ".intelecto" / "intelecto.db"
SYNC_LOG = VAULT_DIR / ".last_sync"

def get_db():
    conn = sqlite3.connect(DB_PATH)
    # Garante que a tabela existe
    conn.execute("""
        CREATE VIRTUAL TABLE IF NOT EXISTS memories
        USING fts5(content, source UNINDEXED, page_id UNINDEXED)
    """)
    return conn

def parse_index(index_path: pathlib.Path) -> list[dict]:
    """Extrai entradas do index.md: [[page]] — resumo"""
    entries = []
    pattern = re.compile(r'\[\[([^\]]+)\]\].*?—\s*(.+)$', re.MULTILINE)
    text = index_path.read_text(encoding='utf-8')
    for match in pattern.finditer(text):
        page_name, summary = match.groups()
        entries.append({'page': page_name.strip(), 'summary': summary.strip()})
    return entries

def sync_page(conn, page_name: str, summary: str):
    """Lê a página wiki e insere no SQLite como memória estruturada"""
    page_file = WIKI_DIR / f"{page_name}.md"
    if not page_file.exists():
        return

    content = page_file.read_text(encoding='utf-8')
    # Extrai apenas o corpo (sem frontmatter YAML)
    body = re.sub(r'^---.*?---\n', '', content, flags=re.DOTALL)[:600]

    page_id = hashlib.md5(page_name.encode()).hexdigest()[:8]
    memory_text = f"[VAULT:{page_name}] {summary}\n{body}"

    # Remove entrada anterior desta página se existir
    conn.execute("DELETE FROM memories WHERE page_id = ?", [page_id])
    conn.execute(
        "INSERT INTO memories(content, source, page_id) VALUES (?, 'vault', ?)",
        [memory_text, page_id]
    )

def main():
    incremental = '--incremental' in sys.argv
    conn = get_db()
    index_path = WIKI_DIR / "index.md"

    if not index_path.exists():
        print("index.md não encontrado. Configure o vault primeiro.")
        return

    entries = parse_index(index_path)
    synced = 0

    for entry in entries:
        page_file = WIKI_DIR / f"{entry['page']}.md"
        if incremental and SYNC_LOG.exists():
            last_sync = SYNC_LOG.stat().st_mtime
            if page_file.exists() and page_file.stat().st_mtime < last_sync:
                continue  # Não modificada desde último sync
        sync_page(conn, entry['page'], entry['summary'])
        synced += 1

    conn.commit()
    SYNC_LOG.write_text(datetime.now().isoformat())
    print(f"Sincronizadas {synced} páginas do vault → SQLite Intelecto")

if __name__ == "__main__":
    main()
Execução manual
python bridge.py

Sincroniza todas as páginas

Incremental
python bridge.py \
  --incremental

Só páginas modificadas

Cron automático
# crontab -e
0 */6 * * * python \
  ~/bridge.py --incremental

A cada 6 horas

3

📥 Ingestão pelo Telegram

O Telegram como interface de captura torna a ingestão tão natural quanto enviar uma mensagem. O fluxo completo: Telegram → raw/ → LLM compila → wiki/ atualizado.

📥 Fluxo de Ingestão via Telegram

📱 Telegram /ingere [URL] ou texto livre 1. Usuário 🧬 Handler Intelecto processa URL → texto salva em raw/ 2. Captura 📂 raw/ fonte-2026-01-15.md + metadados 3. Arquivo raw 🤖 LLM Compila raw/ → página wiki/ → atualiza index 4. Compilação 📖 wiki/ nova página compilada 5. Conhecimento
Handler /ingere no Intelecto (adicionar ao main.py)
from telegram import Update
from telegram.ext import CommandHandler, ContextTypes
import httpx, pathlib, datetime

VAULT_RAW = pathlib.Path.home() / "vault" / "raw"

async def ingere_handler(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
    """Handler para /ingere [URL ou texto]"""
    args = ' '.join(ctx.args) if ctx.args else ''
    if not args:
        await update.message.reply_text("Uso: /ingere [URL ou texto a ingerir]")
        return

    # Se for URL, busca o conteúdo
    if args.startswith('http'):
        await update.message.reply_text("⏳ Buscando conteúdo da URL...")
        async with httpx.AsyncClient() as client:
            resp = await client.get(args, timeout=15)
            content = f"URL: {args}\n\n{resp.text[:5000]}"
    else:
        content = args

    # Salva em raw/
    timestamp = datetime.datetime.now().strftime('%Y-%m-%d-%H%M')
    filename = VAULT_RAW / f"raw-{timestamp}.md"
    filename.write_text(f"---\norigem: telegram\ndata: {timestamp}\n---\n\n{content}")

    await update.message.reply_text(
        f"✅ Salvo em raw/raw-{timestamp}.md\n"
        f"Execute o pipeline de ingestão para compilar no wiki."
    )

# Registrar o handler no Application
app.add_handler(CommandHandler("ingere", ingere_handler))
4

🔀 Consulta Híbrida

Na consulta híbrida, o Intelecto combina contexto de conversa (SQLite BM25) com conhecimento estruturado (página wiki) antes de gerar cada resposta, produzindo sínteses muito mais ricas.

Sem consulta híbrida
Usuário:

Como funciona o BM25 do Intelecto?

Intelecto (só SQLite):

BM25 é um algoritmo de ranking para busca de texto. Usa TF-IDF com normalização de comprimento. O Intelecto usa via SQLite FTS5.

Resposta genérica, sem o detalhe do seu vault

Com consulta híbrida
Usuário:

Como funciona o BM25 do Intelecto?

Intelecto (SQLite + wiki[[bm25]]):

Conforme sua página [[bm25]] no vault: o Intelecto usa k1=1.2 e b=0.75 por padrão. Para suas memórias curtas (~100 tokens), você documentou que reduzir k1 para 0.9 melhora o recall em 23%. Quer atualizar a config?

Resposta com dados específicos do seu vault

Lógica de consulta híbrida (adicionar ao handler de mensagens)
import re, pathlib, sqlite3

WIKI_DIR = pathlib.Path.home() / "vault" / "wiki"
WIKI_PATTERN = re.compile(r'\[\[([^\]]+)\]\]')

def find_wiki_page(query: str, db_path: str) -> str | None:
    """Busca no SQLite e retorna página wiki relevante se encontrada"""
    conn = sqlite3.connect(db_path)
    # Busca memórias do vault (source='vault') relevantes à query
    rows = conn.execute(
        "SELECT content FROM memories WHERE memories MATCH ? "
        "AND source = 'vault' ORDER BY bm25(memories) LIMIT 1",
        [query]
    ).fetchall()
    if not rows:
        return None
    # Extrai nome da página do padrão [VAULT:page_name]
    match = re.search(r'\[VAULT:([^\]]+)\]', rows[0][0])
    if not match:
        return None
    page_name = match.group(1)
    page_file = WIKI_DIR / f"{page_name}.md"
    if page_file.exists():
        return page_file.read_text()[:1500]  # Primeiros 1500 chars
    return None

# No handler de mensagens, antes de chamar o LLM:
wiki_context = find_wiki_page(user_message, DB_PATH)
if wiki_context:
    system_extra = f"\n\n## Conhecimento do vault do usuário:\n{wiki_context}"
    # Adicionar ao system prompt desta chamada
5

🔄 Sincronização Automática

Com um watcher Python monitorando o vault, cada nova página wiki dispara automaticamente a sincronização com o SQLite do Intelecto — sem cron, sem intervenção manual.

watcher.py — Monitora mudanças no vault
#!/usr/bin/env python3
"""
watcher.py — Monitora wiki/ e sincroniza com SQLite automaticamente
Dependência: pip install watchdog
"""
import time, subprocess, pathlib
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

WIKI_DIR = pathlib.Path.home() / "vault" / "wiki"
BRIDGE_SCRIPT = pathlib.Path.home() / "bridge.py"
DEBOUNCE_SECONDS = 5  # Evita sync duplo em saves rápidos

class WikiChangeHandler(FileSystemEventHandler):
    def __init__(self):
        self._last_sync = 0

    def on_modified(self, event):
        if event.is_directory or not event.src_path.endswith('.md'):
            return
        now = time.time()
        if now - self._last_sync < DEBOUNCE_SECONDS:
            return  # Debounce
        self._last_sync = now
        page = pathlib.Path(event.src_path).stem
        print(f"Mudança detectada: {page} — sincronizando...")
        subprocess.run(
            ["python", str(BRIDGE_SCRIPT), "--incremental"],
            capture_output=True
        )
        print(f"Sync concluído para {page}")

if __name__ == "__main__":
    observer = Observer()
    observer.schedule(WikiChangeHandler(), str(WIKI_DIR), recursive=False)
    observer.start()
    print(f"Monitorando {WIKI_DIR}...")
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()
Executar o watcher
# Em background
nohup python watcher.py > \
  ~/.intelecto/watcher.log 2>&1 &

# Como serviço systemd (recomendado)
# Ver arquivo intelecto-watcher.service
Latência de sincronização
  • • Mudança no vault detectada: <1s
  • • Debounce (evita duplicatas): 5s
  • • Execução do bridge.py: <3s
  • • Total: <10s do save ao SQLite
6

🎯 Exemplo Completo — Ponta a Ponta

Veja o sistema completo em ação: da leitura de um artigo à consulta de conhecimento pelo Telegram — o ciclo completo do segundo cérebro integrado.

1

Captura via Telegram

Você no Telegram:

/ingere https://arxiv.org/abs/2024.12345 — artigo sobre otimização de BM25 para textos curtos

Intelecto:

✅ Salvo em raw/raw-2026-01-15-1430.md com metadados. Execute /compilar para adicionar ao wiki.

2

Compilação pelo LLM

O pipeline de ingestão (Trilha 1) lê raw/raw-2026-01-15-1430.md e gera:

# wiki/bm25-short-text-optimization.md
---
tipo: pesquisa
relacionados: [[bm25]], [[intelecto-memoria]]
data: 2026-01-15
---
# BM25 para Textos Curtos — Otimizações
## Problema
BM25 padrão (k1=1.2, b=0.75) foi calibrado
para documentos longos. Para memórias curtas (<150 tokens)...
## Solução proposta
Reduzir k1 para 0.8-0.9 aumenta recall em 18-25%...
3

Watcher sincroniza automaticamente

# No terminal do watcher:
Mudança detectada: bm25-short-text-optimization — sincronizando...
Sync concluído para bm25-short-text-optimization
# SQLite agora tem a nova memória com source='vault'
4

Consulta enriquecida pelo Telegram

Você (3 dias depois):

Devo mudar os parâmetros do BM25 no Intelecto?

Intelecto (com consulta híbrida):

Sim! Conforme sua pesquisa em [[bm25-short-text-optimization]] (15/jan): para as memórias curtas do Intelecto (~100 tokens), reduzir k1 de 1.2 para 0.9 melhora o recall em ~23%. Quer que eu mostre como alterar no código?

Resumo do Módulo 5.3

USER.md com vault — integração zero-code que dá consciência do segundo cérebro ao Intelecto
bridge.py — 80 linhas que sincronizam wiki → SQLite com suporte incremental
Handler /ingere — captura URLs e texto pelo Telegram, salva em raw/ para ingestão
Consulta híbrida — SQLite BM25 + página wiki = respostas com conhecimento específico do seu vault
watcher.py — sincronização automática com debounce, latência <10s do save ao SQLite

Próximo Módulo:

5.4 — Telegram como Interface do Segundo Cérebro: design de captura, comandos customizados, arquivos multi-modal e notificações proativas.