spinny:~/writing $ less rag-langchain-deep-dive.md
12Los Large Language Models (LLMs) como GPT-4 y Claude son extraordinariamente potentes, pero sufren de una limitación fundamental: su conocimiento está congelado en el momento del entrenamiento. No pueden acceder a tus documentos internos, tu base de datos o información en tiempo real. La **Retrieval-Augmented Generation (RAG)** resuelve exactamente este problema, combinando el poder generativo de los LLMs con la capacidad de recuperar información de fuentes externas.34## El Problema: Las Limitaciones de los LLMs56Antes de hablar de RAG, es importante entender por qué la necesitamos.781. **Conocimiento estático**: Un LLM solo sabe lo que vio durante el entrenamiento. Si le preguntas sobre un evento posterior a su fecha de corte, no puede responder.92. **Alucinaciones**: Cuando un LLM no conoce la respuesta, tiende a inventarla, generando información plausible pero completamente falsa.103. **Sin acceso a datos privados**: Un LLM genérico no tiene acceso a la documentación interna de tu empresa, tickets o tu base de código.1112RAG aborda estos tres problemas proporcionando al modelo un **contexto relevante** recuperado de fuentes externas en el momento de la consulta.1314## ¿Qué es RAG?1516La Retrieval-Augmented Generation es una arquitectura que enriquece el prompt enviado a un LLM con información recuperada de una base de conocimiento externa. En lugar de depender únicamente del conocimiento paramétrico del modelo, RAG **busca** primero la información relevante y luego la **inyecta** en el prompt, permitiendo al modelo generar respuestas precisas y fundamentadas.1718```mermaid19graph LR20 User["Usuario"] -- "Pregunta" --> Retriever21 Retriever -- "Busca documentos\nrelevantes" --> VectorStore["Vector Store"]22 VectorStore -- "Documentos\nrelevantes" --> Retriever23 Retriever -- "Contexto + Pregunta" --> LLM24 LLM -- "Respuesta\nfundamentada" --> User25```2627## Cómo Funciona RAG en Detalle2829La arquitectura RAG se compone de dos fases principales: **Indexación** (offline) y **Recuperación + Generación** (online).3031### Fase 1: Indexación (Ingestión de Documentos)3233La fase de indexación prepara tus documentos para la búsqueda semántica. Se compone de cuatro pasos.3435```mermaid36graph TD37 A["Documentos\n(PDF, HTML, MD, DB)"] --> B["Document Loader"]38 B --> C["Text Splitter"]39 C --> D["Chunks de Texto"]40 D --> E["Modelo de Embedding"]41 E --> F["Vectores Numéricos"]42 F --> G["Vector Store\n(ChromaDB, Pinecone, FAISS)"]43```4445#### 1. Carga de Documentos4647Los documentos pueden provenir de cualquier fuente: archivos PDF, páginas web, bases de datos, archivos Markdown, APIs. El **Document Loader** lee estos documentos y los convierte en texto estructurado.4849#### 2. División del Texto (Chunking)5051Los LLMs tienen una ventana de contexto limitada, y los documentos pueden ser muy largos. El **Text Splitter** divide los documentos en fragmentos más pequeños llamados *chunks*. La calidad del chunking es crítica: chunks demasiado pequeños pierden contexto, chunks demasiado grandes diluyen la relevancia.5253Las estrategias más comunes son:54- **Recursive Character Splitting**: Divide el texto recursivamente usando separadores como `\n\n`, `\n`, `. `, respetando la estructura del documento.55- **Semantic Splitting**: Usa los embeddings para encontrar los puntos de ruptura naturales en el texto.56- **Chunk Overlap**: Incluye un solapamiento entre chunks consecutivos para preservar el contexto en los límites.5758#### 3. Embedding5960Cada chunk se transforma en un **vector numérico** (embedding) mediante un modelo de embedding (como `text-embedding-3-small` de OpenAI). Estos vectores capturan el significado semántico del texto: frases con significados similares tendrán vectores cercanos en el espacio multidimensional.6162#### 4. Vector Store6364Los vectores se almacenan en un **Vector Store** (o base de datos vectorial), como ChromaDB, Pinecone, Weaviate o FAISS. Esta base de datos está optimizada para la **búsqueda por similitud**: dada una consulta, encuentra los vectores (y por tanto los chunks de texto) más similares.6566### Fase 2: Recuperación + Generación6768Cuando el usuario hace una pregunta:69701. La pregunta se transforma en un embedding usando el mismo modelo de embedding.712. El Vector Store encuentra los chunks más similares mediante **búsqueda por similitud** (típicamente similitud coseno o distancia euclidiana).723. Los chunks recuperados se insertan en el prompt como contexto.734. El LLM genera la respuesta basándose en el contexto proporcionado.7475## Construir una Pipeline RAG con LangChain7677**LangChain** es el framework Python (y JavaScript) más popular para construir aplicaciones basadas en LLMs. Ofrece abstracciones de alto nivel para cada componente de la pipeline RAG.7879### Instalación8081```bash82pip install langchain langchain-openai langchain-community chromadb83```8485### Paso 1: Cargar Documentos8687LangChain proporciona decenas de Document Loaders para diferentes fuentes de datos.8889```python90from langchain_community.document_loaders import (91 PyPDFLoader,92 WebBaseLoader,93 DirectoryLoader,94 TextLoader,95)9697# Cargar un PDF98pdf_loader = PyPDFLoader("docs/manual.pdf")99pdf_docs = pdf_loader.load()100101# Cargar una página web102web_loader = WebBaseLoader("https://docs.example.com/guide")103web_docs = web_loader.load()104105# Cargar todos los archivos .md de un directorio106dir_loader = DirectoryLoader("./knowledge_base", glob="**/*.md", loader_cls=TextLoader)107md_docs = dir_loader.load()108109all_docs = pdf_docs + web_docs + md_docs110```111112### Paso 2: Dividir Documentos en Chunks113114```python115from langchain.text_splitter import RecursiveCharacterTextSplitter116117text_splitter = RecursiveCharacterTextSplitter(118 chunk_size=1000,119 chunk_overlap=200,120 separators=["\n\n", "\n", ". ", " ", ""],121)122123chunks = text_splitter.split_documents(all_docs)124print(f"Documentos originales: {len(all_docs)}, Chunks: {len(chunks)}")125```126127El parámetro `chunk_overlap` es fundamental: crea un solapamiento entre chunks consecutivos para que no se pierda contexto en los límites.128129### Paso 3: Crear los Embeddings y el Vector Store130131```python132from langchain_openai import OpenAIEmbeddings133from langchain_community.vectorstores import Chroma134135embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")136137vectorstore = Chroma.from_documents(138 documents=chunks,139 embedding=embedding_model,140 persist_directory="./chroma_db",141)142```143144### Paso 4: Crear el Retriever145146El retriever es el componente que, dada una consulta, recupera los chunks más relevantes del vector store.147148```python149retriever = vectorstore.as_retriever(150 search_type="similarity",151 search_kwargs={"k": 4},152)153154relevant_docs = retriever.invoke("¿Cómo funciona la autenticación?")155for doc in relevant_docs:156 print(doc.page_content[:200])157 print("---")158```159160### Paso 5: Construir la Chain RAG161162Ahora juntamos todo con un LLM y un template de prompt.163164```python165from langchain_openai import ChatOpenAI166from langchain_core.prompts import ChatPromptTemplate167from langchain_core.runnables import RunnablePassthrough168from langchain_core.output_parsers import StrOutputParser169170llm = ChatOpenAI(model="gpt-4o", temperature=0)171172prompt = ChatPromptTemplate.from_template("""173Responde a la pregunta basándote exclusivamente en el contexto proporcionado.174Si el contexto no contiene información suficiente, di que no lo sabes.175176Contexto:177{context}178179Pregunta: {question}180181Respuesta:182""")183184def format_docs(docs):185 return "\n\n".join(doc.page_content for doc in docs)186187rag_chain = (188 {"context": retriever | format_docs, "question": RunnablePassthrough()}189 | prompt190 | llm191 | StrOutputParser()192)193194response = rag_chain.invoke("¿Cómo funciona la autenticación en el sistema?")195print(response)196```197198## Técnicas RAG Avanzadas199200La pipeline básica funciona bien, pero existen varias técnicas para mejorar significativamente la calidad de las respuestas.201202### Multi-Query Retrieval203204A veces la consulta del usuario es ambigua o no está alineada con el lenguaje usado en los documentos. El **Multi-Query Retriever** genera automáticamente variantes de la pregunta original para capturar múltiples perspectivas.205206```python207from langchain.retrievers import MultiQueryRetriever208209multi_retriever = MultiQueryRetriever.from_llm(210 retriever=vectorstore.as_retriever(),211 llm=llm,212)213214docs = multi_retriever.invoke("¿Cuáles son las mejores prácticas de seguridad?")215```216217### Contextual Compression218219No todo el contenido de un chunk es relevante para la consulta. El **Contextual Compression Retriever** usa un LLM para extraer solo las partes pertinentes de cada chunk recuperado.220221```python222from langchain.retrievers import ContextualCompressionRetriever223from langchain.retrievers.document_compressors import LLMChainExtractor224225compressor = LLMChainExtractor.from_llm(llm)226compression_retriever = ContextualCompressionRetriever(227 base_compressor=compressor,228 base_retriever=retriever,229)230```231232### Hybrid Search233234La búsqueda puramente semántica no siempre es óptima. La **Hybrid Search** combina la búsqueda semántica (embeddings) con la búsqueda léxica (BM25, coincidencia de palabras clave) para obtener mejores resultados.235236```python237from langchain.retrievers import EnsembleRetriever238from langchain_community.retrievers import BM25Retriever239240bm25_retriever = BM25Retriever.from_documents(chunks)241bm25_retriever.k = 4242243semantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})244245hybrid_retriever = EnsembleRetriever(246 retrievers=[bm25_retriever, semantic_retriever],247 weights=[0.4, 0.6],248)249```250251### RAG Conversacional (con Memoria)252253Para construir un chatbot RAG que recuerde el contexto de la conversación, es necesario añadir una memoria que reformule las preguntas del usuario teniendo en cuenta el historial.254255```python256from langchain.chains import create_history_aware_retriever257from langchain_core.prompts import MessagesPlaceholder258259contextualize_prompt = ChatPromptTemplate.from_messages([260 ("system", "Dado el historial del chat y la última pregunta del usuario, "261 "reformula la pregunta para que sea comprensible sin el historial."),262 MessagesPlaceholder("chat_history"),263 ("human", "{input}"),264])265266history_aware_retriever = create_history_aware_retriever(267 llm, retriever, contextualize_prompt268)269```270271## Mejores Prácticas2722731. **Elige el tamaño correcto de chunk**: Experimenta con diferentes tamaños (500-1500 tokens). Chunks más pequeños para respuestas precisas, más grandes para contexto amplio.2742. **Usa metadatos en los documentos**: Añade fuente, fecha y categoría como metadatos a los chunks. Esto permite filtrar los resultados durante la recuperación.2753. **Evalúa la calidad**: Usa frameworks como [RAGAS](https://docs.ragas.io/) para medir métricas como *faithfulness*, *relevancy* y *context precision*.2764. **Gestiona las actualizaciones de documentos**: Implementa una pipeline de re-ingestión para mantener el vector store sincronizado con tus fuentes de datos.2775. **Añade un re-ranker**: Después de la recuperación inicial, usa un modelo de re-ranking (como Cohere Rerank) para reordenar los resultados según la relevancia real.278279## Conclusión280281RAG se ha convertido en la arquitectura estándar para construir aplicaciones de IA que necesitan acceso a conocimientos específicos y actualizados. LangChain simplifica enormemente la implementación, proporcionando abstracciones para cada componente de la pipeline.282283**Próximos pasos:**284- **Experimenta en local**: Comienza con ChromaDB y pocos documentos para familiarizarte con la pipeline.285- **Explora LangSmith**: Usa [LangSmith](https://smith.langchain.com/) para monitorear y depurar tus chains en producción.286- **Prueba diferentes modelos de embedding**: Compara modelos como `text-embedding-3-small`, `text-embedding-3-large` y modelos open-source de Sentence Transformers.287- **Consulta la documentación**: La [documentación de LangChain](https://python.langchain.com/docs/) es un recurso excelente y en constante actualización.288
:RAG y LangChain: Guía Completa de Retrieval-Augmented Generationlines 1-288 (END) — press q to close