spinny:~/writing $ vim rag-langchain-deep-dive.md
1~2Modelos de Linguagem de Grande Escala (LLMs) como GPT-4 e Claude são extraordinariamente poderosos, mas sofrem de uma limitação fundamental: seu conhecimento é congelado no momento do treinamento. Eles não podem acessar seus documentos internos, seu banco de dados ou informações em tempo real. A **Geração Aumentada por Recuperação (RAG)** resolve exatamente esse problema, combinando o poder generativo dos LLMs com a capacidade de recuperar informações de fontes externas.3~4## O Problema: Limitações dos LLMs5~6Antes de falar sobre RAG, é importante entender por que precisamos dele.7~81. **Conhecimento estático**: Um LLM só conhece o que viu durante o treinamento. Se você perguntar sobre um evento que ocorreu após seu corte, ele não pode responder.92. **Alucinações**: Quando um LLM não sabe a resposta, ele tende a inventar uma, gerando informações plausíveis mas completamente falsas.103. **Sem acesso a dados privados**: Um LLM genérico não tem acesso à documentação interna da sua empresa, tickets ou código-fonte.11~12RAG aborda todos esses três problemas fornecendo ao modelo **contexto relevante** recuperado de fontes externas no momento da consulta.13~14## O que é RAG?15~16Geração Aumentada por Recuperação é uma arquitetura que enriquece o prompt enviado a um LLM com informações recuperadas de uma base de conhecimento externa. Em vez de depender apenas do conhecimento paramétrico do modelo, RAG **busca** informações relevantes primeiro e depois as **injeta** no prompt, permitindo que o modelo gere respostas precisas e fundamentadas.17~18```mermaid19graph LR20 User["User"] -- "Question" --> Retriever21 Retriever -- "Search relevant\ndocuments" --> VectorStore["Vector Store"]22 VectorStore -- "Relevant\ndocuments" --> Retriever23 Retriever -- "Context + Question" --> LLM24 LLM -- "Grounded\nresponse" --> User25```26~27## Como RAG Funciona em Detalhe28~29A arquitetura RAG consiste em duas fases principais: **Indexação** (offline) e **Recuperação + Geração** (online).30~31### Fase 1: Indexação (Ingestão de Documentos)32~33A fase de indexação prepara seus documentos para busca semântica. Ela consiste em quatro etapas.34~35```mermaid36graph TD37 A["Documents\n(PDF, HTML, MD, DB)"] --> B["Document Loader"]38 B --> C["Text Splitter"]39 C --> D["Text Chunks"]40 D --> E["Embedding Model"]41 E --> F["Numerical Vectors"]42 F --> G["Vector Store\n(ChromaDB, Pinecone, FAISS)"]43```44~45#### 1. Carregamento de Documentos46~47Os documentos podem vir de qualquer fonte: arquivos PDF, páginas web, bancos de dados, arquivos Markdown, APIs. O **Document Loader** lê esses documentos e os converte em texto estruturado.48~49#### 2. Divisão de Texto (Chunking)50~51Os LLMs têm uma janela de contexto limitada, e os documentos podem ser muito longos. O **Text Splitter** divide os documentos em fragmentos menores chamados *chunks*. A qualidade do chunking é crítica: chunks muito pequenos perdem contexto, enquanto chunks muito grandes diluem a relevância.52~53As estratégias mais comuns são:54- **Divisão Recursiva por Caracteres**: Divide recursivamente o texto usando separadores como `\n\n`, `\n`, `. `, respeitando a estrutura do documento.55- **Divisão Semântica**: Usa embeddings para encontrar pontos de quebra naturais no texto.56- **Sobreposição de Chunks**: Inclui sobreposição entre chunks consecutivos para preservar o contexto nas fronteiras.57~58#### 3. Embedding59~60Cada chunk é transformado em um **vetor numérico** (embedding) por meio de um modelo de embedding (como o `text-embedding-3-small` da OpenAI). Esses vetores capturam o significado semântico do texto: frases com significados semelhantes terão vetores próximos no espaço multidimensional.61~62#### 4. Vector Store63~64Os vetores são salvos em um **Vector Store** (ou banco de dados vetorial), como ChromaDB, Pinecone, Weaviate ou FAISS. Este banco de dados é otimizado para **busca por similaridade**: dada uma consulta, ele encontra os vetores mais semelhantes (e portanto os chunks de texto mais relevantes).65~66### Fase 2: Recuperação + Geração67~68Quando o usuário faz uma pergunta:69~701. A pergunta é transformada em um embedding usando o mesmo modelo de embedding.712. O Vector Store encontra os chunks mais semelhantes via **busca por similaridade** (tipicamente similaridade de cosseno ou distância euclidiana).723. Os chunks recuperados são inseridos no prompt como contexto.734. O LLM gera uma resposta baseada no contexto fornecido.74~75## Construindo um Pipeline RAG com LangChain76~77**LangChain** é o framework Python (e JavaScript) mais popular para construir aplicações alimentadas por LLMs. Ele fornece abstrações de alto nível para cada componente do pipeline RAG.78~79### Instalação80~81```bash82pip install langchain langchain-openai langchain-community chromadb83```84~85### Passo 1: Carregar Documentos86~87LangChain fornece dezenas de Document Loaders para diferentes fontes de dados.88~89```python90from langchain_community.document_loaders import (91 PyPDFLoader,92 WebBaseLoader,93 DirectoryLoader,94 TextLoader,95)96~97# Load a PDF98pdf_loader = PyPDFLoader("docs/manual.pdf")99pdf_docs = pdf_loader.load()100~101# Load a web page102web_loader = WebBaseLoader("https://docs.example.com/guide")103web_docs = web_loader.load()104~105# Load all .md files from a directory106dir_loader = DirectoryLoader("./knowledge_base", glob="**/*.md", loader_cls=TextLoader)107md_docs = dir_loader.load()108~109all_docs = pdf_docs + web_docs + md_docs110```111~112### Passo 2: Dividir Documentos em Chunks113~114```python115from langchain.text_splitter import RecursiveCharacterTextSplitter116~117text_splitter = RecursiveCharacterTextSplitter(118 chunk_size=1000,119 chunk_overlap=200,120 separators=["\n\n", "\n", ". ", " ", ""],121)122~123chunks = text_splitter.split_documents(all_docs)124print(f"Original documents: {len(all_docs)}, Chunks: {len(chunks)}")125```126~127O parâmetro `chunk_overlap` é crucial: ele cria sobreposição entre chunks consecutivos para que o contexto não seja perdido nas fronteiras.128~129### Passo 3: Criar Embeddings e Vector Store130~131```python132from langchain_openai import OpenAIEmbeddings133from langchain_community.vectorstores import Chroma134~135embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")136~137vectorstore = Chroma.from_documents(138 documents=chunks,139 embedding=embedding_model,140 persist_directory="./chroma_db",141)142```143~144### Passo 4: Criar o Retriever145~146O retriever é o componente que, dada uma consulta, busca os chunks mais relevantes do vector store.147~148```python149retriever = vectorstore.as_retriever(150 search_type="similarity",151 search_kwargs={"k": 4},152)153~154relevant_docs = retriever.invoke("How does authentication work?")155for doc in relevant_docs:156 print(doc.page_content[:200])157 print("---")158```159~160### Passo 5: Construir a Cadeia RAG161~162Agora vamos juntar tudo com um LLM e um template de prompt.163~164```python165from langchain_openai import ChatOpenAI166from langchain_core.prompts import ChatPromptTemplate167from langchain_core.runnables import RunnablePassthrough168from langchain_core.output_parsers import StrOutputParser169~170llm = ChatOpenAI(model="gpt-4o", temperature=0)171~172prompt = ChatPromptTemplate.from_template("""173Answer the question based only on the provided context.174If the context does not contain enough information, say you don't know.175~176Context:177{context}178~179Question: {question}180~181Answer:182""")183~184def format_docs(docs):185 return "\n\n".join(doc.page_content for doc in docs)186~187rag_chain = (188 {"context": retriever | format_docs, "question": RunnablePassthrough()}189 | prompt190 | llm191 | StrOutputParser()192)193~194response = rag_chain.invoke("How does authentication work in the system?")195print(response)196```197~198## Técnicas Avançadas de RAG199~200O pipeline básico funciona bem, mas existem várias técnicas para melhorar significativamente a qualidade das respostas.201~202### Recuperação Multi-Query203~204Às vezes a consulta do usuário é ambígua ou não está alinhada com a linguagem usada nos documentos. O **Multi-Query Retriever** gera automaticamente variantes da pergunta original para capturar múltiplas perspectivas.205~206```python207from langchain.retrievers import MultiQueryRetriever208~209multi_retriever = MultiQueryRetriever.from_llm(210 retriever=vectorstore.as_retriever(),211 llm=llm,212)213~214docs = multi_retriever.invoke("What are the security best practices?")215```216~217### Compressão Contextual218~219Nem todo conteúdo em um chunk é relevante para a consulta. O **Contextual Compression Retriever** usa um LLM para extrair apenas as partes pertinentes de cada chunk recuperado.220~221```python222from langchain.retrievers import ContextualCompressionRetriever223from langchain.retrievers.document_compressors import LLMChainExtractor224~225compressor = LLMChainExtractor.from_llm(llm)226compression_retriever = ContextualCompressionRetriever(227 base_compressor=compressor,228 base_retriever=retriever,229)230```231~232### Busca Híbrida233~234A busca puramente semântica nem sempre é ideal. A **Busca Híbrida** combina busca semântica (embeddings) com busca lexical (BM25, correspondência de palavras-chave) para alcançar melhores resultados.235~236```python237from langchain.retrievers import EnsembleRetriever238from langchain_community.retrievers import BM25Retriever239~240bm25_retriever = BM25Retriever.from_documents(chunks)241bm25_retriever.k = 4242~243semantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})244~245hybrid_retriever = EnsembleRetriever(246 retrievers=[bm25_retriever, semantic_retriever],247 weights=[0.4, 0.6],248)249```250~251### RAG Conversacional (com Memória)252~253Para construir um chatbot RAG que lembra o contexto da conversa, é necessário adicionar memória que reformula as perguntas do usuário levando em conta o histórico da conversa.254~255```python256from langchain.chains import create_history_aware_retriever257from langchain_core.prompts import MessagesPlaceholder258~259contextualize_prompt = ChatPromptTemplate.from_messages([260 ("system", "Given the chat history and the user's latest question, "261 "reformulate the question so it is understandable without the history."),262 MessagesPlaceholder("chat_history"),263 ("human", "{input}"),264])265~266history_aware_retriever = create_history_aware_retriever(267 llm, retriever, contextualize_prompt268)269```270~271## Boas Práticas272~2731. **Escolha o tamanho certo do chunk**: Experimente com diferentes tamanhos (500-1500 tokens). Chunks menores para respostas precisas, maiores para contexto mais amplo.2742. **Use metadados dos documentos**: Adicione fonte, data e categoria como metadados aos chunks. Isso permite filtrar resultados durante a recuperação.2753. **Avalie a qualidade**: Use frameworks como [RAGAS](https://docs.ragas.io/) para medir métricas como *faithfulness*, *relevancy* e *context precision*.2764. **Gerencie atualizações de documentos**: Implemente um pipeline de re-ingestão para manter o vector store sincronizado com suas fontes de dados.2775. **Adicione um re-ranker**: Após a recuperação inicial, use um modelo de re-ranking (como Cohere Rerank) para reordenar os resultados com base na relevância real.278~279## Conclusão280~281RAG se tornou a arquitetura padrão para construir aplicações de IA que precisam de acesso a conhecimento específico e atualizado. LangChain simplifica enormemente a implementação, fornecendo abstrações para cada componente do pipeline.282~283**Próximos passos:**284- **Experimente localmente**: Comece com ChromaDB e alguns documentos para se familiarizar com o pipeline.285- **Explore o LangSmith**: Use o [LangSmith](https://smith.langchain.com/) para monitorar e depurar suas cadeias em produção.286- **Experimente diferentes modelos de embedding**: Compare modelos como `text-embedding-3-small`, `text-embedding-3-large` e modelos open-source do Sentence Transformers.287- **Consulte a documentação**: A [documentação do LangChain](https://python.langchain.com/docs/) é um recurso excelente e constantemente atualizado.288~
NORMAL · rag-langchain-deep-dive.md [readonly]288 lines · :q to close