spinny:~/writing $ vim rag-langchain-deep-dive.md
1~2Los 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.3~4## El Problema: Las Limitaciones de los LLMs5~6Antes de hablar de RAG, es importante entender por qué la necesitamos.7~81. **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.11~12RAG aborda estos tres problemas proporcionando al modelo un **contexto relevante** recuperado de fuentes externas en el momento de la consulta.13~14## ¿Qué es RAG?15~16La 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.17~18```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```26~27## Cómo Funciona RAG en Detalle28~29La arquitectura RAG se compone de dos fases principales: **Indexación** (offline) y **Recuperación + Generación** (online).30~31### Fase 1: Indexación (Ingestión de Documentos)32~33La fase de indexación prepara tus documentos para la búsqueda semántica. Se compone de cuatro pasos.34~35```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```44~45#### 1. Carga de Documentos46~47Los 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.48~49#### 2. División del Texto (Chunking)50~51Los 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.52~53Las 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.57~58#### 3. Embedding59~60Cada 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.61~62#### 4. Vector Store63~64Los 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.65~66### Fase 2: Recuperación + Generación67~68Cuando el usuario hace una pregunta:69~701. 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.74~75## Construir una Pipeline RAG con LangChain76~77**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.78~79### Instalación80~81```bash82pip install langchain langchain-openai langchain-community chromadb83```84~85### Paso 1: Cargar Documentos86~87LangChain proporciona decenas de Document Loaders para diferentes fuentes de datos.88~89```python90from langchain_community.document_loaders import (91 PyPDFLoader,92 WebBaseLoader,93 DirectoryLoader,94 TextLoader,95)96~97# Cargar un PDF98pdf_loader = PyPDFLoader("docs/manual.pdf")99pdf_docs = pdf_loader.load()100~101# Cargar una página web102web_loader = WebBaseLoader("https://docs.example.com/guide")103web_docs = web_loader.load()104~105# Cargar todos los archivos .md de un directorio106dir_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### Paso 2: Dividir Documentos en 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"Documentos originales: {len(all_docs)}, Chunks: {len(chunks)}")125```126~127El parámetro `chunk_overlap` es fundamental: crea un solapamiento entre chunks consecutivos para que no se pierda contexto en los límites.128~129### Paso 3: Crear los Embeddings y el 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### Paso 4: Crear el Retriever145~146El retriever es el componente que, dada una consulta, recupera los chunks más relevantes del vector store.147~148```python149retriever = vectorstore.as_retriever(150 search_type="similarity",151 search_kwargs={"k": 4},152)153~154relevant_docs = retriever.invoke("¿Cómo funciona la autenticación?")155for doc in relevant_docs:156 print(doc.page_content[:200])157 print("---")158```159~160### Paso 5: Construir la Chain RAG161~162Ahora juntamos todo con un LLM y un 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("""173Responde a la pregunta basándote exclusivamente en el contexto proporcionado.174Si el contexto no contiene información suficiente, di que no lo sabes.175~176Contexto:177{context}178~179Pregunta: {question}180~181Respuesta: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("¿Cómo funciona la autenticación en el sistema?")195print(response)196```197~198## Técnicas RAG Avanzadas199~200La pipeline básica funciona bien, pero existen varias técnicas para mejorar significativamente la calidad de las respuestas.201~202### Multi-Query Retrieval203~204A 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.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("¿Cuáles son las mejores prácticas de seguridad?")215```216~217### Contextual Compression218~219No 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.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### Hybrid Search233~234La 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.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 (con Memoria)252~253Para 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.254~255```python256from langchain.chains import create_history_aware_retriever257from langchain_core.prompts import MessagesPlaceholder258~259contextualize_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])265~266history_aware_retriever = create_history_aware_retriever(267 llm, retriever, contextualize_prompt268)269```270~271## Mejores Prácticas272~2731. **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.278~279## Conclusión280~281RAG 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.282~283**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~
NORMAL · rag-langchain-deep-dive.md [readonly]288 lines · :q to close