Pular para o conteúdo
MÓDULO 3.6

🧠 Embeddings, memória & RAG básico

Seu modelo local não conhece os seus documentos — mas pode passar a conhecer. Neste módulo você aprende o trio que dá memória à IA local: embeddings (transformar texto em significado), banco vetorial (guardar e buscar) e RAG (responder com base nos seus arquivos). No fim, um RAG completo rodando em poucas linhas, 100% local.

6
Tópicos
~40
Minutos
Intermediário
Nível
Prático
Tipo

Conteúdo detalhado

1

🔢 O que são embeddings

Computador não entende palavra — entende número. Embedding é o truque que vira texto em número de um jeito que preserva o significado: cada frase vira uma lista longa de números (um vetor), e textos com sentido parecido caem perto uns dos outros num espaço imaginário. "Gato" e "felino" ficam vizinhos; "gato" e "caminhão" ficam longe.

🆕 Novo aqui?

"Embedding" (ou "vetor") é uma lista de números — tipo [0.12, -0.03, 0.88, ...], geralmente com 384 a 1024 posições — que representa o significado de um texto. Um "modelo de embeddings" é uma IA pequena, treinada só pra produzir esses vetores (não gera texto, só "mede sentido"). "Similaridade" = quão perto dois vetores estão; perto = sentido parecido.

🔑 A ideia central

Embeddings transformam "buscar texto" num problema de geometria:

  • Cada pedaço de texto vira um ponto num espaço de muitas dimensões.
  • Significados parecidos ficam próximos; significados distantes ficam longe.
  • Buscar "o que combina com a pergunta" vira achar os pontos mais próximos — não importa se as palavras exatas batem.
vizinhos da pergunta gato felino bichano pergunta: "meu animal de estimação" carro moto veículo ≈ transporte (longe) pizza banana fruta

Cada palavra vira um ponto. A pergunta (em ciano) cai perto do aglomerado de "animais" e longe de "transporte" e "comida" — então a busca devolve gato, felino, bichano, mesmo que a pergunta não use nenhuma dessas palavras. Isso é busca por significado, não por palavra exata.

Texto
vira vetor
Sentido
vira distância
Perto
= parecido
Buscar
= achar vizinhos
2

📚 Por que RAG

O modelo local sabe muita coisa do mundo — mas não sabe nada do seu mundo: seus PDFs, suas notas, a documentação interna da empresa. Treinar o modelo de novo com isso é caro e lento. RAG resolve por fora: na hora da pergunta, ele busca os trechos certos dos seus documentos e cola no prompt, como contexto. O modelo responde lendo aquilo — sem retreinar nada.

🆕 Novo aqui?

RAG = Retrieval-Augmented Generation (geração aumentada por recuperação). Em português direto: "antes de responder, vá buscar os trechos relevantes e leia-os". O modelo continua o mesmo — você só enriquece o prompt com o material certo, na hora certa.

✗ Só o modelo (sem RAG)

  • Não conhece seus documentos privados
  • "Alucina" quando não sabe — inventa com confiança
  • Conhecimento congelado na data do treino
  • Não tem como citar a fonte

✓ Modelo + RAG

  • Responde sobre os SEUS arquivos
  • Menos alucinação — fala com base no trecho recuperado
  • Atualiza na hora: trocou o documento, mudou a resposta
  • Dá pra mostrar de onde veio a resposta

🆚 RAG não é fine-tuning

Fine-tuning muda os pesos do modelo (ensina um novo jeito de responder) — caro, demorado e difícil de atualizar. RAG não toca no modelo: ele só decide o que mostrar no prompt. Para "responder sobre os meus dados", RAG é quase sempre o caminho mais simples, barato e atualizável.

💡 Dica prática

RAG é o que dá memória de longo prazo a um agente local (Trilha 4): você guarda conversas, notas e fatos como vetores, e o agente "lembra" recuperando o que importa — em vez de tentar enfiar tudo na janela de contexto. Memória = RAG sobre o que já aconteceu.

Buscar
trechos certos
Aumentar
o prompt
Gerar
a resposta
Sem
retreinar
3

🔧 Anatomia de um RAG

Todo RAG tem duas metades: a ingestão (feita uma vez, quando você adiciona documentos) e a consulta (feita a cada pergunta). Entenda o desenho e o resto vira detalhe de código.

🆕 Novo aqui?

"Chunk" (pedaço) é um trecho do documento — um parágrafo, uns 200–500 tokens. Você quebra o texto em chunks porque (1) o modelo de embeddings tem limite de tamanho e (2) você quer recuperar só o trecho relevante, não o arquivo inteiro. Chunk bom = autocontido (faz sentido sozinho) e nem grande nem minúsculo demais.

