<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Embeddings on kenji.blog</title><link>http://kenji.blog/es/tags/embeddings/</link><description>Recent content in Embeddings on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>es</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 13:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/es/tags/embeddings/index.xml" rel="self" type="application/rss+xml"/><item><title>【Introducción a la implementación de RAG】Cómo hacer que una IA local lea tus propios documentos</title><link>http://kenji.blog/es/p/rag-local-ai-implementation-guide/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/rag-local-ai-implementation-guide/</guid><description>&lt;img src="http://kenji.blog/p/rag-local-ai-implementation-guide/img/eyecatch.jpg" alt="Featured image of post 【Introducción a la implementación de RAG】Cómo hacer que una IA local lea tus propios documentos" />&lt;h1 id="introducción">Introducción
&lt;/h1>&lt;p>En los últimos años, la evolución de los Grandes Modelos de Lenguaje (LLM) ha sido notable, con inteligencias artificiales como ChatGPT y Claude infiltrándose en nuestras vidas y trabajos. Sin embargo, los LLM generales tienen una clara debilidad: solo conocen la &amp;ldquo;información pública en el momento de su entrenamiento&amp;rdquo;. Naturalmente, no pueden responder preguntas sobre &amp;ldquo;documentos privados&amp;rdquo; como reglamentos internos de la empresa, notas personales o materiales de proyectos no publicados. Si se les fuerza a responder, aumenta el riesgo de que generen mentiras plausibles que no se ajustan a los hechos (alucinaciones).&lt;/p>
&lt;p>Por lo tanto, la arquitectura tecnológica que está experimentando una adopción explosiva en todo el mundo en este momento es &lt;strong>RAG (Retrieval-Augmented Generation: Generación Aumentada por Recuperación)&lt;/strong>. Al utilizar RAG, es posible proporcionar de forma dinámica conocimientos propios al LLM desde una base de datos externa, permitiéndole generar respuestas precisas y fundamentadas basadas en dichos conocimientos.&lt;/p>
&lt;p>Además, al manejar información confidencial de empresas o individuos, el envío de datos a API basadas en la nube como las de OpenAI a menudo no está permitido por las políticas de seguridad. Lo que se necesita en estos casos es la construcción de un &amp;ldquo;RAG local&amp;rdquo; combinado con una &lt;strong>IA local&lt;/strong> (un LLM que opera de manera autónoma en tu propia PC o en un servidor on-premise).&lt;/p>
&lt;p>En este artículo, explicaremos exhaustivamente desde la teoría fundamental de RAG hasta métodos específicos para implementar un RAG local con Python, los antecedentes matemáticos (cómo funciona la búsqueda vectorial) y técnicas avanzadas para poner el sistema en producción.&lt;/p>
&lt;hr>
&lt;h1 id="1-arquitectura-general-de-rag">1. Arquitectura general de RAG
&lt;/h1>&lt;p>RAG no es un modelo de IA único, sino una arquitectura de sistema en la que múltiples componentes colaboran entre sí. Se divide principalmente en dos fases: la &amp;ldquo;Fase de ingesta (importación de datos)&amp;rdquo; y la &amp;ldquo;Fase de recuperación y generación (búsqueda y generación)&amp;rdquo;.&lt;/p>
&lt;p>El siguiente diagrama de Mermaid muestra la imagen general del sistema RAG.&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Fase de Ingesta (Preparación previa)"
Doc["Documentos propios (PDF, TXT, etc.)"] --> Loader["Cargador de documentos"]
Loader --> Splitter["División de texto (Chunking)"]
Splitter --> EmbedModel1["Modelo de incrustación (Embedding)"]
EmbedModel1 --> VectorDB["Base de datos vectorial"]
end
subgraph "Fase de Inferencia (Al consultar el usuario)"
User["Pregunta del usuario (Consulta)"] --> EmbedModel2["Modelo de incrustación (Embedding)"]
EmbedModel2 --> QueryVector["Vector de consulta"]
QueryVector --> Search["Búsqueda de similitud (Búsqueda vectorial)"]
VectorDB --> Search
Search --> Context["Extracción de fragmentos relevantes (Contexto)"]
User --> PromptBuilder["Construcción del prompt"]
Context --> PromptBuilder
PromptBuilder --> LocalLLM["LLM Local"]
LocalLLM --> Answer["Generación de respuesta final"]
end&lt;/div>
&lt;h2 id="fase-de-ingesta-preparación-previa">Fase de ingesta (Preparación previa)
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Carga de documentos&lt;/strong>: Se leen datos no estructurados como archivos PDF, Word o de texto.&lt;/li>
&lt;li>&lt;strong>Chunking (División de texto)&lt;/strong>: Se dividen los textos largos en fragmentos significativos (chunks) para que se ajusten al límite de entrada del LLM (ventana de contexto) y para mejorar la precisión de la búsqueda.&lt;/li>
&lt;li>&lt;strong>Embedding (Vectorización)&lt;/strong>: Se introducen los fragmentos divididos en un modelo de incrustación (Embedding Model) y se convierten en matrices numéricas (vectores) de cientos a miles de dimensiones.&lt;/li>
&lt;li>&lt;strong>Almacenamiento en la base de datos&lt;/strong>: Los vectores convertidos se vinculan con los datos de texto originales y se guardan en una base de datos vectorial (Vector DB).&lt;/li>
&lt;/ol>
&lt;h2 id="fase-de-inferencia-tiempo-de-ejecución">Fase de inferencia (Tiempo de ejecución)
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Vectorización de la consulta&lt;/strong>: Se vectoriza la pregunta del usuario usando el mismo modelo de incrustación de la fase de preparación.&lt;/li>
&lt;li>&lt;strong>Búsqueda de similitud&lt;/strong>: Se calcula la similitud entre el vector de consulta y los vectores de los documentos en la base de datos para recuperar los fragmentos de texto con mayor relevancia (cercanía semántica).&lt;/li>
&lt;li>&lt;strong>Construcción del prompt&lt;/strong>: Los textos relevantes recuperados se combinan con la pregunta del usuario como &amp;ldquo;contexto (conocimiento de fondo)&amp;rdquo; para crear el prompt de entrada para el LLM.&lt;/li>
&lt;li>&lt;strong>Generación de la respuesta&lt;/strong>: El LLM, tras recibir el prompt aumentado, genera una respuesta basándose en la información de contexto proporcionada.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="2-entendimiento-profundo-de-la-búsqueda-vectorial-y-las-incrustaciones-embeddings">2. Entendimiento profundo de la búsqueda vectorial y las incrustaciones (Embeddings)
&lt;/h1>&lt;p>El núcleo de RAG es la &amp;ldquo;búsqueda vectorial (búsqueda semántica)&amp;rdquo;. Mientras que la búsqueda por palabras clave tradicional (como BM25) se basa en coincidencias exactas y frecuencias de palabras, la búsqueda vectorial se basa en la &amp;ldquo;similitud de significado&amp;rdquo;. Por ejemplo, palabras diferentes como &amp;ldquo;perro&amp;rdquo; y &amp;ldquo;cachorro&amp;rdquo;, o &amp;ldquo;PC&amp;rdquo; y &amp;ldquo;ordenador&amp;rdquo;, aparecerán en los resultados si su significado es parecido.&lt;/p>
&lt;h2 id="qué-es-un-modelo-de-incrustación-embedding-model">¿Qué es un modelo de incrustación (Embedding Model)?
&lt;/h2>&lt;p>Un modelo de incrustación es una red neuronal que toma textos en lenguaje natural como entrada y emite un vector denso (Dense Vector) de longitud fija. Los modelos comunes (como &lt;code>text-embedding-3-small&lt;/code> o el de código abierto &lt;code>multilingual-e5-large&lt;/code>) mapean el texto a vectores de números reales de 384 o 1024 dimensiones.&lt;/p>
&lt;p>En este espacio multidimensional (espacio latente), el modelo está entrenado para que los textos con significados similares tengan distancias más cortas en las coordenadas espaciales.&lt;/p>
&lt;h2 id="antecedentes-matemáticos-del-cálculo-de-similitud-similitud-del-coseno">Antecedentes matemáticos del cálculo de similitud: Similitud del coseno
&lt;/h2>&lt;p>Cuando una base de datos vectorial busca documentos relevantes, la métrica de distancia más comúnmente utilizada es la &lt;strong>similitud del coseno (Cosine Similarity)&lt;/strong>. A diferencia de la distancia euclidiana (distancia espacial absoluta), la similitud del coseno se centra en el &amp;ldquo;ángulo entre dos vectores&amp;rdquo;. Es altamente adecuada para calcular la similitud de textos, ya que no se ve tan afectada por la longitud del texto (la norma del vector).&lt;/p>
&lt;p>Expresado matemáticamente, la similitud del coseno entre los vectores $\mathbf{A}$ y $\mathbf{B}$ es la siguiente:&lt;/p>
$$ \text{Cosine Similarity}(\mathbf{A}, \mathbf{B}) = \cos(\theta) = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \|\mathbf{B}\|} = \frac{\sum_{i=1}^{n} A_i B_i}{\sqrt{\sum_{i=1}^{n} A_i^2} \sqrt{\sum_{i=1}^{n} B_i^2}} $$
&lt;ul>
&lt;li>$\mathbf{A} \cdot \mathbf{B}$ representa el producto escalar (Dot Product).&lt;/li>
&lt;li>$\|\mathbf{A}\|$ representa la norma L2 (longitud) del vector $\mathbf{A}$.&lt;/li>
&lt;li>$n$ es el número de dimensiones del vector.&lt;/li>
&lt;/ul>
&lt;p>La similitud del coseno toma valores entre -1 y 1.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Cercano a 1&lt;/strong>: La dirección de los dos vectores es casi la misma (sus significados son muy similares).&lt;/li>
&lt;li>&lt;strong>Cercano a 0&lt;/strong>: Los dos vectores son ortogonales (no están relacionados).&lt;/li>
&lt;li>&lt;strong>Cercano a -1&lt;/strong>: Los dos vectores apuntan en direcciones opuestas (significados opuestos).&lt;/li>
&lt;/ul>
&lt;p>Las bases de datos vectoriales recientes (Chroma, FAISS, Qdrant, etc.) adoptan un algoritmo de búsqueda de vecino más cercano aproximado (ANN) llamado HNSW (Hierarchical Navigable Small World), que está optimizado para buscar documentos con una alta similitud de coseno entre millones de datos vectoriales en milisegundos.&lt;/p>
&lt;hr>
&lt;h1 id="3-pila-tecnológica-para-construir-un-rag-local">3. Pila tecnológica para construir un RAG local
&lt;/h1>&lt;p>Para construir un RAG local completo que no dependa de la nube, aprovechamos el ecosistema de código abierto. A continuación se presentan las pilas tecnológicas recomendadas:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Modelo de lenguaje (LLM)&lt;/strong>
&lt;ul>
&lt;li>Herramienta: &lt;code>Ollama&lt;/code> o &lt;code>Llama.cpp&lt;/code>&lt;/li>
&lt;li>Modelo: Modelos abiertos ligeros y de alto rendimiento como &lt;code>Llama-3-8B-Instruct&lt;/code>, &lt;code>Gemma-2-9B-It&lt;/code>, &lt;code>Qwen2-7B-Instruct&lt;/code>. Para tareas en japonés, son adecuados modelos ajustados al japonés como &lt;code>Llama-3-ELYZA-JP-8B&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Modelo de incrustación (Embedding)&lt;/strong>
&lt;ul>
&lt;li>Modelo: &lt;code>intfloat/multilingual-e5-large&lt;/code> o &lt;code>BAAI/bge-m3&lt;/code>. Si se ejecuta localmente, lo común es descargarlo desde Hugging Face y ejecutarlo con Sentence-Transformers.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Base de datos vectorial (Vector DB)&lt;/strong>
&lt;ul>
&lt;li>&lt;code>ChromaDB&lt;/code>: Basado en Python y extremadamente fácil de configurar. Ideal para el desarrollo local.&lt;/li>
&lt;li>&lt;code>FAISS&lt;/code>: Una biblioteca de búsqueda vectorial rápida desarrollada por Meta.&lt;/li>
&lt;li>&lt;code>Qdrant&lt;/code> / &lt;code>Milvus&lt;/code>: Orientado a entornos de producción de mayor escala.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Marco de orquestación&lt;/strong>
&lt;ul>
&lt;li>&lt;code>LangChain&lt;/code>: El estándar de facto para encadenar componentes (Chain).&lt;/li>
&lt;li>&lt;code>LlamaIndex&lt;/code>: Un marco de conexión de datos especializado especialmente en RAG.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>En esta ocasión, lo implementaremos con la combinación más sencilla de introducir: &lt;strong>LangChain + ChromaDB + Ollama + HuggingFaceEmbeddings&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="4-tutorial-de-implementación-construcción-de-un-rag-local-completo-con-python">4. Tutorial de implementación: Construcción de un RAG local completo con Python
&lt;/h1>&lt;p>A partir de aquí, construiremos el RAG local mientras escribimos código Python real. Asegúrate de tener Ollama instalado en tu PC y ejecutándose en segundo plano con antelación. Además, obtén un modelo en Ollama (ej. &lt;code>ollama run llama3&lt;/code>).&lt;/p>
&lt;h2 id="paso-1-instalación-de-las-bibliotecas-necesarias">Paso 1: Instalación de las bibliotecas necesarias
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install langchain langchain-community langchain-huggingface
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install chromadb sentence-transformers pypdf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="paso-2-visión-general-del-código-de-implementación">Paso 2: Visión general del código de implementación
&lt;/h2>&lt;p>A continuación, se muestra un script de Python completo para leer un archivo PDF, vectorizarlo y hacer que un LLM local responda preguntas basadas en él.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.document_loaders&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PyPDFLoader&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_text_splitters&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RecursiveCharacterTextSplitter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_huggingface&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">HuggingFaceEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.vectorstores&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Chroma&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.llms&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Ollama&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.chains&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RetrievalQA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. Cargar el documento&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Cargando el documento...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Especifica la ruta del PDF que deseas cargar&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">file_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;sample_company_policy.pdf&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">loader&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PyPDFLoader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loader&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. División en fragmentos (Text Splitting)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Divide en un tamaño adecuado para no romper el significado del texto&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">text_splitter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RecursiveCharacterTextSplitter&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunk_size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Número máximo de caracteres por fragmento&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunk_overlap&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Número de caracteres superpuestos con el fragmento anterior y siguiente (evita la ruptura del contexto)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">separators&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;、&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34; &amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunks&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">text_splitter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split_documents&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">documents&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Se ha dividido en &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> fragmentos.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. Inicialización del modelo de incrustación (Local HuggingFace Model)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Usando un modelo multilingüe fuerte en japonés (y otros idiomas)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Cargando el modelo de incrustación...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">HuggingFaceEmbeddings&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;intfloat/multilingual-e5-large&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;device&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;cpu&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="c1"># Si tienes GPU, usa &amp;#39;cuda&amp;#39; o &amp;#39;mps&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. Construcción de la base de datos vectorial (Chroma)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Construyendo la base de datos vectorial...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./chroma_db&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Chroma&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_documents&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">embeddings&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">persist_directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Creación del recuperador (Retriever). Configurado para obtener los 3 mejores documentos relevantes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retriever&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">as_retriever&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">search_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;k&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 5. Inicialización del LLM local (Ollama)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Conectando al LLM local...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Asegúrate de haber obtenido el modelo de antemano con &amp;#39;ollama pull llama3&amp;#39; o similar&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ollama&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;llama3&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 6. Definición de la plantilla del prompt&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt_template&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Eres un asistente excelente, experto en los reglamentos y la información interna de la empresa.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Usa únicamente el siguiente contexto (información de fondo) para responder a la pregunta del usuario detalladamente en español.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Si no puedes encontrar la respuesta en el contexto, no adivines; responde honestamente con un &amp;#34;No se puede determinar a partir de la información proporcionada&amp;#34;.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【Contexto】
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{context}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【Pregunta】
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{question}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【Respuesta】:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PROMPT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">prompt_template&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">input_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;context&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;question&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 7. Construcción de la cadena RAG&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qa_chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RetrievalQA&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_chain_type&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chain_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;stuff&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retriever&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">retriever&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">return_source_documents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Configura si deseas devolver la fuente de información&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chain_type_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;prompt&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">PROMPT&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 8. Ejecución de la pregunta&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Cuéntame sobre las condiciones de pago de gastos de transporte para el trabajo remoto.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">Pregunta: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">qa_chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;query&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【Respuesta】&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;result&amp;#39;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【Fuentes consultadas】&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">doc&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;source_documents&amp;#39;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;- Página &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;page&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;desconocido&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">page_content&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="explicación-de-los-puntos-clave-del-código">Explicación de los puntos clave del código
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>RecursiveCharacterTextSplitter&lt;/strong>:
Es el separador más recomendado para dividir el lenguaje natural. Intenta dividir en el orden de párrafo (&lt;code>\n\n&lt;/code>), línea (&lt;code>\n&lt;/code>) y punto (&lt;code>。&lt;/code>), dividiendo el texto para que se ajuste al &lt;code>chunk_size&lt;/code> especificado mientras se mantiene el significado en la medida de lo posible. Al configurar el &lt;code>chunk_overlap&lt;/code>, se evita que se rompa el límite del contexto y se pierda información.&lt;/li>
&lt;li>&lt;strong>HuggingFaceEmbeddings&lt;/strong>:
&lt;code>intfloat/multilingual-e5-large&lt;/code> es un modelo de incrustación de código abierto muy potente que soporta múltiples idiomas. Permite vectorizar texto en la memoria local sin conexión, sin utilizar una API en la nube (como &lt;code>text-embedding-ada-002&lt;/code> de OpenAI).&lt;/li>
&lt;li>&lt;strong>ChromaDB&lt;/strong>:
Dado que funciona en memoria o en el almacenamiento local (basado en SQLite), no requiere la configuración de un servidor de base de datos complejo. Al especificar un &lt;code>persist_directory&lt;/code>, se puede omitir el proceso de vectorización en ejecuciones posteriores y cargar la base de datos desde el disco.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="5-técnicas-avanzadas-de-rag-advanced-rag-techniques">5. Técnicas avanzadas de RAG (Advanced RAG Techniques)
&lt;/h1>&lt;p>El sistema básico de RAG (Naive RAG) construido en el tutorial anterior funcionará, pero si se requiere una alta precisión de respuesta en un entorno de producción, será necesario introducir técnicas avanzadas como las siguientes.&lt;/p>
&lt;h2 id="51-búsqueda-híbrida-hybrid-search">5.1 Búsqueda Híbrida (Hybrid Search)
&lt;/h2>&lt;p>La búsqueda vectorial es buena capturando el &amp;ldquo;significado&amp;rdquo;, pero a veces puede tener dificultades con búsquedas estrictas de palabras clave como &amp;ldquo;nombres propios específicos&amp;rdquo;, &amp;ldquo;números de modelo de productos&amp;rdquo; o &amp;ldquo;identificadores de empleados&amp;rdquo;.
Por lo tanto, se puede reducir drásticamente las omisiones de búsqueda ejecutando en paralelo la &lt;strong>búsqueda semántica&lt;/strong> basada en vectores y la &lt;strong>búsqueda por palabras clave&lt;/strong> usando algoritmos como BM25, y luego integrando los resultados de ambos (usando técnicas como Reciprocal Rank Fusion; RRF).&lt;/p>
&lt;h2 id="52-reclasificación-re-ranking">5.2 Reclasificación (Re-ranking)
&lt;/h2>&lt;p>La búsqueda vectorial es rápida, pero no siempre evalúa la idoneidad contextual exacta. Una canalización general para mejorar la precisión de la búsqueda es la siguiente:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Búsqueda inicial (First-stage Retrieval)&lt;/strong>: Se recuperan de 20 a 30 fragmentos relevantes de la base de datos vectorial, buscando de forma amplia y superficial.&lt;/li>
&lt;li>&lt;strong>Reevaluación (Re-ranking)&lt;/strong>: Se utiliza otro modelo de aprendizaje automático más pesado llamado Cross-Encoder (por ejemplo, &lt;code>bge-reranker&lt;/code>) para introducir los pares de la consulta del usuario y los fragmentos recuperados, y recalcular la puntuación de adecuación semántica.&lt;/li>
&lt;li>&lt;strong>Selección&lt;/strong>: Solo los 3 a 5 mejores resultados con las puntuaciones más altas se pasan como contexto final al prompt del LLM.&lt;/li>
&lt;/ol>
&lt;p>Con este enfoque, se evita pasar información de ruido irrelevante al LLM, aumentando significativamente la precisión (Precision) de la respuesta.&lt;/p>
&lt;div class="mermaid">graph LR
Query["Consulta"] --> VSearch["Búsqueda vectorial (Top 20)"]
VSearch --> Reranker["Modelo reclasificador (Cross-Encoder)"]
Query --> Reranker
Reranker --> TopK["Top 3 de alta precisión"]
TopK --> LLM["Generación por LLM"]&lt;/div>
&lt;h2 id="53-chunking-semántico-y-búsqueda-de-documentos-primarios">5.3 Chunking semántico y búsqueda de documentos primarios
&lt;/h2>&lt;p>Existe una técnica llamada &amp;ldquo;Semantic Chunking&amp;rdquo; (división semántica) que en lugar de dividir el texto mecánicamente con un número fijo de caracteres, utiliza IA para detectar cambios en el significado de las oraciones y dividir allí.
Además, la técnica del &amp;ldquo;Parent Document Retriever&amp;rdquo; (Búsqueda de documentos primarios) permite lograr búsquedas altamente precisas vectorizando unidades muy pequeñas (como oraciones) para la búsqueda, pero al pasarlo al LLM, proporciona el &amp;ldquo;párrafo original grande (documento primario)&amp;rdquo; que contiene esa oración, entregando un contexto suficiente al LLM.&lt;/p>
&lt;hr>
&lt;h1 id="6-retos-y-contramedidas-al-operar-un-rag-local">6. Retos y contramedidas al operar un RAG local
&lt;/h1>&lt;p>Existen obstáculos específicos al construir y operar RAG en un entorno local.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Agotamiento de VRAM (Memoria de video)&lt;/strong>:
Para ejecutar un LLM local a una velocidad práctica (decenas de tokens por segundo), es necesario cargar el modelo en la VRAM de la GPU. Ejecutar un modelo de clase 8B en fp16 (punto flotante de 16 bits) requiere aproximadamente 16 GB de VRAM. Sin embargo, al usar tecnología de &lt;strong>cuantización (Quantization)&lt;/strong> (compresión a 4 bits u 8 bits, como los formatos GGUF o AWQ), es posible que funcione lo suficientemente rápido incluso con 8 GB de VRAM (como en una PC de juegos típica). Llama.cpp y Ollama son compatibles con estos formatos cuantizados por defecto.&lt;/li>
&lt;li>&lt;strong>Límite de la ventana de contexto&lt;/strong>:
Si la cantidad de contexto recuperado es demasiada, puede exceder el límite de entrada (límite de tokens) del LLM o causar que el modelo olvide la información en el medio (fenómeno de &amp;ldquo;Lost in the middle&amp;rdquo;). Es esencial ajustar el número de fragmentos a extraer y realizar una selección rigurosa utilizando la técnica de reclasificación mencionada anteriormente.&lt;/li>
&lt;li>&lt;strong>Gestión de la frescura de los datos&lt;/strong>:
Si se actualiza el documento de origen, los vectores correspondientes en la base de datos vectorial también deben ser actualizados o eliminados (operaciones CRUD). Dado que ChromaDB soporta actualizaciones basadas en el ID del documento, es práctico gestionar los valores hash de los archivos y establecer un proceso por lotes que sincronice solo las diferencias.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="conclusión">Conclusión
&lt;/h1>&lt;p>RAG (Generación Aumentada por Recuperación) es un potente paradigma que evoluciona a la IA de ser un asistente generalizado a un &amp;ldquo;experto personal exclusivo&amp;rdquo; o un &amp;ldquo;especialista en tareas internas de la empresa&amp;rdquo;.&lt;/p>
&lt;p>Hemos visto que incluso con requisitos altamente confidenciales donde no se pueden usar servicios en la nube, es relativamente fácil construir un entorno &amp;ldquo;RAG local&amp;rdquo; completo combinando ecosistemas de código abierto como Ollama, LangChain y ChromaDB.&lt;/p>
&lt;p>Basándote en el entendimiento matemático del espacio vectorial explicado en este artículo, y enfoques avanzados como la división de texto y la reclasificación, te animamos a desarrollar tu propio sistema de IA utilizando tus propios datos. La velocidad de evolución de la IA local es asombrosa, y el sistema que construyes hoy puede ser actualizado instantáneamente reemplazando el modelo por uno más ligero e inteligente que aparezca mañana.&lt;/p>
&lt;hr>
&lt;p>&lt;em>En este blog continuaremos publicando artículos en profundidad sobre la tecnología de IA y RAG. Si tienes preguntas o comentarios, por favor déjalos en la sección de comentarios.&lt;/em>&lt;/p></description></item></channel></rss>