Eu estava cansado dos resultados inconsistentes que obtinha com sistemas RAG tradicionais. A promessa era grande: recuperação precisa de informações usando vetores e inteligência artificial. Mas na prática, o que eu via eram respostas medianas, latência imprevisível e uma complexidade operacional que não fazia sentido para projetos reais.

Foi quando decidi abandonar as soluções prontas e construir meu próprio banco vetorial do zero. Em Rust. Sem saber Rust. Parece loucura? Talvez. Mas os resultados transformaram completamente minha abordagem para busca semântica e RAG.

Por Que Abandonar o Ecossistema Python Tradicional

O ecossistema Python para IA é impressionante - TensorFlow, PyTorch, uma infinidade de bibliotecas. Mas quando você precisa colocar algo em produção, especialmente em ambientes edge ou on-premise, a história muda completamente.

Eu queria previsibilidade na latência. Controle fino sobre o uso de memória. Binários enxutos que não dependessem de um ambiente Python complexo. E, acima de tudo, queria entender exatamente o que estava acontecendo sob o capô, sem "mágica" escondida.

Rust oferecia tudo isso, mesmo que eu estivesse começando do zero. A curva de aprendizado foi íngreme, mas recompensadora. E com ferramentas como Cursor combinadas com modelos avançados como Claude-3.7 e GPT-4, consegui desenvolver em semanas o que normalmente levaria meses.

A Descoberta Surpreendente Sobre Embeddings

Testei de tudo: TF-IDF, BM25, BERT, SVD, MiniLM, Bag-of-Words, Char n-gram, e diversos modelos ONNX como sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2, intfloat/multilingual-e5-small, e Alibaba-NLP/gte-multilingual-base.

E aqui está a parte que me surpreendeu: na prática, a melhoria marginal que esses embeddings complexos ofereciam não justificava a complexidade operacional adicional. Voltei ao BM25 - sim, aquele algoritmo clássico de recuperação de informação - e implementei uma versão nativa em Rust.

O resultado? Performance consistente e previsível. E quando você combina BM25 com HNSW para indexação e quantização SQ-8 para economia de memória, obtém algo realmente poderoso.

A Fórmula Que Realmente Funciona

Depois de incontáveis testes e benchmarks, cheguei a uma combinação que simplesmente funciona:

  • HNSW como índice principal - rápido e consistente

  • BM25 512D para representação - simples e previsível

  • Quantização SQ-8 - 4x menos memória com perda mínima de qualidade

  • Chunking disciplinado entre 200-500 tokens

Essa abordagem opinionada eliminou a complexidade de configurar múltiplos modelos e parâmetros. E os números falam por si: 4.6k operações de inserção por segundo e 2.3k operações de atualização por segundo, tudo em um projeto com menos de 30 dias e 100% gerado por IA.

UX Como Prioridade Absoluta

O que realmente fez diferença na prática foi focar na experiência do desenvolvedor. Auto-mapeamento de dados locais de projetos. Collections segmentadas que migram automaticamente para multi-index quando passam de 10k vetores. Setup com zero dependências externas.

E o mais importante: APIs REST, MCP e UMICP já prontos para uso. O Vectorizer não é apenas um índice - é um runtime de contexto que orquestra ingestão, chunking, indexação e consulta de forma coerente.

Em apenas um dia, usando Cursor + Vectorizer, consegui criar o Transmutation - uma ferramenta que converte PDF, DOCX, XLSX, HTML e até áudio/vídeo (via OCR e STT) para Markdown consistente, com integração direta para ingestão no Vectorizer.

Os próximos passos são ambiciosos: embeddings de imagem para LLMs multimodais, suporte nativo para CUDA/Metal/Vulkan, e manipulação direta de VRAM com shaders. A visão é ter ~11 bilhões de tokens em uma máquina comum com 32GB RAM, sem GPU e sem APIs externas.

O projeto está em pré-alpha - há bugs inevitáveis - mas já uso diariamente e os resultados têm sido transformadores. Às vezes, a solução não está em adicionar mais complexidade, mas em simplificar e focar no que realmente importa.

O Desafio da Implementação Prática em Rust

Implementar tudo isso em Rust, sendo um iniciante na linguagem, foi como aprender a pilotar um avião enquanto ele já estava no ar. Mas aqui está o segredo: Rust tem uma característica fascinante que poucos mencionam quando falam sobre sua curva de aprendizado. A linguagem praticamente te força a escrever código seguro desde o início. E quando você está lidando com sistemas de busca que precisam processar milhões de vetores sem vazamentos de memória, isso deixa de ser uma inconveniência e se torna uma benção disfarçada.

Meu maior desafio foi entender o sistema de ownership e lifetimes. Quantas vezes eu tentei compilar apenas para receber erros que pareciam escritos em uma língua alienígena? Mas depois que o "clique" acontece, você percebe que esses erros estão literalmente prevenindo bugs que apareceriam apenas em produção. E em sistemas de busca, onde a consistência é tudo, isso faz toda a diferença.

Por Que BM25 Ainda É Tão Eficaz

Vamos falar sobre essa decisão de voltar ao BM25 quando todo mundo está obcecado com embeddings neurais. A verdade é que muitos projetos de busca semântica sofrem do que chamo de "síndrome do martelo de ouro" - quando você tem um martelo caro, tudo parece ser prego.

