Mapa da trilha
⚙️ Setup do estúdio
CLI + repo + CLAUDE.md mínimo
📐 Contratos limpos
Provider/Channel/Memory/Tool
💬 Canal Telegram
Bot long-polling em 50 linhas
🧠 Memória
SQLite FTS5 vs vector vs MD
🔧 Tools que dão dinheiro
Shell, web, calendar, JSON Schema
📦 Empacotar e operar
launchd, logs, Fernet secrets
Conteúdo detalhado
⚙️ Setup do estúdio
Claude Code + Codex CLI + repo + CLAUDE.md mínimo. Em 1 hora você tem ambiente pronto.
npm install -g @anthropic-ai/claude-code + codex-cli com OAuth pra usar plano em vez de API.
CLI agêntica é a forma humana de operar. Sem ela você fica preso ao chat web.
OAuth headless via VPS, ~/.claude e ~/.codex como home dos perfis.
Layout cebola do intelecto: src/ (código), doc/ (specs), .claude/skills (skills locais), tests/ (pytest).
Decisões de pasta no dia 1 evitam refatoração no dia 90.
Separar contratos (base.py) de implementações; specs antes de código.
Arquivo que define identidade, comandos, escopo. Começa enxuto e cresce sob demanda.
CLAUDE.md gigante = sinal de medo. Mínimo viável força clareza.
Project Overview, Commands, Interface Contracts, Identity Stack.
Script bash que pergunta tokens (Telegram, OpenRouter) e grava encriptado em ~/.app/.secrets.
Onboard de 60s vs 30min de README. Mais provável de ser usado.
Validação inline, criptografia Fernet, idempotência.
Esqueleto com pytest + pytest-asyncio + teste smoke que importa o pacote sem quebrar.
Sem testes desde dia 1, ninguém adiciona depois. Ritual obrigatório.
pyproject.toml com pip install -e ., pytest-asyncio mode=auto.
Script que chama OpenRouter (147 modelos via 1 chave) e imprime resposta. Sem framework.
Provar que tudo conecta antes de adicionar complexidade.
httpx async, gestão de erros, timeout default.
📐 Contratos limpos (lição do intelecto)
Provider, Channel, Memory, Tool — 4 abstrações que tornam o agente trocável por dentro.
Toda dependência externa (LLM, canal, DB) entra atrás de uma classe-base abstrata.
Quando vier Claude 5 ou GPT-6, troca 1 arquivo, não a aplicação.
Dependency Inversion, ABC do Python, dataclass para DTOs.
Interface que normaliza chamadas LLM. Implementações: OpenRouterProvider, OllamaProvider, AnthropicProvider.
Test com mock fica trivial. Comparar modelos vira A/B em 1 flag.
LLMResponse dataclass, tool_calls, finish_reason normalizado.
Abstração de canal entrada/saída. Telegram, Slack, Discord, WhatsApp viram irmãos.
Adicionar canal novo = 1 classe + registro em config.
IncomingMessage/OutgoingMessage, callback assíncrono, idempotência.
Store com 4 operações. Implementações: SQLiteFTS5Store, PineconeStore, MarkdownStore.
Memória diferencia chat de agente. Sem contrato, vira bagunça.
MemoryEntry com categoria, forget explícito (LGPD), recent paginated.
Classe com metadata JSON Schema + execute async. LLM seleciona qual chamar.
Tool design é 70% do sucesso. Vale módulo próprio (2.5).
ToolResult com error vs output, descrição que vira system prompt parcial.
Loop que recebe message, chama provider com tools, executa tools, salva memory, responde via channel.
É onde os contratos provam que valem. 50 linhas, qualquer um lê.
Multi-turn tool calling, fail-soft em tool error, audit log inline.
💬 Canal Telegram em 50 linhas
Bot long-polling completo. Token → primeira mensagem em produção em <30 min.
Conversa com bot oficial Telegram pra criar bot e receber token.
60s e desbloqueia distribuição pra qualquer humano com celular.
Token vai no Fernet, /setdescription melhora UX.
Long-polling: cliente pergunta "tem mensagem?". Webhook: Telegram bate no seu servidor.
Long-polling roda em qualquer máquina sem IP público.
getUpdates com offset, timeout 30s, idempotência com update_id.
Classe que implementa contrato Channel: start() loop, send() faz sendMessage.
Receita pronta pra produção que você copia e cola.
httpx.AsyncClient, retry com backoff, chunking de mensagens longas.
Telegram aceita MarkdownV2 com escaping próprio. Negrito, italic, code.
Resposta LLM com markdown fica feia sem parse_mode.
Escaping de caracteres reservados, fallback pra plaintext se falhar.
Filtro de chat_id pra evitar que qualquer um descubra seu bot e gaste seu token.
Sem isso, em 1 semana algum scraper acha e te quebra a fatura.
Mensagem default pra negado, /start não-autenticado, audit log.
Recebe voice, baixa, transcreve com Whisper, processa texto.
Voz triplica adoção em mobile. Pessoas falam mais que digitam.
getFile + download, faster-whisper local, fallback pra Whisper API.
🧠 Memória: SQLite FTS5 vs vector vs Markdown
Quando usar cada um. O intelecto escolheu SQLite FTS5 — entender por quê.
Armazenar fatos, preferências, decisões além da sessão. Agente lembra de você semana que vem.
Sem memória, cada conversa começa do zero. ROI cai pela metade.
Episódica vs semântica, categorias, TTL.
Tabela virtual SQLite com índice invertido. Busca BM25 sub-100ms em 100k registros.
"Default certo" pra 90% dos agentes pessoais. Sem servidor, sem custo.
CREATE VIRTUAL TABLE USING fts5, MATCH operator, snippet().
Embeddings em alta dimensão + busca por similaridade cosseno.
FTS5 falha quando vocabulário do user difere do guardado. Vector resolve.
text-embedding-3-small $0.02/M tokens, Chroma local gratuito.
Memórias como .md em pastas. Frontmatter (name, type, description).
Humano lê, git versiona, grep funciona. Ótimo pra memórias durar.
Index MEMORY.md, links [[name]] entre memórias.
<1k registros = MD; 1k-100k = FTS5; >100k ou semântica importa = vector.
Evita over-engineering e under.
Híbrido FTS+vector é melhor pra produção mas adia até precisar.
Operação forget(id) que apaga completamente. Comando /esqueça no Telegram.
LGPD exige. E user precisa confiar pra contar coisas pessoais.
Hard delete vs soft, audit do que foi esquecido, cascade em embeddings.
🔧 Tools que dão dinheiro
Shell, web fetch, calendar, file. Como descrever no JSON Schema sem o modelo errar.
Classe Python com 3 atributos + método execute. JSON Schema vira contrato com LLM.
Descrição mal escrita = LLM nunca chama ou chama errado.
name verbo, description com exemplo, parameters required estrito.
Executa comandos shell com whitelist, timeout 30s, cwd fixo.
Shell é a tool mais poderosa. E a mais perigosa. Sandbox obrigatório.
subprocess.run, captura stderr separado, nunca shell=True com input do user.
httpx.get + html2text. LLM vê markdown limpo.
Web access transforma agente reactive em proactive.
User-Agent realista, cap em 10k chars, headers customizáveis.
list_events(day), create_event(title, start, end, attendees). OAuth Google.
"Marca reunião com X amanhã 10h" é o tipo mais pedido. WOW garantido.
google-auth-oauthlib, refresh token persistente, timezone explícito.
Read/write/list em pasta dedicada. Sem ../ nem absolutos.
Agente que escreve no FS pode evoluir o próprio contexto.
pathlib.resolve() + is_relative_to(), limit em tamanho de write.
Description começa com verbo, 1 exemplo, lista quando NÃO usar. Types específicos.
Descrição boa vs ruim = 90% vs 30% de uso correto.
"any" é proibido, enums quando possível, parameters <7.
📦 Empacotar e operar
launchd/systemd/cron, logs auditáveis, Fernet secrets. De script solto a serviço.
XML em ~/Library/LaunchAgents + launchctl load. Roda em background, sobrevive reboot.
macOS-native, sem dependência externa. Padrão do intelecto.
KeepAlive=true, StandardOutPath pra log, EnvironmentVariables.
~/.config/systemd/user/app.service + systemctl --user enable.
VPS é Linux. Saber systemd = saber operar em produção barata.
Restart=always, journalctl --user -u app, RestartSec=10.
cryptography.fernet.Fernet, chave derivada de senha + salt. ~/.app/.secrets opaco.
Tokens em texto puro é receita de incidente. Fernet resolve em 10 linhas.
Fernet.generate_key(), TokenBox wrapper, fail-loud se chave perdida.
~/.app/logs/app-YYYYMMDD.log com 1 JSON por linha. event, user_id, latency_ms, model.
Texto livre não dá pra agregar. JSON deixa jq/Loki/Grafana funcionar.
structlog ou loguru, audit.log separado de app.log, redact de PII.
Job que roda 3h consolidando memórias, gerando insights, limpando logs antigos.
Agente que limpa atrás de si dura. Sem dream cycle, o disco explode.
launchd com StartCalendarInterval, idempotência, lock pra não rodar 2x.
Comando que reporta uptime, custo do dia, último erro. /killall para tudo.
Quando algo vai errado, você precisa de botão de pânico.
/status sempre acessível mesmo se bot travou, /killall via launchctl stop.