← Blog

RAG na prática: construindo sistemas de recuperação com LangChain

2025-06-01· 12 minRAGLangChainLLMPython

O que é RAG e por que importa

Retrieval Augmented Generation (RAG) é a arquitetura que permite que modelos de linguagem respondam com base em dados externos — sem precisar de fine-tuning. Em vez de depender apenas do conhecimento treinado no modelo, o sistema consulta uma base de documentos em tempo real e usa o conteúdo recuperado como contexto para gerar a resposta.

Pense na diferença assim: um LLM sozinho responde com o que "lembra" do treinamento. Um sistema RAG responde com o que está nos seus documentos — manuais internos, contratos, bases de conhecimento, políticas da empresa.

A vantagem prática é enorme. Você atualiza seus dados sem retreinar nada: basta reindexar os documentos novos. Para aplicações corporativas — suporte interno, análise de contratos, Q&A sobre documentação técnica — isso é o que torna o sistema utilizável em produção.

O desafio é que um pipeline RAG envolve mais do que "jogar PDFs num vector store". A qualidade da resposta depende de como você indexa, recupera e avalia o contexto antes de chegar ao LLM.

Como funciona o pipeline

Um pipeline RAG tem três etapas fundamentais que se repetem em todo sistema, independente da stack:

  1. Indexação — processar documentos, dividir em chunks e gerar embeddings
  2. Recuperação — buscar os trechos mais relevantes para a pergunta do usuário
  3. Geração — passar o contexto recuperado ao modelo para produzir a resposta
Pipeline RAG

Documentos são divididos em chunks e convertidos em embeddings. O vector store persiste os vetores para busca futura.

in → PDFs, docs, APIs|out → Vector store

Na prática, a indexação roda offline (ou em batch): você processa os documentos uma vez e persiste os vetores. A recuperação e a geração rodam em tempo real, a cada pergunta do usuário.

Cada etapa tem decisões que impactam a qualidade final. Entender esses blocos ajuda a diagnosticar problemas: se a resposta está errada, o bug provavelmente está na recuperação — não no prompt.

Indexação e chunking

Antes de gerar embeddings, você precisa dividir documentos longos em chunks — pedaços menores de texto. O modelo não consegue indexar um PDF de 200 páginas como um bloco único; a busca semântica funciona em unidades menores.

Duas decisões críticas aqui:

  • Tamanho do chunk — chunks muito grandes diluem a precisão; muito pequenos perdem contexto
  • Overlap (sobreposição) — trechos repetidos entre chunks adjacentes evitam que informações na fronteira sejam cortadas
Chunking interativo
80 caracteres
20 caracteres

Documento

O Open Finance permite que consumidores compartilhem dados financeiros entre instituições autorizadas de forma segura e padronizada, mediante consentimento explícito.

Documentos técnicos (APIs, manuais) costumam pedir chunks de 256–512 tokens com overlap de 10–20%. Narrativas longas toleram blocos maiores. Não existe valor universal — ajuste com base em evals.

Depois do chunking, cada pedaço passa por um modelo de embedding que converte texto em vetor numérico. Textos semanticamente próximos ficam próximos no espaço vetorial — essa é a base da busca por similaridade.

Embeddings e recuperação vetorial

O vector store (Chroma, Pinecone, pgvector, Qdrant) armazena os embeddings e permite buscar por similaridade. Quando o usuário faz uma pergunta, ela também vira um vetor — e o sistema retorna os chunks cujos vetores estão mais próximos.

O retriever define quantos chunks retornar (k) e com qual estratégia:

  • Similaridade pura — os k vetores mais próximos
  • MMR (Maximal Marginal Relevance) — equilibra relevância e diversidade, evitando chunks redundantes
  • Busca híbrida — combina similaridade vetorial com busca por palavras-chave (BM25)
Busca vetorial

Clique em Buscar para ver os chunks recuperados