INGESTÃO (uma vez) CONSULTA (a cada pergunta) quebra embeda guarda embeda busca vizinhos trechos 📄 Documento.txt / .pdf ✂️ Pedaços(chunks) 🔢 Embeddingsvetores 🗄️ Vector Store busca por similaridade ❓ Perguntado usuário 🔢 Vetor da perguntamesmo modelo 🤖 LLM local monta a resposta ✅ Respostacom fontes

Leia em duas linhas: em cima (roxo), a ingestão — documento → pedaços → vetores → banco. Embaixo (ciano), a consulta — pergunta → vetor → busca os vizinhos no banco → o LLM local recebe os trechos e escreve a resposta. O mesmo modelo de embeddings é usado dos dois lados.

Os 6 passos, em ordem

1

Chunk — quebrar o documento

Divida o texto em pedaços autocontidos (ex.: 1 parágrafo). Só na ingestão.

2

Embed — virar vetor

Passe cada chunk pelo modelo de embeddings; cada um vira um vetor.

3

Store — guardar no banco vetorial

Salve vetor + texto original no vector store. Pronto: documento "indexado".

4

Query — embedar a pergunta

Na hora da pergunta, embede o texto dela com o mesmo modelo.

5

Retrieve — buscar os vizinhos

Peça ao banco os N chunks mais próximos do vetor da pergunta.

6

Generate — responder com contexto

Cole os trechos no prompt e peça ao LLM local responder só com base neles.

Ingestão
chunk·embed·store
Consulta
query·retrieve·gen
Mesmo
modelo de embed
LLM
só no fim
4

⚡ Rodando embeddings local

Modelos de embeddings são pequenos — alguns têm poucas centenas de MB e rodam liso até em hardware budget, sem GPU. O nomic-embed-text é um ótimo padrão para começar: leve, bom em português e já empacotado no Ollama. Vamos baixar e gerar o primeiro vetor.

⌨️ Exemplo prático: seu primeiro embedding

Objetivo: baixar um modelo de embeddings e transformar uma frase num vetor. Cole no terminal e depois no Python.

# 1) baixe um modelo de embeddings pequeno (~270 MB)
ollama pull nomic-embed-text
# 2a) gere o vetor de um texto pela API REST do Ollama
curl http://localhost:11434/api/embeddings -d '{
  "model": "nomic-embed-text",
  "prompt": "IA local roda na sua máquina"
}'
# 2b) ou do Python, via SDK da OpenAI apontado pro local (módulo 3.5)
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
v = client.embeddings.create(model="nomic-embed-text",
                     input="IA local roda na sua máquina")
print(len(v.data[0].embedding))  # ex.: 768 → o tamanho do vetor

Como verificar: o curl devolve um JSON com uma lista grande de números — esse é o vetor. No Python, len(...) imprime a dimensão (ex.: 768). Se imprimiu um número, seus embeddings estão rodando 100% local. Guarde esse número: todos os vetores do mesmo modelo têm o mesmo tamanho.

💡 Dica prática

Use o mesmo modelo de embeddings para indexar os documentos e para embedar a pergunta. Misturar modelos (ou trocar de modelo no meio) quebra a busca, porque os vetores deixam de viver no mesmo espaço. Trocou de modelo de embeddings? Re-indexe tudo.

Pequeno
roda até sem GPU
nomic
bom padrão
768
dimensões fixas
Mesmo
modelo nos 2 lados
5

🗄️ Banco vetorial simples

Você poderia guardar os vetores numa lista e comparar um por um — funciona com 10 chunks, vira tartaruga com 100 mil. Um banco vetorial existe pra isso: armazena os vetores e responde rápido à pergunta "quais são os N mais parecidos com este aqui?".

🆕 Novo aqui?

"Vector store" / "banco vetorial" é um banco de dados especializado em busca por similaridade entre vetores. Opções locais comuns: Chroma (mais fácil pra começar, guarda em disco com 2 linhas), FAISS (biblioteca da Meta, rapidíssima, sem servidor) e sqlite-vec (uma extensão que dá busca vetorial ao SQLite que você já conhece). Todos rodam local, sem nuvem.

🟣 Chroma

Mais simples pra começar. Persiste em disco sozinho, API amigável.

Use quando: quer subir um RAG em minutos.

🟣 FAISS

Biblioteca de busca da Meta. Muito rápida, ótima em escala.

Use quando: são muitos vetores e velocidade importa.

🟣 sqlite-vec

Busca vetorial dentro de um arquivo SQLite. Zero servidor.

Use quando: já usa SQLite e quer um arquivo só.

⌨️ Montagem mínima com Chroma

Objetivo: guardar um vetor e buscar o mais parecido — o coração do RAG, isolado.

# pip install chromadb
import chromadb

