Pitch: Experimento: um classificador de e-mails que quase não precisa chamar LLM
O problema
Nos últimos meses tenho experimentado arquiteturas de IA que evitam chamar LLMs sempre que possível. Hoje é muito comum ver soluções seguindo o padrão:
Input → LLM → Resultado
Funciona, mas em muitos casos parece exagero. Principalmente para tarefas como classificação, onde muitos exemplos são previsíveis. Por exemplo, em e-mails, geralmente temos esses assuntos:
- faturas
- notificações automáticas
- newsletters
- confirmações de reunião
Esses casos geralmente são fáceis de identificar sem precisar de um modelo grande.
Então resolvi testar uma arquitetura diferente.
A ideia: um pipeline em camadas
Em vez de usar um LLM para tudo, a ideia foi criar um pipeline onde cada camada tenta classificar o e-mail antes de passar para a próxima. O pipeline ficou assim:
hash cache → heuristics → embeddings → LLM fallback
A lógica é simples:
- começar com métodos baratos e rápidos
- escalar para métodos mais complexos apenas quando necessário
Camada 1 — Hash cache
A primeira camada evita trabalho desnecessário. Quando um e-mail é processado, um hash do conteúdo é armazenado no banco. Se o mesmo e-mail aparecer novamente, o sistema simplesmente retorna a classificação já existente, evitando, assim, reprocessamento.
Camada 2 — Heurísticas
Depois vêm regras simples como:
- domínio do remetente
- padrões no assunto
- expressões regulares
As regras ficam em um arquivo YAM, por exemplo:
finance:
senders:
- nubank.com.br
- paypal.com
subject_patterns:
- fatura
- invoice
- payment
Esta camada é extremamente rápida e resolve muitos casos triviais.
Camada 3 — Embeddings
Se o e-mail não foi classificado pelas heurísticas, o sistema usa similaridade semântica. O texto do e-mail é convertido em um vetor usando sentence-transformers.
Depois esse vetor é comparado com exemplos já rotulados usando similaridade de cosseno.
Se o e-mail for semanticamente parecido com exemplos conhecidos, a classificação pode ser inferida.
Camada 4 — LLM fallback
Somente quando nenhuma das etapas anteriores consegue classificar com confiança, o sistema chama um LLM.
Estou usando modelos locais via Ollama, o que permite rodar tudo localmente.
A resposta do modelo vem em JSON para facilitar o parsing.
O resultado mais interessante
Nos testes iniciais, a maioria dos e-mails é classificada antes de chegar no LLM. Ou seja:
- heurísticas resolvem muitos casos
- embeddings resolvem boa parte do restante
- o LLM fica apenas para os casos realmente ambíguos
Isso reduz:
- custo
- latência
- dependência de modelos grandes
Estrutura do projeto
O projeto está organizado mais ou menos assim:
triage/
├── cli/ interface de linha de comando
├── core/ pipeline de classificação
├── email/ parsing e leitura de e-mails
├── embedder/ modelo de embeddings
├── llm/ integração com LLM
├── data/ persistência em SQLite
└── examples/ e-mails de exemplo
Também existe um CLI para rodar o pipeline diretamente:
triage run
Saída:
⏭ 18 email(s) already processed, skipped.
📬 3 new email(s).
[HEURISTIC] 'April invoice' → REFERENCE_ONLY (90%)
[CACHE] 'Payment due soon' → REFERENCE_ONLY (100%)
[LLM] 'Following up on...' → ACTION_REQUIRED (81%)
Motivação
Uma coisa que tenho percebido é que muitas discussões sobre IA focam apenas em modelos cada vez maiores. Mas em muitos sistemas reais, o problema não é só o modelo — é a arquitetura ao redor dele.
Esse projeto é um pequeno experimento nessa direção: tentar usar LLMs de forma mais estratégica, em vez de chamá-los o tempo todo.
Código aberto
Resolvi abrir o projeto como open source. Se alguém tiver interesse em:
- pipelines de IA
- classificação de texto
- arquiteturas híbridas (heurísticas + ML + LLM)
o repositório está aqui:
GitHub: https://github.com/stonefullstm/ai-email-triage
Também escrevi um artigo explicando melhor a ideia: https://medium.com/@stonefull.stm/stop-letting-your-inbox-manage-you-build-a-local-ai-email-triage-cli-in-python-4b89379610fd
Se alguém tiver sugestões ou críticas sobre a arquitetura, fico feliz em discutir.