Cómo construir un sistema RAG robusto con recursos mínimos

En este artículo, aprenderá a diseñar, ensamblar y ajustar un sistema de generación de recuperación aumentada que se ejecuta completamente en una computadora portátil estándar, sin infraestructura de nube ni API pagas.

Los temas que cubriremos incluyen:

Cómo la cuantificación, los modelos de incrustación compactos y los almacenes de vectores en proceso hacen posible una canalización RAG completa en hardware de consumo. Qué paquetes livianos manejan cada etapa del proceso, desde la ingestión y fragmentación de documentos hasta la recuperación, las indicaciones y la generación local. Cómo hacer que el sistema sea confiable mediante citas de fuentes, umbrales de recuperación, conjuntos de evaluación y registros de consultas que distingan las fallas de recuperación de las fallas de generación.

Introducción

La generación de recuperación aumentada, o RAG, conecta un modelo de lenguaje con su propia colección de documentos para que responda a partir de su material en lugar de adivinar. La mayoría de las guías de compilación asumen una GPU en la nube, una base de datos vectorial alojada y una API paga que le cobra por cada pregunta. Nada de eso es necesario. Una computadora portátil con 8 GB o 16 GB de RAM puede ejecutar un sistema RAG completo que permanece fuera de línea, no cuesta nada por consulta y guarda documentos confidenciales en su propia máquina.

Esta guía cubre la arquitectura y las opciones de paquete que hacen que una configuración pequeña se mantenga firme en lugar de caerse. No hay ningún código aquí a propósito. Un sistema RAG en funcionamiento abarca la carga, fragmentación, incrustación, almacenamiento, recuperación, indicaciones y generación de documentos, y ningún fragmento breve representa eso honestamente. Cada sección explica qué hace un componente, qué paquete liviano lo maneja y dónde encontrar una implementación probada que pueda copiar y adaptar.

Definición de lo que significa aquí “recursos mínimos”

Mínimo significa que no hay GPU dedicada, ni factura mensual ni datos que salen de su máquina. Tres opciones lo hacen posible.

El primero es la cuantización. Los pesos de los modelos normalmente se almacenan en 16 bits por parámetro y los formatos cuantificados como GGUF los comprimen a 4 o 5 bits. Esto reduce el uso de memoria en aproximadamente dos tercios con un pequeño costo de precisión. Un modelo de 7 mil millones de parámetros que necesita 14 GB con total precisión se ejecuta en aproximadamente 4 GB una vez cuantificado.

El segundo es un pequeño modelo de incrustación. Las incrustaciones convierten el texto en vectores numéricos para que pasajes similares queden muy juntos. Los codificadores de oraciones compactos de alrededor de 80 MB de tamaño producen vectores de 384 dimensiones y manejan bien la recuperación de la mayoría de las colecciones de documentos.

El tercero es un almacén de vectores local que se ejecuta dentro de su proceso Python en lugar de como un servidor de base de datos independiente.

Establezca sus expectativas de velocidad en consecuencia. En hardware exclusivo de CPU, la generación se ejecuta a unos pocos tokens por segundo. Eso es adecuado para un asistente de investigación o una herramienta de conocimiento interno, no para una aplicación pública de alto tráfico.

Armando el kit de herramientas para espacios reducidos

Estos son los paquetes que vale la pena conocer antes de comenzar.

Orquestación: LangChain conecta las piezas y proporciona cargadores de documentos, divisores de texto e interfaces de recuperación. LlamaIndex es una alternativa razonable con un mayor enfoque en la indexación. Inferencia local: llama.cpp es una implementación en C y C++ de inferencia de modelo de lenguaje optimizada para CPU, expuesta a Python a través del paquete llama-cpp-python. Ollama incluye una funcionalidad similar detrás de una línea de comando más simple y un servidor local. Incrustaciones: los transformadores de oraciones de Hugging Face descargan y ejecutan modelos de codificadores compactos localmente, sin llamadas API. Almacenamiento vectorial: FAISS le ofrece una búsqueda rápida de similitudes a través de un índice en memoria que se guarda en el disco. ChromaDB agrega filtrado de metadatos y persistencia, con un poco más de configuración. Análisis de documentos: pypdf maneja archivos PDF. El paquete no estructurado cubre una combinación más amplia de formatos de archivo. Interfaz: Streamlit convierte su canalización en una herramienta basada en navegador en unas pocas docenas de líneas.

Para una compilación completa sin conexión usando llama.cpp, LangChain y ChromaDB juntos, siga Cómo crear una canalización RAG con llama.cpp en Python. Para la variante FAISS y Hugging Face, consulte Una guía práctica para crear aplicaciones RAG locales con LangChain.

Paso 1: Ingerir y fragmentar sus documentos

Su sistema es tan bueno como el texto que le proporciona. Cargue cada documento, elimine los encabezados y pies de página de las páginas y luego divida el texto en partes.

El tamaño del fragmento impulsa la calidad de la recuperación más que casi cualquier otra cosa. Frascos de 500 a 1000 caracteres con una superposición del 10 al 20 por ciento son un buen punto de partida. Demasiado pequeño y una parte pierde el contexto necesario para responder cualquier cosa. Demasiado grande, y el pasaje recuperado entierra la oración relevante en ruido, desperdiciando espacio en la ventana de contexto limitada de un modelo pequeño.

