RAG 100% local e privado: LanceDB, Ollama, Qdrant local
- ⬜📱 On-device inference mobile: ExecuTorch, MediaPipe, Core ML(Local LLMs & Edge AI)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
Quando RAG precisa ser local
Cenários comuns em 2026 onde dado NÃO pode sair: contratos legais (escritórios), prontuários médicos (hospitais), código proprietário (corporações), documentos confidenciais (defesa, governo). Toda chamada para OpenAI/Anthropic vira incidente de compliance. A alternativa: stack 100% local, do parsing à geração.
A pilha em uma página
Setup mínimo
# 1. Instalar Ollama + modelo
brew install ollama
ollama pull qwen2.5:14b # ou llama3.3:70b se tiver VRAM
ollama pull bge-m3 # embedding model
# 2. Python env
uv venv && source .venv/bin/activate
uv pip install docling lancedb sentence-transformers FlagEmbeddingfrom docling import DocumentConverter
from sentence_transformers import SentenceTransformer
import lancedb
# 1. Parse PDF mantendo estrutura
converter = DocumentConverter()
doc = converter.convert('contrato.pdf')
chunks = doc.export_to_markdown().split('\n\n') # simplificado
# 2. Embed local (sem API)
embedder = SentenceTransformer('BAAI/bge-m3')
vectors = embedder.encode(chunks)
# 3. Index local em LanceDB
db = lancedb.connect('./vault')
table = db.create_table('docs', data=[
{'text': c, 'vector': v.tolist()} for c, v in zip(chunks, vectors)
])
# 4. Query
query_vec = embedder.encode('cláusula de rescisão')
results = table.search(query_vec).limit(5).to_list()
# 5. Generate com Ollama local
import ollama
ctx = '\n\n'.join(r['text'] for r in results)
response = ollama.chat(
model='qwen2.5:14b',
messages=[{
'role': 'user',
'content': f'Contexto:\n{ctx}\n\nPergunta: O que diz a cláusula de rescisão?'
}]
)
print(response['message']['content'])Hybrid search — código
# LanceDB suporta hybrid nativamente com FTS + vector
import lancedb
from lancedb.rerankers import LinearCombinationReranker
db = lancedb.connect('./vault')
table = db.open_table('docs')
# Cria índice FTS (BM25-like) sobre 'text'
table.create_fts_index('text')
# Hybrid search
reranker = LinearCombinationReranker(weight=0.7)
results = (
table.search('cláusula rescisão contratual', query_type='hybrid')
.vector(embedder.encode('cláusula rescisão contratual').tolist())
.rerank(reranker=reranker)
.limit(10)
.to_list()
)Que restrição justifica montar toda a pilha de recuperação localmente?
Reranking com cross-encoder local
from FlagEmbedding import FlagReranker
# Roda local — sem API
reranker = FlagReranker('BAAI/bge-reranker-v2-m3', use_fp16=True)
# Pair-wise scoring
pairs = [['cláusula rescisão', candidate['text']] for candidate in results[:50]]
scores = reranker.compute_score(pairs, normalize=True)
# Reorder
reranked = sorted(zip(scores, results), reverse=True)[:5]Casos onde isso resolve
| Setor | Dado típico | Por que precisa ser local |
|---|---|---|
| Jurídico | Contratos, peças processuais, jurisprudência interna | Privilege, LGPD, segredo profissional |
| Médico | Prontuários, exames, protocolos | HIPAA, LGPD Art. 11 (dado sensível) |
| Corporativo (M&A, jurídico) | Due diligence, comitês | NDA estrito, vazamento = lawsuit |
| Defesa/Governo | Documentos classificados | Regulação setorial específica |
| Banking/Fintech | Transações, modelagem de risco | Compliance + custo de API em escala |
Performance esperada (M3 Ultra 128GB)
Perguntas frequentes
❓ Dá para fazer RAG sem enviar nada para a nuvem?
❓ Quando RAG local é a escolha certa?
❓ Qual a maior perda ao rodar tudo local?
Fixando
- → trechos
- → trechos
- → caminho híbrido
- → trecho sem PII
- Conceito de arquitetura
- Compute
- Segurança e identidade
- IA e machine learning
A escolha não é local contra nuvem: é qual PARTE do pipeline precisa ficar dentro. Recuperação local com geração hospedada sobre trecho anonimizado costuma satisfazer a norma sem pagar o custo de qualidade.
- Nada sai, e é esse o requisito. Quando a norma proíbe o dado deixar o perímetro, local não é preferência: é viabilidade. Fora disso, o custo de operar costuma superar a economia de token.
- A recuperação local fica boa. Corte, embedding e índice funcionam bem com modelos pequenos e sem GPU. Essa metade do pipeline não é o problema.
- A geração é o gargalo. É onde a diferença com modelo hospedado aparece, e mais em pergunta complexa. Aceitar isso explicitamente é melhor que descobrir na avaliação.
- Memória decide o que cabe. Modelo que não cabe não roda. Antes de vazão, a pergunta é quanta memória há para o modelo — e é ela que ordena a escolha de hardware.
- O híbrido resolve a maioria dos casos reais. Recuperar local e enviar só o trecho anonimizado ao modelo hospedado mantém o dado sensível dentro e a qualidade de raciocínio fora.
Por que a pilha local combina busca vetorial com busca por termos?
Qual é o papel do reordenador local depois da busca?
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…