O k=4 é um bom ponto de partida: poucos chunks perdem contexto; muitos estouram a janela do modelo e aumentam custo. Ajuste com base em evals, não em intuição.

O fluxo de uma pergunta

Quando o usuário envia uma pergunta, o ciclo completo se desenrola em segundos:

Fluxo de uma pergunta
Usuário
Retriever
LLM

O LLM recebe um prompt montado com a pergunta original mais os chunks recuperados. A resposta final deve ser fundamentada no contexto — não inventada. Se os chunks errados forem recuperados, a resposta será ruim mesmo com o melhor modelo disponível.

Exemplo prático com LangChain

O trecho abaixo monta um pipeline RAG mínimo com LangChain e Chroma. O ponto central é o ciclo: indexar uma vez, consultar em runtime.

from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate

# 1. Indexação — vetorizar documentos e persistir no Chroma
vectorstore = Chroma.from_documents(
    documents=docs,
    embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
    persist_directory="./chroma_db",
)

# 2. Recuperação — retriever busca os k chunks mais similares
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})

# 3. Geração — LLM recebe pergunta + contexto recuperado
prompt = ChatPromptTemplate.from_template("""
Responda com base apenas no contexto abaixo.

Contexto: {context}

Pergunta: {input}
""")

llm = ChatOpenAI(model="gpt-4o-mini")
doc_chain = create_stuff_documents_chain(llm, prompt)
rag_chain = create_retrieval_chain(retriever, doc_chain)

resposta = rag_chain.invoke({"input": "Como funciona o open finance?"})
print(resposta["answer"])

Três detalhes que fazem diferença na prática:

  • Use um modelo de embedding consistente entre indexação e consulta — trocar o modelo exige reindexar tudo
  • O prompt deve instruir o modelo a responder somente com base no contexto
  • Persista o vector store em disco (persist_directory) para não reindexar a cada restart

Além do básico: pontos de atenção

Na prática, um RAG simples não é suficiente para produção. Alguns pontos críticos:

  • Chunking estratégico — tamanho e sobreposição dos chunks afetam diretamente a qualidade da recuperação
  • Reranking — um cross-encoder reordena os resultados do retriever antes de passar ao LLM, melhorando precisão sem aumentar k
  • Avaliação — métricas como faithfulness e answer relevancy (framework RAGAS) medem se o sistema responde com base no contexto e se a resposta é útil
  • Observabilidade — logue queries, chunks recuperados e latência por etapa; sem isso, debugar em produção é adivinhação

Armadilhas comuns:

  • Confiar que "mais contexto = melhor resposta" — contexto irrelevante confunde o modelo
  • Ignorar atualização do índice — documentos desatualizados geram respostas desatualizadas
  • Pular evals e ir direto para produção — o protótipo funciona na demo, falha no edge case
  • Não versionar o pipeline de indexação — mudou o chunker ou o embedding, reindexe e compare métricas

Conclusão

RAG é a espinha dorsal de boa parte das aplicações de IA generativa em produção. Dominar os detalhes — desde a estratégia de chunking até o monitoramento — é o que separa um protótipo de um sistema confiável.

O próximo passo natural é pegar um conjunto real de documentos, definir 10–20 perguntas de teste com respostas esperadas, e medir retrieval precision antes de otimizar prompts ou trocar de modelo. Comece simples, meça, e adicione complexidade (reranking, busca híbrida, agentes) só quando os dados justificarem.

Referencias

  1. Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks (Lewis et al., 2020)
  2. LangChain — Conceitos de RAG
  3. LangChain — Tutorial de RAG
  4. LangChain — Retrieval e vector stores
  5. Chroma — Documentação
  6. OpenAI — Guia de Embeddings
  7. RAGAS — Framework de avaliação
  8. Precise Zero-Shot Dense Retrieval without Relevance Labels (HyDE)
  9. Lost in the Middle: How Language Models Use Long Contexts
  10. Pinecone — Chunking strategies for RAG