Divídete en límites naturales donde puedas. Los saltos de párrafo y los títulos de sección conservan el significado mejor que un recuento fijo de caracteres. Adjunte metadatos a cada fragmento a medida que lo crea: nombre del archivo fuente, número de página y título de la sección. Esos metadatos le permiten filtrar búsquedas y citar fuentes en sus respuestas más adelante.

Para obtener un tutorial sobre la fragmentación de archivos PDF académicos densos, incluida una interfaz Streamlit, consulte Construyamos un asistente para trabajos de investigación impulsado por RAG.

Paso 2: incrustar e indexar sus fragmentos

Cada fragmento pasa por el modelo de incrustación una vez y regresa como un vector. Esos vectores van a su índice junto con el texto original y los metadatos.

Dos reglas evitan que esta etapa cause problemas más adelante. Utilice el mismo modelo de incrustación para indexar y consultar, ya que los vectores de diferentes modelos no son comparables. Y guarde el índice en el disco, porque volver a incrustar miles de fragmentos en la CPU lleva minutos que no necesita gastar dos veces.

Unos miles de documentos producen un índice medido en decenas de megabytes, que FAISS busca en milisegundos. Reconstruya solo cuando los documentos cambien o cuando cambie los modelos de incrustación.

Paso 3: Recuperar y Solicitar

En el momento de la consulta, la pregunta del usuario se incrusta con el mismo modelo y el índice devuelve los fragmentos más cercanos. De cuatro a seis fragmentos son adecuados para un modelo pequeño con una ventana de contexto modesta.

La búsqueda simple por similitud falla con más frecuencia de lo que la gente espera. Las preguntas breves producen vectores vagos y las frases que difieren del texto original reducen la puntuación de coincidencia. Dos técnicas abordan esto de forma económica. La expansión de consultas reescribe la pregunta en varias variantes y agrupa los resultados. Las incrustaciones de documentos hipotéticos, o HyDE, piden al modelo que primero redacte una respuesta plausible y luego busque utilizando ese borrador. Una respuesta inventada se parece más al pasaje objetivo que una pregunta.

El mensaje que cree en torno al texto recuperado es igualmente importante. Dígale al modelo que responda sólo desde el contexto proporcionado y que diga que no sabe cuando el contexto se queda corto. Patrones de ingeniería rápidos para implementaciones exitosas de RAG cubre estos patrones de recuperación en detalle.

Paso 4: generar respuestas localmente

Los fragmentos recuperados y sus instrucciones van al modelo local. Un modelo cuantificado ajustado a instrucciones 7B u 8B maneja bien la respuesta a preguntas fundamentadas. Los modelos 3B más pequeños responden más rápido y se adaptan a tareas estrechas.

Dos escenarios merecen atención. Establezca la longitud del contexto lo suficientemente alta como para contener los fragmentos recuperados más la pregunta y la respuesta. Y mantenga la temperatura baja, entre 0,1 y 0,3, ya que las respuestas objetivas extraídas de documentos originales no deberían ser creativas.

Hacer que el sistema sea confiable

La confiabilidad proviene de la conexión a tierra y de saber cuándo ha fallado el sistema.

Requerir citas. Cuando cada reclamo lleva un nombre de archivo fuente y un número de página, las respuestas incorrectas se vuelven visibles en lugar de esconderse detrás de frases seguras.

Establezca un umbral de similitud. Si el fragmento mejor recuperado obtiene una puntuación inferior a su límite, devuelva un mensaje indicando que la respuesta no está en la base de conocimientos en lugar de pasar un contexto débil al modelo.

Construya un pequeño conjunto de evaluación. De veinte a treinta preguntas con respuestas correctas conocidas, revisadas después de cada cambio en el tamaño del fragmento o en el modelo de incrustación, le indican si un ajuste ayudó. Sin esto, la sintonización es una conjetura.

Registre los fragmentos recuperados para cada consulta. Cuando una respuesta es incorrecta, el registro muestra de inmediato si falló la recuperación o la generación, y esos dos problemas tienen soluciones completamente diferentes.

Saber cuándo escalar

Un sistema local pequeño cubre mucho terreno, pero algunos problemas necesitan más.

Las preguntas que conectan hechos en varios documentos exponen los límites de la búsqueda de similitudes. La recuperación basada en gráficos, que almacena entidades y relaciones en lugar de fragmentos aislados, maneja mejor ese patrón. Consulte Creación de un sistema Graph RAG: un enfoque paso a paso.

Los dominios especializados a veces necesitan un modelo generador capacitado para interpretar pasajes recuperados de manera más confiable, como se explica en Comprensión de RAG, Parte IX: Ajuste de los LLM para RAG. Y cuando un prototipo se convierte en algo de lo que dependen los colegas, Understanding RAG Part X: RAG Pipelines in Production describe cómo dividir la indexación, la recuperación y la generación en flujos automatizados independientes.

Conclusión

Un sistema RAG que funcione necesita un modelo local cuantificado, un modelo de incrustación compacto, un índice vectorial basado en archivos y una fragmentación cuidadosa. La confiabilidad proviene de lo que rodea a esas piezas: citas de fuentes, un umbral de recuperación, un pequeño conjunto de evaluación y registros que separan los errores de recuperación de los errores de generación.

Comience con las compilaciones llama.cpp o LangChain vinculadas anteriormente, luego ajuste el tamaño del fragmento con sus propias preguntas de prueba antes de agregar algo más complicado.