RAG local com ChromaDB e Ollama: tutorial Python
Fazer RAG localmente com ChromaDB, Ollama e Python envolve três componentes que se encaixam: um armazenamento vetorial que mantém os dados em disco (ChromaDB), um modelo de embeddings que transforma seus blocos de texto em vetores (nomic-embed-text via Ollama) e um LLM de chat que responde com base nos trechos encontrados. Sem chave de API, sem vazamento de dados. Este guia leva você de um PDF bruto a um chatbot que cita suas fontes em 22 minutos.
#Por que essa stack para um RAG local
Muitos tutoriais de RAG começam com LangChain ou LlamaIndex. Esses frameworks são poderosos, mas escondem o que acontece por trás. Aqui, escrevemos o pipeline manualmente com apenas três dependências. Você entenderá cada etapa e saberá o que otimizar mais tarde.
- ChromaDB
- Armazenamento de vetores de código aberto, puramente em Python, com modo persistente integrado (SQLite + índice HNSW). Sem necessidade de iniciar um servidor.
- Ollama
- Serve tanto o modelo de embeddings (nomic-embed-text) quanto o LLM de chat (Qwen 3.5, Granite 4.2, Gemma 4). Ponto de acesso HTTP único em localhost:11434.
- Python nativo
- Algumas funções, sem framework. Você poderá conectar o LangChain mais tarde, se necessário, mas isso não é necessário para começar.
#Pré-requisitos
Este guia leva você ao modelo. O kit leva você ao copiloto que programa no seu editor.
- Espaço online vitalício
- PDF + arquivos
- Atualizações vitalícias
- Python 3.10+
- ChromaDB exige pelo menos 3.10. Verifique com python --version.
- Ollama instalado e iniciado
- O daemon escuta por padrão em http://localhost:11434. Se você está começando do zero, siga primeiro o guia de instalação do Ollama.
- 8 GB de RAM
- 16 GB confortáveis. O modelo de chat 9B em Q4 ocupa ~6 GB, o modelo de embeddings ~300 MB.
- Um GPU não é obrigatório
- A inferência na CPU funciona, mas é mais lenta. Para a ingestão de um grande corpus, uma GPU de 6 GB ou mais acelera muito os embeddings.
#1. Instalar o ChromaDB e preparar Ollama
Criamos um ambiente virtual limpo, instalamos as três bibliotecas necessárias e baixamos os modelos no Ollama.
Três pacotes: chromadb para o armazenamento vetorial, ollama para o cliente Python oficial, pypdf para ler PDFs. Isso é tudo.
nomic-embed-text é um modelo de embeddings de 137M de parâmetros, multilíngue, que gera vetores de dimensão 768. Leve, rápido, bom em francês. Qwen 3.5 9B (6,6 GB, 256k de contexto, multilíngue, Apache 2.0) é usado para o chat final: é a escolha padrão para 8 GB em 2026. Você pode substituí-lo por granite4.2:8b (mais econômico) ou gemma4:12b sem alterar nada no código.
#2. Configurar o modelo de embeddings
Um embedding é um vetor que representa o significado de um texto. Dois textos semanticamente próximos têm vetores próximos. É o motor do RAG: procuramos os chunks cujo embedding se assemelha mais ao da pergunta.
Você deve ver Dimensão do vetor: 768. Se ocorrer uma falha com a mensagem model not found, é porque o comando ollama pull nomic-embed-text não foi executado.
#3. Ingestão de PDF em francês
A ingestão faz três coisas: ler as páginas de um PDF, dividir o texto em pedaços de tamanho razoável e armazenar cada pedaço com seu embedding no ChromaDB em modo persistente.
O chunker divide em blocos de 800 caracteres com 100 de sobreposição. É um ponto de partida: nem muito pequeno (falta de contexto) nem muito grande (dilui o sinal). Para conteúdo jurídico muito denso, reduza para 500. Para manuais técnicos com texto mais espaçado, aumente para 1200.
Inicie a ingestão em um diretório ./pdfs/ contendo seus documentos:
#4. Busca top-k no ChromaDB
Após a indexação dos chunks, a busca consiste em gerar um embedding da pergunta e depois pedir ao Chroma os k vetores mais próximos pela distância de cosseno. É instantâneo, mesmo com 100.000 chunks.
k=4 é um bom valor padrão. Se o valor for muito baixo, você perde contexto relevante; se for muito alto, você sobrecarrega o LLM com ruído e ultrapassa o limite da janela de contexto. Para perguntas muito precisas, k=2 é suficiente. Para perguntas transversais, aumente para 6.
#5. Loop de conversa com citações
Agora vamos montar: buscamos os chunks relevantes, construímos um prompt com o contexto, enviamos para Qwen 3.5 via Ollama, e pedimos ao modelo para citar suas fontes.
Três detalhes que importam. Primeiro, temperature=0.2: queremos uma resposta factual, não criativa. Segundo, num_ctx=8192: a janela padrão do Ollama (2048) é curta demais quando se injetam 4 blocos de 800 caracteres. Terceiro, o prompt de sistema força o modelo a dizer 'não sei' em vez de alucinar — esse é o principal mecanismo de proteção contra alucinações do RAG.
#6. Exemplo prático: chatbot jurídico para contratos
Imaginemos um escritório que deseja consultar 200 contratos de prestação de serviços em PDF. Com a stack acima, em menos de uma hora já temos um assistente respondendo perguntas do tipo:
- Pergunta típica
- « Quais contratos incluem uma cláusula de não concorrência com duração superior a 12 meses após a rescisão? »
- O que está acontecendo
- O embedding da pergunta localiza os chunks que contêm palavras-chave semanticamente próximas (não concorrência, pós-ruptura, duração). O Qwen 3.5 lê esses 4 trechos e responde com os nomes dos arquivos correspondentes.
- Garantia de confidencialidade
- Nenhum dado sai do dispositivo. Sem chave de API. Sem telemetria. Isso diferencia um RAG local de um wrapper OpenAI.
#Solução de problemas
- ChromaDB lento na ingestão
- O gargalo é quase sempre a chamada de embeddings para o Ollama. Verifique se nomic-embed-text está rodando na GPU com o comando ollama ps. Na CPU, espere cerca de 50 blocos por segundo; na GPU, cerca de 500.
- « model not found »
- Ollama não encontra nomic-embed-text. Execute novamente ollama pull nomic-embed-text e verifique com o comando ollama list.
- Respostas que inventam fontes
- Um modelo 9B ainda alucina de vez em quando. Mude para mistral-small (24B, ~14 GB, muito bom em francês) ou qwen3.8:27b se você tiver VRAM suficiente. Ou adicione um reranker (cross-encoder) após o ChromaDB para filtrar falsos positivos.
- Embeddings de baixa qualidade em FR
- nomic-embed-text é multilíngue, mas não é ideal para conteúdo exclusivamente em francês. Para conteúdo jurídico ou médico, teste Solon-embeddings-large-0.1 ou bge-m3 (a carregar via sentence-transformers, fora do Ollama).
- ChromaDB cresce sem limite
- Cada reindexação adiciona duplicatas. Antes de ingerir novamente um PDF, execute collection.delete(where={"source": name}) para remover os blocos antigos.
#Para se aprofundar
Você tem um RAG funcional. Aqui estão os próximos passos naturais para levá-lo mais longe:
- Comparar modelos de embeddings FR
- Nosso guia « Os melhores modelos de embeddings FR » compara BGE, E5, Solon e nomic em conteúdo francês.
- Melhorar o chunking
- « Estratégias de chunking » detalha o chunking semântico, por títulos em markdown ou por parágrafos — muitas vezes o que traz mais precisão.
- Adicionar um reranker
- « Adicionar um reranker ao pipeline »: +15% de relevância ao colocar um cross-encoder após o Chroma. O próximo passo lógico.
- Busca híbrida
- “Busca híbrida BM25 + vetorial” combina busca lexical e semântica, sendo indispensável sempre que houver muito jargão ou muitos nomes próprios.
Um comentário, um erro ou uma observação? Avise-nos; isso ajuda a melhorar o guia para todos.