Todo ingeniero de IA lo sabe bien. Acaba de enviar una prueba de concepto. La demostración fue brillante. El LLM respondió preguntas con fluidez, sintetizó información sobre la marcha e impresionó a todos en la sala. Entonces alguien le preguntó sobre la política de reembolso de la empresa y con seguridad dio la respuesta equivocada, una que no había sido cierta durante ocho meses.
Ese momento no es un fracaso modelo. Es un fracaso de la arquitectura. Y es exactamente el problema para el que se diseñó la Generación Aumentada de Recuperación, o RAG.
Este artículo explica cómo construir un sistema RAG de nivel de producción para una base de conocimientos interna de la empresa, utilizando una pila de código totalmente abierto. Pasaremos del problema al diseño, a través de cada etapa del proceso, y terminaremos con cómo saber realmente si el sistema está funcionando. El objetivo no es cubrir todas las variaciones posibles, sino brindarle un modelo mental claro y una base práctica sobre la cual pueda construir.
Lo que cubriremos
Por qué los LLM por sí solos no son suficientes para la recuperación de conocimientos empresariales La arquitectura RAG: cómo encajan los dos canales Construyendo el canal de indexación: cargando, fragmentando, incrustando y almacenando Construyendo el canal de recuperación y generación: búsqueda, reclasificación y solicitud Evaluación: midiendo la calidad en cada etapa, no solo al final Dónde termina RAG y comienza el ajuste
El problema que vale la pena resolver
La mayoría de las organizaciones medianas y grandes cuentan con miles de documentos internos, manuales de ingeniería, políticas de recursos humanos, directrices de cumplimiento, guías de incorporación y especificaciones de productos. Viven en Confluence, SharePoint, Notion, unidades compartidas e hilos de correo electrónico que nadie ha tocado en tres años.
El empleado promedio pasa de dos a tres horas por semana simplemente buscando información que ya existe en alguna parte. Los ingenieros superiores se convierten en agentes de soporte accidentales. Los nuevos miembros tardan meses en volverse productivos de forma independiente, no porque carezcan de capacidad, sino porque el conocimiento institucional está disperso y es inescrutable.
La respuesta ingenua es señalar todo esto a un LLM y hacerle preguntas. El problema es que los LLM son estáticos. Una vez capacitados, no tienen conocimiento del último lanzamiento de su producto, de la política que cambió el último trimestre o de la autopsia que su equipo publicó ayer. El ajuste fino ayuda con el estilo y el tono, pero es costoso, lento de actualizar y no indica de dónde vino la respuesta. En una industria regulada, esa brecha de auditabilidad no es aceptable.
RAG enhebra la aguja. En el momento de la consulta, el sistema recupera los documentos más relevantes de su base de conocimientos y se los entrega al LLM como contexto. El modelo genera una respuesta basada en esos documentos, no en lo que aprendió durante la capacitación. Cada respuesta tiene su origen en una fuente. La base de conocimientos se puede actualizar en minutos. Y nada necesita salir de su infraestructura.
La Arquitectura
Antes de entrar en los componentes individuales, es útil ver la forma de todo el sistema. RAG no es un modelo único, son dos tuberías que trabajan juntas.
El proceso de indexación se ejecuta una vez cuando configura el sistema por primera vez y luego de forma incremental cada vez que se agregan o modifican documentos. Su trabajo es tomar documentos sin procesar, dividirlos en fragmentos significativos, convertir esos fragmentos en representaciones vectoriales y almacenarlos.
El proceso de recuperación y generación se ejecuta en cada consulta de usuario. Toma la pregunta, encuentra los fragmentos más relevantes, los reúne en un mensaje y le pide al LLM que genere una respuesta basada en ese contexto.
Los dos oleoductos comparten el almacén de vectores como punto de encuentro. Esa única decisión de diseño, que separa la indexación de la recuperación, es lo que hace que todo el sistema sea actualizable sin necesidad de volver a capacitarlo.
Fase uno: el proceso de indexación
Cargando sus documentos
El primer desafío es simplemente lograr que sus documentos estén en un formato utilizable. El conocimiento empresarial rara vez se encuentra en un solo lugar o en un solo formato.
Para ello utilizamos LlamaIndex. Mientras LangChain ofrece cargadores de documentos, LlamaIndex va más allá: ofrece más de cien conectores nativos para sistemas como Confluence, Notion, SharePoint, Google Drive y S3, y rastrea los hash de los documentos para que solo los archivos modificados se vuelvan a indexar en ejecuciones posteriores. Para una base de conocimientos que evoluciona constantemente, esa sincronización incremental no es algo agradable, sino esencial.
de llama_index.readers.confluence import ConfluenceReader de llama_index.core import SimpleDirectoryReader # Extraer de Confluence confluence_docs = ConfluenceReader( base_url="https://yourcompany.atlassian.net/wiki", oauth2={"client_id": "…", "token": "…"} ).load_data(space_key="ENGG", page_status="current") # Extraer de un directorio local (PDF, Markdown, DOCX) local_docs = SimpleDirectoryReader( input_dir="./knowledge_base", require_exts=[".pdf", ".docx", ".md"], recursivo=True ).load_data()
Qué comprobar aquí: registre cuántos documentos se cargaron correctamente, cuántos se omitieron y si alguno falló silenciosamente. Una falla del cargador en esta etapa crea una brecha de conocimiento que se manifestará más adelante como una respuesta incorrecta o faltante y será muy difícil de rastrear.
Fragmentación: el paso en el que la mayoría de los equipos se equivocan
Si toma algo de este artículo, que sea esto: la calidad de su fragmentación tiene más impacto en el rendimiento de su sistema que su elección de LLM o incluso su modelo de integración.
La razón es sencilla. Cuando un usuario hace una pregunta, el sistema recupera fragmentos, no documentos completos. Si un fragmento se corta a mitad de un argumento, o divide una tabla en dos segmentos, o es tan grande que diluye la señal, el sistema de recuperación no puede hacer su trabajo correctamente.
División simple de tamaño fijo: cortar cada 512 tokens sin tener conocimiento de los límites de las oraciones o párrafos es una tarea rápida de implementar y consistentemente mediocre. Para contenido empresarial, utilizamos SentenceWindowNodeParser de LlamaIndex, que indexa a nivel de oración para una recuperación precisa pero se expande a una ventana circundante de oraciones al generar la respuesta. Se obtiene una recuperación quirúrgica sin perder el contexto que hace que una respuesta sea coherente.
from llama_index.core.node_parser import SentenceWindowNodeParser parser = SentenceWindowNodeParser.from_defaults( window_size=3, # 3 oraciones a cada lado en el momento de la generación window_metadata_key="window", original_text_metadata_key="original_text" ) nodes = parser.get_nodes_from_documents(all_docs)
Para documentos más largos, como archivos de políticas o runbooks técnicos, un enfoque jerárquico funciona mejor: indexar a nivel de párrafo, pero devolver la sección completa al generar. La estrategia de fragmentación adecuada depende de su tipo de contenido; no hay una respuesta universal.
Qué comprobar aquí: revise manualmente alrededor de cincuenta fragmentos aleatorios. Pregúntese si cada uno de ellos podría ser por sí solo una respuesta significativa a alguna pregunta. Si más de uno de cada cinco parecen fragmentos de oraciones o cláusulas huérfanas, el tamaño del fragmento es demasiado pequeño o la superposición es insuficiente.
Convertir texto en vectores
Cada fragmento debe convertirse en un vector numérico para que podamos medir la similitud entre una consulta y un documento. Este es el trabajo del modelo integrado y la elección es más importante de lo que muchos ingenieros creen.
Usamos BAAI/bge-large-en-v1.5, un modelo de código abierto de la Academia de IA de Beijing, que se encuentra entre los modelos de código abierto de mayor rendimiento en el punto de referencia MTEB. Se ejecuta íntegramente a nivel local, lo que para la mayoría de las empresas no es opcional sino obligatorio. El envío de documentos internos a una API de incrustación externa es un problema de residencia de datos que detendrá el lanzamiento de producción.
from llama_index.embeddings.huggingface import HuggingFaceEmbedding embed_model = HuggingFaceEmbedding( model_name="BAAI/bge-large-en-v1.5", query_instruction="Represente esta oración para buscar pasajes relevantes: " )
El prefijo de instrucciones en la última línea es específico de los modelos BGE y vale la pena conservarlo. Es una optimización de recuperación asimétrica que mejora considerablemente la precisión. Una regla que debe considerarse absoluta: se debe utilizar el mismo modelo de incrustación tanto para la indexación como para las consultas. Estas dos operaciones producen vectores que viven en el mismo espacio matemático. Mezclar modelos, incluso actualizar a una versión más nueva en mitad de la implementación, rompe ese espacio y hace que su índice pierda sentido.
Qué comprobar aquí: ejecute las veinte consultas más comunes con un pequeño índice de prueba e inspeccione las puntuaciones de similitud. Una puntuación constante por debajo de 0,6 en consultas que sabes que deberían coincidir bien indica que el dominio no coincide. Considere ajustar el incrustador en una muestra de su corpus interno.
Almacenamiento de vectores: por qué Weaviate
El almacén de vectores es donde viven todos los fragmentos indexados, listos para ser buscados. Usamos Weaviate, autohospedado, y vale la pena mencionar las razones.
La mayoría de las bases de datos vectoriales hacen una cosa: almacenar vectores y encontrar los vecinos más cercanos. Weaviate hace eso, pero también ofrece algo que las implementaciones empresariales realmente necesitan: búsqueda híbrida nativa, que combina vectores semánticos densos con búsqueda de palabras clave BM25 en una sola llamada de consulta. Esto es importante porque los usuarios empresariales no buscan como lo hace un usuario web general. Buscan con nombres de productos exactos, ID de tickets internos, abreviaturas de equipo y jerga que los modelos integrados manejan mal. Una consulta de “Lista de verificación de cumplimiento del artículo 17 del RGPD” contiene un término específico cuya similitud semántica diluirá. BM25 lo encontrará inmediatamente.
Más allá de la búsqueda híbrida, Weaviate ofrece multiinquilino nativo: puede dividir el índice por departamento, de modo que una consulta de recursos humanos nunca muestre accidentalmente documentos de arquitectura de ingeniería, y el control de acceso se aplica a nivel de base de datos en lugar de incluirse en el código de la aplicación.
importar weaviate desde weaviate.classes.config importar Configurar, Propiedad, Tipo de datos client = weaviate.connect_to_local(host="localhost", port=8080, grpc_port=50051) client.collections.create( name="EnterpriseKB", vectorizer_config=Configure.Vectorizer.none(), properties=[ Property(name="text", data_type=DataType.TEXT), Property(name="source", tipo_datos=Tipo_datos.TEXT), Propiedad(nombre="departamento", tipo_datos=Tipo_datos.TEXT), Propiedad(nombre="clasificación", tipo_datos=Tipo_datos.TEXT), Propiedad(nombre="actualizado_en", tipo_datos=Tipo_datos.FECHA), ] )
Qdrant es una alternativa sólida si está comenzando con algo pequeño y desea operaciones más simples. pgvector es razonable si ya estás en Postgres y no necesitas escala horizontal. Pero para una implementación empresarial donde la búsqueda híbrida, el control de acceso y el aislamiento de varios equipos son importantes, Weaviate es la herramienta adecuada.
Fase dos: recuperación y generación
Encontrar los trozos correctos
Cuando un usuario envía una consulta, el primer trabajo es la recuperación: encontrar los fragmentos que con mayor probabilidad contengan la respuesta. Incorporamos la consulta utilizando el mismo modelo que la indexación, luego buscamos en Weaviate con el modo híbrido habilitado.
de llama_index.core.retrievers importar VectorIndexRetriever de llama_index.core.vector_stores importar MetadataFilter, MetadataFilters retriever = VectorIndexRetriever( index=index, similarity_top_k=10, vector_store_query_mode="hybrid", alpha=0.75, # Mezcla: 75% semántico, 25% palabra clave vector_store_kwargs={ "filtros": MetadataFilters(filtros=[ MetadataFilter(key="departamento", valor="ingeniería"), MetadataFilter(key="clasificación", valor="confidencial", operador=FilterOperator.NE) ]) } )
El parámetro alfa controla la combinación entre la búsqueda semántica y de palabras clave. Un valor de 0,75 se inclina hacia la similitud semántica y al mismo tiempo otorga un peso significativo a las coincidencias de palabras clave. Es posible que necesites ajustar esto según tu contenido; los dominios con mucha terminología técnica precisa a menudo se benefician de un alfa más bajo.
Medir la calidad de la recuperación requiere un conjunto de evaluación etiquetado: una colección de consultas emparejadas con los documentos que deben devolverse. Su historial de tickets de la mesa de ayuda de TI es una fuente práctica para esto: preguntas reales de los empleados con resoluciones documentadas. Las métricas a seguir son Tasa de aciertos en K (¿aparece el documento correcto en los K resultados principales?), Rango recíproco medio (¿a qué altura de la lista aparece el primer resultado correcto?) y Precisión de contexto (¿qué proporción de fragmentos recuperados son realmente relevantes?).
Un objetivo razonable para un sistema de producción es una tasa de acierto superior a 0,80 con K=5.
Reclasificación: el pase de refinamiento
La búsqueda de vectores es rápida y se escala bien, pero tiene una debilidad conocida: compara consultas y documentos de forma independiente como vectores separados. Dos documentos pueden tener vectores similares para una consulta, pero sólo uno la responde genuinamente.
Un reclasificador de codificador cruzado soluciona esto leyendo la consulta y cada documento juntos y puntuando la verdadera alineación semántica. Es más lento, pero se aplica sólo a los diez mejores candidatos de la recuperación; la latencia añadida es de cincuenta a cien milisegundos y suele ser aceptable.
Usamos ms-marco-MiniLM-L-6-v2, un codificador cruzado de código abierto bien probado y entrenado en datos de relevancia de búsqueda. LlamaIndex lo integra claramente en el motor de consultas como posprocesador, por lo que no se requiere una orquestación personalizada.
Vale la pena agregar una nueva clasificación cuando sus consultas son largas o ambiguas, o cuando nota que la recuperación encuentra documentos vagamente relevantes pero omite el mejor. Si su modelo de inserción ya se adapta bien a su dominio y la precisión de recuperación es alta, omítalo, el costo de latencia no siempre está justificado.
El LLM local: mantener los datos internamente
Para muchas empresas, especialmente aquellas en sectores regulados, enviar documentos internos a una API LLM externa simplemente no está sobre la mesa. El RGPD, los requisitos de residencia de datos y las preocupaciones sobre la confidencialidad comercial empujan hacia la inferencia local.
Ollama lo explica claramente. Empaqueta modelos de código abierto con un tiempo de ejecución y una API simple, lo que le permite ejecutar Llama 3.1 localmente con un solo comando. Para un modelo de 8 mil millones de parámetros, una sola GPU de 16 GB es suficiente. Para una mayor precisión a costa de la computación, la variante de 70 mil millones de parámetros requiere aproximadamente 80 GB de memoria GPU; alcanzable en un grupo pequeño.
from llama_index.llms.ollama import Ollama llm = Ollama( model="llama3.1:8b", temperatura=0.1, # Temperatura baja para tareas de recuperación de hechos context_window=8192, request_timeout=120.0 )
La temperatura merece una palabra aquí. Para responder preguntas objetivas sobre una base de conocimientos, es necesario que el modelo sea determinista y conservador. Una temperatura de 0,1 mantiene el modelo firmemente conectado al contexto proporcionado. Elevarlo por encima de 0,4 aumenta el riesgo de que el modelo interpola más allá de lo que realmente dicen los fragmentos recuperados.
Armando el mensaje
La ingeniería rápida para RAG a menudo se considera una ocurrencia tardía, lo cual es un error. La forma en que se encuadra el contexto y la instrucción determina directamente si el modelo se mantiene firme o cae en una alucinación.
Lo esencial es: decirle explícitamente al modelo que debe responder utilizando únicamente el contexto proporcionado; darle una instrucción alternativa clara para cuando la respuesta no esté en el contexto; y pídale que cite el documento fuente. El último punto no sólo es útil para los usuarios sino que hace que los errores sean auditables.
from llama_index.core import PromptTemplate qa_prompt = PromptTemplate( """Usted es un asistente experto para la base de conocimientos interna. Responda la pregunta utilizando solo el contexto proporcionado a continuación. Si la respuesta no está claramente presente en el contexto, dígalo honestamente y sugiera al empleado que se comunique directamente con el equipo correspondiente. Siempre termine su respuesta citando los documentos fuente que utilizó. Contexto: {context_str} Pregunta: {query_str} Respuesta:""" )
RetrieverQueryEngine de LlamaIndex conecta la recuperación, la reclasificación, el ensamblaje rápido y la generación juntos. MetadataReplacementPostProcessor se encarga de expandir los fragmentos de oraciones comprimidas a su ventana completa antes de pasarlos al LLM.
de llama_index.core.query_engine importar RetrieverQueryEngine de llama_index.core.postprocessor importar ( MetadataReplacementPostProcessor, SentenceTransformerRerank ) query_engine = RetrieverQueryEngine.from_args( retriever=retriever, llm=llm, node_postprocessors=[ MetadataReplacementPostProcessor(target_metadata_key="window"), SentenceTransformerRerank(model="cross-encoder/ms-marco-MiniLM-L-6-v2", top_n=3) ], text_qa_template=qa_prompt ) respuesta = query_engine.query("¿Cuál es el proceso para solicitar acceso a la base de datos de producción?") print(response.response) para el nodo en respuesta.source_nodes: print(f"Fuente: {node.metadata.get('source')} – puntuación: {node.score:.3f}")
Evaluación del proceso completo
Construir un sistema RAG sin un marco de evaluación es como enviar software sin pruebas. No se puede saber si un cambio mejoró o degradó el sistema a menos que tenga una línea de base con la que comparar.
RAGAS (Evaluación de generación aumentada de recuperación) es el marco estándar de código abierto para esto. Su propiedad más valiosa es que no requiere respuestas doradas preetiquetadas para cada pregunta; utiliza un LLM como juez internamente, lo que lo hace escalable a cientos de evaluaciones por ejecución.
desde ragas importar evaluar desde ragas.metrics importar fidelidad, respuesta_relevancia, contexto_recuerdo, contexto_precisión resultado = evaluar( conjunto de datos=eval_dataset, # consulta, contextos, respuesta, métricas de verdad_terreno=[fidelidad, relevancia_respuesta, recuerdo_contexto, precisión_contexto], llm=LlamaIndexLLMWrapper(llm), embeddings=LlamaIndexEmbeddingsWrapper(embed_model) )
Las cuatro métricas principales: cada una detecta una categoría diferente de falla:
La fidelidad comprueba si la respuesta está realmente respaldada por el contexto recuperado. Una puntuación de fidelidad baja significa que el LLM está alucinando, lo que genera afirmaciones que van más allá de lo que dicen los documentos. Esta es la métrica más crítica para el uso empresarial.
La relevancia de la respuesta mide si la respuesta realmente aborda la pregunta formulada. Un modelo puede ser perfectamente fiel (solo decir cosas que el contexto respalda) pero aun así dar una respuesta irrelevante.
Context Recall comprueba si el paso de recuperación mostró la información que se necesitaba. Si es bajo, el problema está en tu recuperación, no en tu generación.
La precisión del contexto mide qué proporción de los fragmentos recuperados fueron realmente útiles. Los fragmentos de alta recuperación con baja precisión significan que se está pasando ruido al LLM, lo que degrada la calidad de la generación.
Para un sistema de producción, los objetivos razonables son una fidelidad superior a 0,90, una relevancia de la respuesta superior a 0,85, un recuerdo del contexto superior a 0,80 y una precisión del contexto superior a 0,75. Estas no son reglas fijas, pero si está significativamente por debajo de alguna de ellas, tiene una señal clara de dónde enfocar su esfuerzo de depuración.
¿RAG o puesta a punto? La respuesta honesta
Esta pregunta surge en casi todas las conversaciones sobre LLM en empresas, y vale la pena abordarla directamente en lugar de tomar medidas cautelares.
El ajuste fino es la herramienta adecuada cuando se desea cambiar el comportamiento de un modelo: su tono, su patrón de razonamiento, cómo estructura las respuestas, el vocabulario que utiliza. Incorpora esas propiedades a los pesos del modelo. Actualizar ese conocimiento más adelante requiere otra ejecución de ajuste.
RAG es la herramienta adecuada cuando se quiere cambiar lo que sabe un modelo: los hechos, las políticas y los documentos en los que puede basarse. Actualizar conocimientos es cuestión de volver a indexar documentos, lo que lleva unos minutos.
Los dos no están en competencia. Los sistemas de producción más sólidos utilizan ambos: un modelo adaptado al estilo de escritura y la terminología interna de la empresa, combinado con RAG para fundamentar el conocimiento. El ajuste fino le brinda consistencia de voz; RAG le brinda precisión objetiva y auditabilidad.
El error común es realizar ajustes cuando un documento es “demasiado importante como para arriesgarse a que el modelo se equivoque”. El ajuste fino no garantiza la precisión, solo hace que el modelo tenga más confianza. RAG, con un índice bien mantenido y un aviso de base estricto, le brinda algo que el ajuste fino no puede: una línea directa desde cada respuesta hasta su fuente.
Modos de falla comunes
Algunos patrones aparecen con suficiente frecuencia como para que valga la pena nombrarlos explícitamente.
El problema más común no son las alucinaciones sino la falla en la recuperación. El modelo no puede responder correctamente si nunca se recuperó el fragmento correcto. Antes de culpar al LLM, verifique su tasa de aciertos en su conjunto de evaluación. Si está por debajo de 0,70, comience con la calidad de fragmentación e incrustación y luego considere la búsqueda híbrida si aún no la está utilizando.
El conocimiento obsoleto es el segundo problema más común en la producción. Se actualizó un documento, pero no el índice. La solución es operativa: configure un trabajo de reindexación incremental activado por eventos de cambio de documentos en Confluence o SharePoint, en lugar de ejecutar una reindexación completa según una programación.
El tercer patrón es el contexto que el modelo recupera técnicamente pero que ignora: el problema de “perdido en el medio”. Los LLM ponderan más el principio y el final de la ventana de contexto que el medio. Si pasa diez fragmentos, el más relevante debe ser el primero. Reduzca su top-K y asegúrese de que su reclasificador realice el pedido correctamente.
Antes de enviar
Una breve lista de verificación que refleja la brecha entre un prototipo funcional y un sistema en el que arriesgaría su reputación:
Evaluar la tasa de aciertos en K=5 en al menos 150 consultas etiquetadas; objetivo superior a 0,85 Ejecute la fidelidad de RAGAS en 100 o más pares de consulta-respuesta; objetivo superior a 0,90 Configure el aislamiento de inquilinos de Weaviate si se implementa en varios departamentos Configure la reindexación incremental activada por eventos de cambio de documentos Agregue un respaldo de baja confianza: si el puntaje de recuperación máximo es inferior a 0,55, devuelva un honesto "No pude encontrar una respuesta confiable" en lugar de adivinar Implemente el registro de consultas con un mecanismo de comentarios del usuario: este se convierte en su conjunto de datos de evaluación continua
Conclusión
RAG no hace que su LLM sea más inteligente. Lo hace honesto.
La diferencia entre un sistema en el que sus colegas confían y uno que dejan de usar silenciosamente después de quince días generalmente no tiene nada que ver con el modelo elegido. Todo se reduce a si la recuperación es lo suficientemente precisa como para encontrar el fragmento correcto, si el mensaje es lo suficientemente disciplinado como para mantener el modelo basado en él y si se cuenta con la evaluación para saber cuándo cualquiera de esas cosas comienza a degradarse.
El proceso descrito en este artículo no es la única forma de construir un sistema RAG. Es un conjunto de elecciones deliberadas, cada una de las cuales se toma por una razón específica, que en conjunto producen algo que se puede implementar en un entorno regulado, entregarlo a una parte interesada no técnica y respaldar cuando alguien pregunta de dónde vino una respuesta.
Esa última parte importa más que cualquier puntaje de referencia. En entornos empresariales, la confianza es el producto. Todo lo demás es sólo infraestructura.