O BM25 tem uma vantagem fundamental: transparência. Você sabe exatamente por que um documento foi classificado como relevante. São fatores como frequência de termos, comprimento do documento e frequência inversa do termo. Simples, previsível e, na maioria dos casos práticos, incrivelmente eficaz.

Compare isso com embeddings de 768 ou 1024 dimensões. Você realmente precisa de toda essa complexidade para determinar que "gato" e "felino" são semanticamente similares? Às vezes, a solução mais simples não é apenas mais fácil de implementar - é genuinamente melhor para o problema em questão.

O Poder da Quantização na Prática

A quantização SQ-8 foi outra decisão que transformou completamente a viabilidade do projeto. Quando você está lidando com milhões de vetores, cada byte economizado se multiplica exponencialmente. E a beleza da quantização é que ela permite essa economia sem sacrificar significativamente a qualidade dos resultados.

Imagine que cada vetor de 512 dimensões, usando floats de 32 bits, consumiria 2KB. Com SQ-8, isso cai para 512 bytes. Em um sistema com 1 milhão de vetores, você vai de 2GB para 500MB. Essa diferença não é apenas sobre economia de memória - é sobre a possibilidade de manter todo o índice na RAM, o que reduz drasticamente a latência das consultas.

E o mais interessante? Na prática, a perda de precisão é mínima. A maioria das aplicações não precisa da precisão milimétrica que floats de 32 bits oferecem. É como medir a distância entre duas cidades com uma régua milimetrada - precisão excessiva para o problema em questão.

Lições Sobre Chunking Que Ninguém Conta

O chunking parece uma das partes mais simples de um sistema RAG, mas é onde muitos projetos falham silenciosamente. Depois de testar inúmeras estratégias, aprendi que não existe uma abordagem única que funcione para todos os casos.

Para documentos técnicos, chunks entre 200-300 tokens funcionam melhor. A informação fica suficientemente concentrada para ser útil, mas não tão diluída que perde o contexto. Para conteúdo narrativo, como artigos ou livros, chunks maiores de 400-500 tokens capturam melhor o fluxo da história.

Mas aqui está o insight mais valioso: o overlap entre chunks é superestimado. Muitos sistemas usam overlaps de 10-20%, mas na prática isso gera redundância sem benefício significativo. Um overlap de 5% ou até mesmo nenhum overlap, combinado com uma estratégia inteligente de quebra por parágrafos ou seções, geralmente produz resultados melhores.

Integração MCP: Mais Do Que Apenas Um Protocolo

A decisão de implementar suporte nativo para Model Context Protocol (MCP) não foi apenas sobre seguir tendências. Foi sobre reconhecer que os sistemas de busca modernos precisam se integrar naturalmente com o ecossistema de IA que está surgendo.

O MCP permite que o Vectorizer seja usado diretamente por assistentes de IA como Claude ou GPT, sem camadas complexas de abstração. E isso muda completamente a experiência do desenvolvedor. Em vez de gastar dias configurando APIs e formatos de resposta, você tem uma integração que "apenas funciona".

E quando você combina isso com a capacidade de ingestão automática de múltiplos formatos de arquivo, o resultado é um sistema que se adapta ao fluxo de trabalho natural, em vez de forçar o desenvolvedor a se adaptar ao sistema.

Benchmarks Que Importam na Vida Real

Todo mundo fala sobre QPS (queries per second) e recall@k, mas essas métricas frequentemente não refletem a experiência real do usuário. O que realmente importa em sistemas de produção é a consistência da latência e a capacidade de lidar com picos de carga sem degradação significativa.

Durante meus testes, observei que o Vectorizer mantinha latências abaixo de 50ms para 95% das consultas, mesmo sob carga. E isso em hardware modesto - um laptop com 16GB de RAM. A combinação de Rust + HNSW + quantização cria uma base tão sólida que a performance se mantém previsível independentemente das condições.

E aqui está algo que raramente é discutido: a importância do cold start time. Muitos sistemas de busca levam minutos para carregar índices grandes. Com a arquitetura do Vectorizer, mesmo índices com milhões de vetores carregam em segundos. Essa pode ser a diferença entre uma aplicação que escala naturalmente e uma que requer complexos sistemas de cache e pré-carregamento.

O Futuro: Além dos Vetores Tradicionais

Enquanto continuo desenvolvendo o Vectorizer, estou explorando direções que vão além da busca vetorial tradicional. Uma delas é a integração nativa com modelos multimodais - a capacidade de indexar e buscar não apenas texto, mas também imagens e áudio de forma unificada.

Outra fronteira interessante é o uso de técnicas de aprendizado por reforço para otimizar automaticamente os parâmetros de busca baseado no feedback do usuário. Imagine um sistema que aprende com cada interação quais tipos de resultados são mais relevantes para contextos específicos.

E talvez o mais ambicioso: a criação de um sistema de cache inteligente que não apenas armazena resultados, mas prevê quais vetores serão acessados com base nos padrões de uso. Isso poderia reduzir ainda mais a latência enquanto mantém a consistência dos resultados.

O caminho até aqui foi repleto de descobertas inesperadas e insights contra-intuitivos. E cada nova implementação revela que, às vezes, as soluções mais elegantes estão escondidas em princípios simples que resistiram ao teste do tempo, combinados com implementações modernas que respeitam as limitações do mundo real.

Com informações do: andreferreira.com.br