db  = chromadb.PersistentClient(path="./meu_rag")   # guarda em disco
col = db.get_or_create_collection("docs")

# guardar: id + texto + o vetor que veio do nomic-embed-text
col.add(ids=["1"],
        documents=["A senha do wifi do escritório é roxo-1234"],
        embeddings=[[0.12, -0.03, 0.88]])            # <vetor real do passo 4>

# buscar: os 3 mais parecidos com o vetor da pergunta
hits = col.query(query_embeddings=[[0.10, -0.01, 0.90]], n_results=3)
print(hits["documents"])   # → o(s) trecho(s) recuperado(s)

Como verificar: rode duas vezes. O PersistentClient grava em ./meu_rag, então na segunda execução o documento já está lá sem precisar adicionar de novo. O query deve devolver o texto que você guardou.

💡 Dica prática

Guarde sempre o texto original junto do vetor (o campo documents). O vetor serve só para encontrar; o que você cola no prompt é o texto. Banco vetorial sem o texto guardado é uma busca que acha o ponto certo mas não sabe te mostrar o conteúdo.

Guarda
vetor + texto
Busca
os N vizinhos
Local
em disco, sem nuvem
Rápido
mesmo com muitos
6

🚀 Um RAG mínimo end-to-end

Hora de juntar tudo. Este script faz o RAG completo: lê um .txt, quebra em chunks, embeda, guarda, e aí responde uma pergunta usando só o que está no documento — tudo no seu Ollama. Os trechos marcados com <...> são o que você troca.

⌨️ Exemplo prático: RAG completo em ~25 linhas

Objetivo: responder uma pergunta cuja resposta só existe no seu arquivo. Salve como rag.py e rode.

# pip install chromadb openai   (Ollama já rodando)
from openai import OpenAI
import chromadb

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
EMB = "nomic-embed-text"
LLM = "qwen2.5:7b"                       # <troque pelo seu modelo>

def embed(txt):
    return client.embeddings.create(model=EMB, input=txt).data[0].embedding

# 1) INGESTÃO: lê o .txt, quebra em chunks, embeda e guarda
texto  = open("notas.txt").read()        # <seu arquivo .txt>
chunks = [c for c in texto.split("\n\n") if c.strip()]   # 1 parágrafo = 1 chunk
col = chromadb.Client().create_collection("notas")
col.add(ids=[str(i) for i in range(len(chunks))],
        documents=chunks,
        embeddings=[embed(c) for c in chunks])

# 2) CONSULTA: embeda a pergunta, busca os vizinhos, monta o prompt
pergunta = "Qual a senha do wifi do escritorio?"   # <sua pergunta>
viz = col.query(query_embeddings=[embed(pergunta)], n_results=3)["documents"][0]
contexto = "\n".join(viz)

resp = client.chat.completions.create(model=LLM, messages=[
  {"role": "system", "content": "Responda SO com base no contexto. Se nao houver, diga 'nao sei'."},
  {"role": "user",   "content": f"Contexto:\n{contexto}\n\nPergunta: {pergunta}"}
])
print(resp.choices[0].message.content)

Como verificar: crie um notas.txt com um fato que o modelo não teria como saber — ex.: a linha A senha do wifi do escritório é roxo-1234. Rode e faça a pergunta acima: se a resposta trouxer roxo-1234, o RAG funcionou — o modelo só acertou porque o trecho veio do seu documento. Agora pergunte algo que não está no arquivo: ele deve responder não sei em vez de inventar.

✓ RAG que funciona

  • Chunks autocontidos e do tamanho certo
  • Mesmo modelo de embeddings na ingestão e na consulta
  • System prompt manda usar SÓ o contexto
  • Mostra a fonte de cada resposta

✗ RAG que falha

  • Chunk gigante (documento inteiro) ou picado demais
  • Trocou o modelo de embeddings e não re-indexou
  • Sem regra de "não inventar" — o modelo alucina
  • Joga texto demais no prompt e estoura o contexto

Auto-checagem (opcional): o que o RAG faz, na prática?

Ingere
.txt → vetores
Busca
vizinhos da pergunta
Responde
só com o contexto
100%
local

📌 Resumo do módulo

Embeddings = significado em números — textos parecidos viram pontos próximos; buscar vira achar vizinhos.
RAG dá memória sem retreinar — busca os trechos certos e cola no prompt; responde sobre os SEUS dados.
Anatomia: ingestão + consulta — chunk·embed·store, depois query·retrieve·generate, mesmo modelo de embed.
Tudo local e em poucas linhas — nomic-embed-text + Chroma + Ollama = um RAG completo, sem nuvem.

Próximo:

Trilha 4 — Avançado & Agentes 24/7 · você sai de "rodar e servir modelos" para colocá-los a trabalhar sozinhos, o tempo todo.