> LangChain: RAG (Retrieval Augmented Generation)
~/posts/langchain:-rag-(retrieval-augmented-generation).md
O que é RAG?
RAG (Retrieval Augmented Generation) combina busca de documentos com geração de texto. Em vez de confiar apenas no conhecimento do modelo, você fornece contexto relevante.
Pergunta → Busca documentos → Passa contexto + pergunta → LLM responde
Por que usar RAG?
- Dados atualizados - Informações além do corte de conhecimento do modelo
- Dados privados - Documentos internos da empresa
- Precisão - Respostas baseadas em fontes específicas
- Citações - Pode referenciar de onde veio a informação
Arquitetura básica
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Documentos │ → │ Embeddings │ → │ Vector Store│
└─────────────┘ └─────────────┘ └─────────────┘
↓
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Resposta │ ← │ LLM │ ← │ Retriever │
└─────────────┘ └─────────────┘ └─────────────┘
Instalação
pip install langchain langchain-openai langchain-chroma
Passo 1: Carregar documentos
from langchain_community.document_loaders import (
TextLoader,
PyPDFLoader,
WebBaseLoader
)
# Texto simples
loader = TextLoader("documento.txt")
docs = loader.load()
# PDF
loader = PyPDFLoader("relatorio.pdf")
docs = loader.load()
# Página web
loader = WebBaseLoader("https://exemplo.com/artigo")
docs = loader.load()
Passo 2: Dividir em chunks
Documentos grandes precisam ser divididos:
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
chunks = splitter.split_documents(docs)
print(f"{len(chunks)} chunks criados")
Passo 3: Criar embeddings
Embeddings convertem texto em vetores numéricos:
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# Teste
vetor = embeddings.embed_query("O que é Python?")
print(f"Dimensões: {len(vetor)}") # 1536
Passo 4: Armazenar em vector store
from langchain_chroma import Chroma
# Cria vector store com os chunks
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
# Busca por similaridade
resultados = vectorstore.similarity_search("sua pergunta", k=3)
Passo 5: Criar retriever
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
# Busca documentos relevantes
docs = retriever.invoke("O que é machine learning?")
Passo 6: Montar a cadeia RAG
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
llm = ChatOpenAI(model="gpt-4o-mini")
# Prompt com contexto
prompt = ChatPromptTemplate.from_template("""
Responda a pergunta baseado apenas no contexto fornecido.
Contexto:
{context}
Pergunta: {question}
Resposta:
""")
def formatar_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# Cadeia RAG
rag_chain = (
{"context": retriever | formatar_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# Pergunta
resposta = rag_chain.invoke("Qual o tema principal do documento?")
print(resposta)
Exemplo completo
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.document_loaders import WebBaseLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# 1. Carrega
loader = WebBaseLoader("https://pt.wikipedia.org/wiki/Python")
docs = loader.load()
# 2. Divide
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = splitter.split_documents(docs)
# 3. Embeddings + Vector Store
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(chunks, embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# 4. LLM + Prompt
llm = ChatOpenAI(model="gpt-4o-mini")
prompt = ChatPromptTemplate.from_template("""
Contexto: {context}
Pergunta: {question}
Responda de forma clara e concisa:
""")
def formatar_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# 5. Cadeia RAG
rag = (
{"context": retriever | formatar_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# 6. Usa
print(rag.invoke("Quem criou o Python?"))
Vector Stores alternativos
# FAISS (local, rápido)
from langchain_community.vectorstores import FAISS
vectorstore = FAISS.from_documents(chunks, embeddings)
# Pinecone (cloud, escalável)
from langchain_pinecone import PineconeVectorStore
vectorstore = PineconeVectorStore.from_documents(
chunks, embeddings, index_name="meu-indice"
)
# Weaviate
from langchain_weaviate import WeaviateVectorStore
vectorstore = WeaviateVectorStore.from_documents(
chunks, embeddings, client=client
)
Estratégias de retrieval
Similarity Search (padrão)
retriever = vectorstore.as_retriever(search_type="similarity")
MMR (Maximum Marginal Relevance)
Diversifica resultados:
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={"k": 5, "fetch_k": 10}
)
Threshold
Filtra por score mínimo:
retriever = vectorstore.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={"score_threshold": 0.7}
)
Multi-Query Retriever
Gera múltiplas versões da pergunta para melhor busca:
from langchain.retrievers.multi_query import MultiQueryRetriever
retriever_mq = MultiQueryRetriever.from_llm(
retriever=vectorstore.as_retriever(),
llm=llm
)
Parent Document Retriever
Busca chunks, retorna documento completo:
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
store = InMemoryStore()
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=RecursiveCharacterTextSplitter(chunk_size=200),
parent_splitter=RecursiveCharacterTextSplitter(chunk_size=1000)
)
RAG com citações
from langchain_core.prompts import ChatPromptTemplate
prompt_com_citacao = ChatPromptTemplate.from_template("""
Responda a pergunta baseado no contexto.
Inclua citações entre [1], [2], etc.
Contexto:
{context}
Pergunta: {question}
Resposta (com citações):
""")
def formatar_com_indice(docs):
return "\n\n".join(
f"[{i+1}] {doc.page_content}"
for i, doc in enumerate(docs)
)
rag_citacoes = (
{"context": retriever | formatar_com_indice, "question": RunnablePassthrough()}
| prompt_com_citacao
| llm
| StrOutputParser()
)
Reranking
Reordena resultados para maior precisão:
from langchain.retrievers import ContextualCompressionRetriever
from langchain_cohere import CohereRerank
compressor = CohereRerank(model="rerank-english-v3.0")
retriever_rerank = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=vectorstore.as_retriever(search_kwargs={"k": 10})
)
Resumo
| Componente | Função |
|---|---|
| Document Loader | Carrega documentos |
| Text Splitter | Divide em chunks |
| Embeddings | Texto → vetores |
| Vector Store | Armazena e busca |
| Retriever | Interface de busca |
| RAG Chain | Junta tudo |
RAG é a forma mais comum de dar conhecimento específico a LLMs sem fine-tuning.