Eu estava cansado dos resultados inconsistentes que obtinha com sistemas RAG tradicionais. A combinação de GPT-3.5, bibliotecas Python e vetores "mágicos" simplesmente não entregava a confiabilidade que eu precisava para projetos reais. Foi quando decidi abandonar as soluções prontas e construir meu próprio banco vetorial do zero — em Rust.
Por Que Abandonar o Ecossistema Python?
Programo há mais de 20 anos e, como muitos, comecei minha jornada em IA com o stack Python padrão. Funcionava... até certo ponto. Mas quando precisei de previsibilidade de latência, controle fino de memória e binários enxutos para produção, especialmente em ambientes edge e on-prem, percebi que precisava de algo diferente.
Rust oferecia exatamente o que eu buscava: performance consistente sem as surpresas do garbage collector e a capacidade de criar binários autônticos. E aqui está algo que pode surpreender você: mesmo sem saber Rust no início, consegui desenvolver uma solução que competia de igual para igual com as opções existentes — tudo gerado por IA em menos de 30 dias, sem uma linha de código manual.
A Descoberta que Mudou Tudo: Simplicidade vs Complexidade
Testei praticamente todo tipo de embedding disponível: desde TF-IDF e BM25 até modelos mais complexos como BERT, SVD, MiniLM e várias implementações ONNX. Avaliei sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2, intfloat/multilingual-e5-small, Alibaba-NLP/gte-multilingual-base e até LaBSE.
E a conclusão foi surpreendentemente simples: a melhoria marginal que esses embeddings complexos ofereciam não justificava a complexidade operacional adicional. Voltei ao BM25 — sim, aquele método clássico — e implementei uma versão nativa em Rust. A combinação com HNSW para indexação e quantização SQ-8 para economia de memória criou um pipeline que era tanto eficiente quanto previsível.
Performance que Impressiona
Os benchmarks públicos mostram números que falam por si mesmos: 4.6 mil operações de inserção por segundo e 2.3 mil atualizações por segundo. A quantização SQ-8 reduziu o uso de memória em aproximadamente 4 vezes com perda mínima de qualidade. E a partir de 10 mil vetores por coleção, o sistema multi-index mantém a performance consistente.
Mas os números são apenas parte da história. O que realmente importa é como isso se traduz na experiência prática. E aqui a diferença foi notável: buscas precisas, latência estável e setup sem dependências externas complicadas.
A Fórmula que Funcionou na Prática
Após todos os testes, cheguei a uma combinação que se mostrou extremamente eficaz:
HNSW como índice principal pela sua velocidade e consistência
BM25 com 512 dimensões para representação simples e previsível
Quantização SQ-8 para economia de memória
Chunking disciplinado entre 200-500 tokens
Essa combinação, empacotada como MCP (Model Context Protocol), começou a entregar resultados cirúrgicos nas buscas dentro do Cursor — inclusive com modelos locais como LM Studio e GPT-OSS-20B retornando os trechos corretos de projetos vetorizados.
UX Acima de Tudo
O que diferencia o Vectorizer não é apenas a tecnologia, mas a experiência do usuário. O auto-mapeamento de dados locais de projetos de software, collections segmentadas e a capacidade de migrar facilmente para multi-index quando necessário criam uma fluidez que simplesmente não encontrei em outras soluções.
E talvez o mais importante: setup zero dependência externa. Sem precisar trazer "seu modelo x, y, z" — tudo funciona de forma integrada e opinionada.
Transmutation: O Irmão que Faltava
Em 13 de outubro, adicionei o Transmutation — desenvolvido literalmente em um dia com ajuda do Cursor e Vectorizer. Ele converte PDF, DOCX, XLSX, HTML e outros formatos para Markdown consistente, incluindo OCR com Tesseract e STT com Whisper para áudio e vídeo. A integração direta com o Vectorizer cria um pipeline completo: ingestão → chunking → index.
E agora estamos olhando para o futuro: embeddings de imagem para coleções "vision", suporte nativo para CUDA/Metal/Vulkan, e um projeto em Rust para manipulação direta de VRAM com shaders. Minhas contas indicam capacidade para aproximadamente 11 bilhões de tokens em uma máquina comum com 32GB de RAM — sem GPU e sem APIs externas.
O Vectorizer já está puxando desenvolvimento em várias frentes: Transmutation, GPU nativa, vision embeddings. E cada novo componente reforça a mesma lição: às vezes, a solução mais eficaz não é a mais complexa, mas a mais bem executada.
O Desafio dos Embeddings Multilingues
Uma das maiores surpresas durante o desenvolvimento foi lidar com a complexidade dos embeddings multilingues. Você já parou para pensar por que tantas soluções prometem suporte a múltiplos idiomas, mas na prática entregam resultados inconsistentes? Descobri que muitos modelos simplesmente não foram treinados adequadamente para lidar com as nuances do português brasileiro — especialmente quando se trata de gírias, expressões regionais e contextos específicos.
Na minha experiência, testar com documentos técnicos em português versus inglês revelou diferenças significativas na qualidade dos resultados. O modelo paraphrase-multilingual-MiniLM-L12-v2, por exemplo, apresentava performance 15-20% inferior em português quando comparado ao inglês para consultas técnicas específicas. E isso não era um problema isolado — praticamente todos os modelos multilingues que testei sofriam dessa mesma limitação.
Por Que o BM25 Funciona Tão Bem em Rust?
A implementação do BM25 em Rust revelou algumas vantagens que eu não antecipava. A primeira foi a velocidade pura — conseguimos processar consultas em menos de 2ms mesmo com coleções contendo centenas de milhares de documentos. Mas mais importante que a velocidade foi a consistência. Sem a sobrecarga do garbage collector e com alocação de memória previsível, o sistema mantinha a mesma latência independente da carga de trabalho.
E aqui está algo que poucos discutem: a simplicidade do BM25 torna o debugging infinitamente mais fácil. Quando você está lidando com embeddings complexos de 768 ou 1024 dimensões, identificar por que uma consulta retornou resultados ruins é como procurar uma agulha em um palheiro. Com o BM25, você pode literalmente rastrear o scoring termo a termo e entender exatamente o que está acontecendo.
Implementamos algumas otimizações específicas que fizeram toda a diferença:
Cache inteligente de termos frequentes que evita recálculos desnecessários
Pré-processamento de documentos com normalização customizada para português
Suporte a consultas booleanas combinadas com scoring BM25
Indexação incremental que não requer reindexação completa
Os Segredos do Chunking Eficiente
Se tem uma coisa que aprendi da maneira mais difícil, é que o chunking pode fazer ou quebrar qualquer sistema de busca vetorial. E não, não é só sobre o tamanho dos chunks. A qualidade da quebra é tão importante quanto o tamanho. Implementamos um sistema híbrido que combina quebra por tokens com análise semântica, preservando a integridade de parágrafos e seções lógicas.
O que descobri? Chunks entre 200-500 tokens funcionam bem para a maioria dos casos, mas o segredo está na sobreposição. Uma sobreposição de 10-15% entre chunks consecutivos melhorou significativamente a recuperação de informações que ficariam cortadas nas bordas. E para documentos técnicos, desenvolvemos heurísticas específicas que identificam e preservam blocos de código, definições de funções e documentação de APIs.
Desafios com Quantização em Produção
A quantização SQ-8 parecia a solução perfeita no papel — redução de 4x no uso de memória com perda mínima de precisão. Mas na prática, enfrentamos alguns desafios interessantes. O maior deles foi o chamado "degradação silenciosa" — onde a qualidade das buscas diminuía gradualmente sem que os benchmarks tradicionais detectassem.
Desenvolvemos um sistema de monitoramento contínuo que compara resultados quantizados versus não quantizados em uma amostra de consultas reais. Isso nos permitiu identificar padrões específicos onde a quantização causava mais impacto — geralmente em consultas com múltiplos termos de baixa frequência. A solução? Implementamos quantização adaptativa que aplica níveis diferentes de compressão baseado nas características de cada coleção.
Integração com MCP: Mais que uma Simples Interface
Quando decidimos integrar com Model Context Protocol (MCP), imaginávamos que seria apenas mais uma interface. Mas rapidamente percebemos que estávamos subestimando o potencial. O MCP nos permitiu criar uma experiência verdadeiramente unificada onde o Vectorizer podia ser acessado de forma consistente através do Cursor, VS Code, ou qualquer outra ferramenta compatível.
E aqui está a parte mais interessante: a integração revelou padrões de uso que nunca teríamos descoberto de outra forma. Os desenvolvedores estavam usando o sistema para muito mais que busca de código — documentação, decisões de arquitetura, até mesmo para recuperar conversas técnicas antigas. Isso nos levou a expandir o suporte para diferentes tipos de conteúdo e metadados.
Algumas das funcionalidades que emergiram dessa integração:
Busca contextual baseada no projeto atual
Suporte a consultas híbridas (texto + metadados)
Cache inteligente de consultas frequentes
Integração direta com sistemas de versionamento
Lições sobre Desenvolvimento Assistido por IA
Desenvolver o Vectorizer inteiramente com assistência de IA em Rust — uma linguagem que eu não dominava — foi uma experiência que mudou minha perspectiva sobre desenvolvimento de software. Aprendi que a IA não substitui o desenvolvedor, mas transforma radicalmente o processo. Em vez de escrever código linha por linha, você se torna um arquiteto que guia, revisa e integra.
O processo que funcionou para mim envolvia ciclos iterativos curtos: prompt → implementação → teste → refinamento. E o mais surpreendente? A qualidade do código gerado frequentemente superava minhas expectativas. O Rust Analyzer, combinado com os modelos mais recentes, conseguia produzir código idiomático e eficiente que seguia as melhores práticas da linguagem.
Mas nem tudo foram flores. Aprendi da pior maneira que a IA tem limitações significativas quando se trata de:
Otimizações de baixo nível que requerem entendimento profundo da arquitetura
Design de APIs complexas que precisam evoluir consistentemente
Decisões de arquitetura que impactam múltiplos componentes
Debugging de problemas de concorrência e race conditions
O Futuro Imediato: Visão e Processamento Multimodal
Atualmente, estamos explorando embeddings de imagem para criar coleções "vision" — uma extensão natural do Vectorizer que permitirá busca por similaridade visual além de textual. O desafio aqui é criar uma representação unificada que combine características visuais e textuais de forma coerente.
Os primeiros experimentos com modelos como CLIP e OpenCLIP mostraram potencial, mas também revelaram a complexidade adicional de lidar com dados multimodais. Como comparar a similaridade entre uma imagem e um texto? Como rankear resultados que combinam diferentes modalidades? São questões que estamos abordando com uma abordagem prática: começando com casos de uso específicos e expandindo gradualmente.
E enquanto isso, o trabalho na aceleração GPU continua. A implementação de shaders diretos para manipulação de VRAM está mostrando ganhos promissores — até 8x de aceleração em operações de embedding em hardware compatível. Mas o mais interessante é que estamos descobrindo que muitas operações podem ser otimizadas significativamente mesmo sem GPU, através de SIMD e paralelização cuidadosa.
Com informações do: andreferreira.com.br