"RAG na prática: dê memória e contexto ao seu LLM"
Implemente Retrieval-Augmented Generation para responder com base nos seus próprios documentos, reduzir alucinações, manter o modelo atualizado e medir a qualidade do pipeline.

Um modelo de linguagem sozinho só sabe o que viu durante o treinamento — ele não conhece os seus documentos, sua política interna ou o produto que você lançou ontem. RAG (Retrieval-Augmented Generation) resolve isso conectando o modelo a uma base de conhecimento que você controla. Neste guia prático você vai entender o fluxo completo de um sistema RAG, desde a indexação dos documentos até a geração da resposta, com as decisões, o código e as armadilhas que aparecem na vida real.
O problema que o RAG resolve
LLMs têm duas limitações incômodas. Primeiro, o conhecimento congelado: o que não estava nos dados de treino, o modelo não sabe. Segundo, a alucinação: quando não sabe, o modelo frequentemente inventa uma resposta confiante e errada.
RAG ataca os dois de uma vez. Em vez de confiar só na "memória" interna do modelo, você busca trechos relevantes de uma base externa e os injeta no prompt antes de pedir a resposta. O modelo passa a responder com base em fatos que você forneceu. A técnica foi formalizada por Lewis et al. (2020), que combinaram um recuperador de documentos com um gerador e demonstraram ganhos consistentes em tarefas que dependem de conhecimento específico. Se você ainda não tem o conceito firme, vale ler antes o que é RAG (Retrieval-Augmented Generation)?.
Há um terceiro ganho que costuma decidir a adoção em empresas: rastreabilidade. Como a resposta vem de trechos concretos, você pode citar a fonte de cada afirmação. Num suporte técnico ou num assistente jurídico, poder apontar "isto veio do documento X, seção Y" é a diferença entre uma ferramenta confiável e um gerador de texto bonito.
A anatomia de um sistema RAG
Todo sistema RAG tem duas fases. A fase de indexação acontece offline, uma vez (e a cada atualização dos documentos). A fase de consulta acontece a cada pergunta do usuário.
INDEXAÇÃO (offline)
documentos → divisão em pedaços → embeddings → banco vetorial
CONSULTA (a cada pergunta)
pergunta → embedding → busca por similaridade → trechos relevantes
→ prompt (pergunta + trechos) → LLM → respostaVamos destrinchar cada parte.
Passo 1: divida os documentos em pedaços (chunking)
Você não joga documentos inteiros na busca — divide em pedaços menores, os chunks. Por quê? Porque você quer recuperar só os trechos relevantes, não páginas inteiras de ruído. O tamanho do chunk é uma decisão delicada:
Um ponto de partida comum é dividir por parágrafos ou em blocos de algumas centenas de tokens, com uma pequena sobreposição entre chunks vizinhos para não cortar uma ideia no meio. Ajuste conforme o seu conteúdo.
Estratégias de chunking
Nem todo documento se divide bem do mesmo jeito. Vale conhecer as abordagens:
Um cuidado prático: preserve metadados junto de cada chunk (título do documento, seção, URL, data). Eles servem tanto para filtrar a busca quanto para citar a fonte na resposta.
def dividir_em_chunks(texto: str, tamanho: int = 500, sobreposicao: int = 75) -> list[str]:
palavras = texto.split()
chunks = []
passo = tamanho - sobreposicao
for inicio in range(0, len(palavras), passo):
pedaco = palavras[inicio:inicio + tamanho]
if pedaco:
chunks.append(" ".join(pedaco))
return chunksPasso 2: transforme texto em embeddings
Cada chunk vira um vetor numérico que representa seu significado — um embedding. Textos com significado parecido geram vetores próximos no espaço, o que permite busca por similaridade semântica em vez de busca por palavra exata. Esse é o coração do RAG. Se a ideia de "significado virar número" ainda é nebulosa, leia o que são embeddings? Representando significado em vetores.
from openai import OpenAI
client = OpenAI()
def gerar_embedding(texto: str) -> list[float]:
resp = client.embeddings.create(
model="text-embedding-3-small",
input=texto,
)
return resp.data[0].embeddingVocê gera embeddings para todos os chunks na indexação e, depois, para cada pergunta na consulta.
Duas regras de ouro aqui. Primeiro, use o mesmo modelo de embedding na indexação e na consulta — vetores de modelos diferentes não são comparáveis. Segundo, se você trocar o modelo de embedding, reindexe tudo: os vetores antigos viram lixo no novo espaço. Por isso a escolha do modelo de embedding não é trivial e vale testar alguns antes de comprometer a base inteira.
Passo 3: guarde os vetores em um banco vetorial
Com milhares de embeddings, você precisa de uma estrutura que faça busca por similaridade rápido. É o papel do banco de dados vetorial, que indexa os vetores e responde "quais são os k mais parecidos com este?" em milissegundos. Entenda as opções em o que é um banco de dados vetorial?.
A busca por similaridade no RAG é um caso de recuperação densa (dense retrieval): em vez de casar palavras, você compara vetores. Karpukhin et al. (2020) mostraram que essa abordagem supera a busca por palavras-chave tradicional em perguntas de domínio aberto, porque captura significado e não só termos literais.
Vale entender o trade-off do índice. Bancos vetoriais usam algoritmos de busca aproximada de vizinhos (ANN, como HNSW) para serem rápidos em escala — o preço é abrir mão de uma fração da precisão em troca de muita velocidade. Para a maioria das aplicações, esse trade é excelente; só em bases pequenas faz sentido uma busca exata.
Passo 4: recupere os trechos relevantes
Na hora da pergunta, o fluxo é:
O valor de k é um ajuste: poucos trechos podem deixar de fora a resposta; trechos demais diluem o contexto e elevam o custo. Comece com algo entre 3 e 5 e calibre.
def recuperar(pergunta: str, k: int = 4) -> list[str]:
emb = gerar_embedding(pergunta)
resultados = banco_vetorial.buscar(vetor=emb, top_k=k)
return [r.texto for r in resultados]Indo além do top-k simples: busca híbrida e reranking
Quando a recuperação por embedding falha, costuma ser por dois motivos: a pergunta usa um termo exato (um código de produto, um nome próprio, uma sigla) que a busca semântica não prioriza, ou os top-k trazem trechos parecidos mas não os mais úteis. Duas técnicas atacam isso:
Essas duas adições são, na prática, o que mais diferencia um RAG "de demo" de um RAG que aguenta perguntas reais.
Passo 5: monte o prompt e gere a resposta
Agora você combina os trechos recuperados com a pergunta num prompt bem estruturado. A instrução-chave é ancorar o modelo no contexto e permitir que ele admita ignorância.
def responder(pergunta: str) -> str:
trechos = recuperar(pergunta)
contexto = "\n\n".join(trechos)
prompt = (
"Use APENAS o contexto abaixo para responder. "
"Se a resposta não estiver no contexto, diga que não sabe.\n\n"
f"Contexto:\n{contexto}\n\n"
f"Pergunta: {pergunta}"
)
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
)
return resp.choices[0].message.contentEssa instrução de "use apenas o contexto" é o que mais reduz alucinação — assunto que aprofundo em o que é alucinação em IA e como reduzi-la.
Para tornar a resposta rastreável, vale incluir um identificador em cada trecho e pedir que o modelo cite a fonte:
contexto = "\n\n".join(f"[Fonte {i+1}] {t}" for i, t in enumerate(trechos))
# e na instrução: "Cite a fonte entre colchetes ao usar uma informação."Assim a resposta vem com "[Fonte 2]" ao lado de cada afirmação, e o usuário consegue verificar.
Reescrevendo a pergunta antes de buscar
Um ganho de qualidade que muita gente esquece está antes da busca: a pergunta do usuário nem sempre é o melhor texto para gerar o embedding de consulta. Perguntas reais são curtas, cheias de pronomes e dependentes do histórico da conversa. "E o segundo caso?" não tem como recuperar nada útil sozinho.
Duas técnicas resolvem isso:
def reescrever_consulta(pergunta: str, historico: str) -> str:
prompt = (
"Reescreva a pergunta abaixo como uma consulta de busca "
"autossuficiente, incorporando o contexto do histórico.\n\n"
f"Histórico:\n{historico}\n\nPergunta: {pergunta}\n\nConsulta:"
)
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
)
return resp.choices[0].message.contentEssa etapa custa uma chamada extra ao modelo, mas em sistemas conversacionais costuma ser a diferença entre uma recuperação que funciona e uma que falha silenciosamente.
Por que o RAG vence o fine-tuning em muitos casos
Uma dúvida frequente: por que não treinar o modelo nos meus dados? RAG costuma ser melhor quando:
Fine-tuning ainda tem seu lugar (ensinar formato, tom, tarefas específicas), mas para "responder com base nos meus documentos", RAG quase sempre é o caminho. Os dois também não são excludentes: você pode usar fine-tuning para fixar o estilo das respostas e RAG para alimentar os fatos atualizados.
Onde os sistemas RAG falham
Conhecer as falhas comuns economiza semanas de frustração:
Como depurar quando a resposta vem errada
Quando um RAG erra, o reflexo de muitos é mexer no modelo gerador — quase sempre o lugar errado. Depure na ordem do pipeline:
Separar "falha de recuperação" de "falha de geração" é a habilidade de depuração mais importante em RAG.
Medindo a qualidade
Não confie no "parece que está bom". Avalie em duas frentes:
Monte um conjunto de perguntas de teste com respostas conhecidas e rode-o sempre que mudar chunking, modelo de embedding ou k.
Na avaliação da geração, três dimensões são úteis de medir separadamente:
Você pode automatizar parte dessa avaliação usando um LLM como juiz, comparando resposta e contexto — desde que valide o juiz contra um conjunto rotulado por humanos antes de confiar nele.
Mantendo a base saudável ao longo do tempo
Um RAG não é um projeto que você entrega e esquece — é um sistema vivo que degrada se a base não for cuidada. Alguns hábitos de manutenção evitam o apodrecimento silencioso:
A frase "lixo entra, lixo sai" vale em dose dupla para RAG: a qualidade da resposta nunca supera a qualidade e a organização dos documentos que você indexou.
Segurança e privacidade no pipeline
RAG levanta questões de segurança que valem atenção desde o início. Como você injeta conteúdo recuperado no prompt, sua aplicação fica exposta a prompt injection indireto: um documento malicioso na base pode conter instruções ocultas que o modelo segue ao processá-lo. Trate o conteúdo recuperado como dado não confiável e delimite-o claramente no prompt.
Há também a dimensão de controle de acesso. Se diferentes usuários têm permissão para ver diferentes documentos, a busca precisa respeitar isso. O padrão é filtrar por permissão no momento da recuperação — usando metadados de cada chunk — para que um usuário nunca receba, no contexto, um trecho que ele não poderia ler. Aplicar o filtro só na resposta final é tarde demais: o dado sensível já passou pelo modelo.
Perguntas frequentes
Qual o melhor tamanho de chunk? Não existe número universal. Para texto corrido, blocos de algumas centenas de tokens com 10–20% de sobreposição funcionam bem como ponto de partida. Documentos muito estruturados pedem chunking por seção. Meça com o seu conteúdo e ajuste.
Posso usar RAG sem banco vetorial? Em bases pequenas (poucas centenas de chunks), uma busca exata em memória, calculando a similaridade contra todos os vetores, resolve. O banco vetorial passa a valer a pena quando o volume cresce e a latência importa.
RAG elimina alucinação? Reduz drasticamente, mas não elimina. Se a recuperação falha ou o prompt não ancora bem o modelo, ele ainda pode inventar. Por isso a instrução de "use apenas o contexto" e a medição de fidelidade são essenciais.
Preciso reindexar quando adiciono um documento novo? Só o documento novo: gere os chunks e os embeddings dele e insira no banco. Reindexação completa só é necessária quando você troca o modelo de embedding ou a estratégia de chunking.
Conclusão
RAG é a forma mais prática e econômica de dar a um LLM conhecimento próprio, atualizado e rastreável. O segredo é entender que a qualidade do sistema depende sobretudo da recuperação: chunking bem-feito, bons embeddings, um banco vetorial eficiente, técnicas como busca híbrida e reranking, e um k calibrado. Com a recuperação certa e um prompt que ancora o modelo no contexto, você reduz alucinações drasticamente e ganha um assistente que responde sobre o que importa para você. Depure na ordem do pipeline, meça com rigor as dimensões de fidelidade e relevância, e refine o pipeline antes de culpar o modelo — é nele, não no gerador, que mora o ganho.