RAG Engine em Rust
Backend RAG em Rust: recuperação híbrida em dois estágios com pgvector, chat em streaming via WebSocket com atores Actix, inferência local com Ollama.
Overview
A maioria das demos de RAG para no busca vetorial pura. Este projeto vai além: um pipeline de recuperação híbrida em dois estágios onde a busca semântica por cosseno no pgvector reduz o conjunto de candidatos, depois um re-ranking por palavras-chave pontua dentro desse conjunto e combina os dois sinais. O resultado lida com consultas de termos exatos que enganam a busca vetorial pura, sem sacrificar o recall em consultas parafraseadas. Tudo em Rust — não por ser na moda, mas porque a arquitetura exige: um modelo de atores Actix por sessão WebSocket, um pipeline Ollama com concorrência limitada e uma integração com pgvector que precisa de async sem overhead. O lado de ingestão busca artigos de uma API REST no estilo HelpScout, converte HTML em Markdown limpo usando html2md e regras customizadas de scraper, armazena artigos canônicos no PostgreSQL via Diesel ORM, e depois distribui para dois pipelines paralelos: um microsserviço Python (Flask + all-MiniLM-L6-v2) gera os embeddings armazenados no pgvector, enquanto um pipeline Ollama com concorrência limitada gera palavras-chave e resumos por artigo. O pipeline de metadados usa um Semaphore do Tokio e buffer_unordered para limitar as chamadas simultâneas ao LLM local, com rastreamento estruturado de sucesso/parcial/falha e persistência dos IDs com falha para reprocessamento. O chat em streaming funciona através de um modelo de atores Actix. Cada conexão WebSocket gera um ator ChatSession; um ator central ChatServer roteia mensagens e gerencia o estado da sessão. A API de streaming do Ollama é consumida token por token e encaminhada em tempo real pela conexão — o cliente vê as palavras aparecerem conforme são geradas, ancoradas em artigos recuperados pelo pipeline híbrido. Um balanceador de carga Ollama round-robin com thread pool do Rayon está incluído no código para distribuição de inferência em múltiplas instâncias — presente e funcional, aguardando hardware. Este é um serviço exclusivamente de backend — o scaffold de frontend incompleto foi removido intencionalmente. O valor está na infraestrutura: um pipeline RAG em Rust de nível produção com recuperação híbrida real, chat em streaming e design centrado em inferência local. Build limpo, zero avisos do clippy, 19 testes, CI configurado. Código público no GitHub sob bredmond1019.
Technical Stack
IA / Recuperação
- ▸pgvector (similaridade por cosseno)
- ▸Sentence Transformers (all-MiniLM-L6-v2)
- ▸Ollama (llama3.1)
- ▸Recuperação híbrida em dois estágios
- ▸Re-ranking por palavras-chave
Backend
- ▸Rust (edição 2021)
- ▸Actix-web 4
- ▸Atores Actix
- ▸Tokio
- ▸Diesel 2
Dados
- ▸PostgreSQL
- ▸pgvector
- ▸Migrations Diesel
- ▸Python Flask (microsserviço de embeddings)
Infraestrutura
- ▸GitHub Actions CI
- ▸Docker (Postgres + pgvector)
Key Features
Recuperação híbrida em dois estágios: busca semântica por cosseno no pgvector reduz candidatos, re-ranking por palavras-chave combina os sinais para pontuação final
Modelo de atores Actix para chat WebSocket — atores ChatSession por sessão gerenciados por um ChatServer central
Streaming token por token do Ollama encaminhado em tempo real pela conexão WebSocket
Pipeline de metadados com concorrência limitada usando Semaphore e buffer_unordered do Tokio para controlar chamadas simultâneas ao Ollama
Sincronização paginada de artigos de uma API REST no estilo HelpScout com timeout de 30 segundos e conversão completa de HTML para Markdown
Microsserviço de embeddings em Python com sentence-transformers all-MiniLM-L6-v2 na porta 8080
Fila de jobs assíncrona para ingestão em background com rastreamento de status por job
Persistência de artigos com falha e endpoint de re-embedding para recuperação de falhas parciais no pipeline
Balanceador de carga Ollama round-robin (thread pool Rayon) para distribuição de inferência em múltiplas instâncias — incluído, aguardando hardware
Code Examples
Technical Challenges
Projetar a fusão de recuperação em dois estágios para que o re-ranking por palavras-chave melhore a precisão em termos exatos sem regredir o recall semântico
Construir o modelo de atores Actix para que o histórico de chat por sessão fique dentro do ator, sem estado externo compartilhado
Implementar concorrência limitada no pipeline de metadados — Semaphore + buffer_unordered — para que chamadas em lote ao Ollama não saturem o hardware local
Fazer o vendoring e patching do ollama-rs 0.2.0: a versão upstream 0.2.x→0.3.x mudou a API de histórico para exigir Arc<Mutex<MessagesHistory>> externo, incompatível com o modelo de atores por sessão
Escrever regras customizadas de scraper para conversão HTML-Markdown preservando a estrutura de artigos de suporte longos e aninhados
Manter a interface do microsserviço de embeddings estável para que mudanças de dimensão de embedding sejam tratadas via migration, não via código