La mayoría de los prototipos de RAG se ven iguales bajo el capó. Alguien introduce un PDF en un pipeline, divide el texto en fragmentos limpios de 512 tokens, los vuelca en una base de datos vectorial y da el trabajo por terminado. Para una demostración rápida, esto puede parecer impresionante. En producción, colapsa.

Un fragmento fijo no tiene en cuenta qué está cortando. Dividirá un contrato legal en medio de una cláusula de indemnización. Meterá cinco endpoints de API no relacionados en la misma ventana de contexto y ahogará al modelo en ruido. Te obligará a recuperar más fragmentos de los necesarios, aumentando la latencia y consumiendo más tokens. El resultado son respuestas a medias, alucinaciones y usuarios frustrados.

Desmantelamos nuestra capa de recuperación desde los cimientos y la reconstruimos. El resultado fue un sistema que alcanzó un 95 por ciento de recall mientras reducía la latencia en un 40 por ciento. Así es exactamente como lo hicimos.

Por qué los fragmentos fijos fracasan en producción

El valor predeterminado de 512 tokens no es una elección de diseño. Es un subproducto de las ventanas de contexto de los primeros modelos de embedding y de los valores predeterminados de las librerías. Es fácil de implementar y catastrófico para confiar en él.

Los documentos no son uniformes. Una cláusula legal puede extenderse por setecientos tokens sin una ruptura clara. Si la divides en quinientos doce, crearás dos fragmentos huérfanos. Cuando un abogado o un oficial de cumplimiento pregunte sobre los límites de responsabilidad, el sistema devolverá solo la mitad de la obligación. El modelo de lenguaje alucina la mitad que falta o, peor aún, niega que el límite exista.

La documentación de la API sufre de un mal opuesto. Un fragmento de quinientos tokens podría tragarse un módulo entero: encabezados de autenticación, códigos de error, límites de tasa y esquemas de webhook. Cuando un desarrollador pregunta cómo manejar AUTH_4027, el recuperador presenta una mezcla confusa de funciones no relacionadas. El modelo no tiene más remedio que promediarlas en una masa genérica.

Un mal fragmentado también infla la latencia. Los fragmentos débiles significan que necesitas un top-k más grande para cubrir un tema. Más fragmentos significan prompts más largos. Los prompts más largos significan una generación más lenta y facturas más altas. La experiencia del usuario muere por mil cortes.

Adapte el fragmento al documento

Dejamos de contar tokens y empezamos a leer el material. La estrategia de fragmentado adecuada depende de la estructura de la fuente.

Los documentos legales necesitan un fragmentado recursivo de caracteres con límites conscientes de las cláusulas. El divisor respeta la jerarquía: busca primero los encabezados de sección, luego los párrafos numerados y después las rupturas naturales de las oraciones. Nunca corta una subcláusula ni divide una frase obligacional entre fragmentos. Cuando recuperas un pasaje sobre indemnización, obtienes la cláusula completa, el límite y las excepciones.

La documentación de la API exige un fragmentado consciente de la estructura. Analizamos por definición de función, no por presupuesto de tokens. Cada fragmento contiene una firma de función completa, sus descripciones de parámetros y las notas de manejo de errores inmediatamente adyacentes. Si un desarrollador busca un método específico, recibe el contrato completo, no un fragmento atrapado dentro de una división arbitraria.

Los tickets de soporte son ruidosos y no lineales. Un hilo puede comenzar con un informe de error, introducir una solución temporal y terminar con una nota de escalación interna. El fragmentado semántico detecta cambios de tema midiendo la similitud de los embeddings entre oraciones. Permitimos rupturas solo en los límites temáticos naturales, de modo que una conversación sobre fallos de inicio de sesión se mantenga separada de un seguimiento sobre ciclos de facturación.

Las wikis fueron lo más difícil. Son extensas, están interconectadas y organizadas de forma laxa. Utilizamos fragmentado agéntico, donde un LLM ligero lee una página y decide las rupturas basándose en la coherencia temática. Cuesta un poco más en el momento de la ingesta, pero los fragmentos resultantes son autónomos y están listos para la recuperación. Una página sobre mejores prácticas de despliegue se divide en unidades lógicas: comprobaciones previas al vuelo, procedimientos de reversión y configuración de monitoreo, en lugar de bloques de texto arbitrarios.

Recuperación híbrida: palabras clave y vectores juntos

La búsqueda de vectores densos entiende el significado. Es pésima con las cadenas exactas. Si un usuario busca un código de error preciso como AUTH_4027 o un nombre de cliente como "Stark Industries", los embeddings vectoriales pueden errar el objetivo porque optimizan la proximidad conceptual, no la precisión a nivel de caracteres.

La búsqueda pura de palabras clave mediante BM25 tiene el fallo inverso. Encontrará AUTH_4027 perfectamente, pero perderá el puente conceptual entre "fallo de autorización" y "inicio de sesión denegado".

We run both in parallel. BM25 and vector search operate independently over the same corpus. Their result lists are merged using Reciprocal Rank Fusion, which reorders candidates by balancing their positional ranks. You do not need calibrated weights. You simply get the precision of exact match and the intuition of semantic search in a single ranked list.

Then we add a cross-encoder reranker. This is a separate model that scores each passage against the original query, producing a relevance signal far finer than either retriever alone. It adds about 50 milliseconds of latency. It increases recall by 15 percent. If you care about answer quality, that trade is non-negotiable.

Query Expansion: Fix the Search Before It Starts

Bad queries are the dirty secret of every retrieval system. Users do not write like your embedding space. They type "it broke." They paste truncated stack traces. They use internal jargon your index has never seen.

We transform the query before it ever touches the index. First, we expand a single query into three to five diverse search terms. If the original is "payment failed," we also search for "transaction error," "billing declined," and "charge unsuccessful