Image for post Búsqueda híbrida en RAG: arregla lo que el vector falla

Búsqueda híbrida en RAG: arregla lo que el vector falla


TL;DR: La búsqueda híbrida en RAG combina recuperación léxica (BM25) con recuperación semántica (embeddings) y fusiona ambas listas con Reciprocal Rank Fusion (RRF). Resuelve el punto ciego del vector search puro: códigos, siglas y nombres exactos que los embeddings no recuperan. Si le añades un paso de re-ranking con cross-encoder, pasas de "los resultados están bien" a "el chunk correcto sale el primero".

El problema: tu vector search ignora lo que escribiste literal

Un patrón que se repite cuando montas un pipeline RAG solo con embeddings: el usuario busca un código de error exacto, ERR_CONN_RESET_4XX, y el sistema devuelve tres páginas sobre "buenas prácticas de conexión". Semánticamente cercanas, prácticamente inútiles. El fragmento correcto, donde aparece el código literal, ni siquiera entra en el top 10.

La causa es estructural. Los embeddings comprimen el significado en un vector, y en esa compresión se pierde la literalidad. El vector search entiende conceptos, pero es malo con tokens raros: identificadores, SKUs, nombres propios, números de versión. Y en documentación técnica o legal, esos tokens raros suelen ser justo lo que el usuario busca.

Esto importa por dinero y por confianza. Un RAG que no recupera el chunk correcto genera respuestas incompletas o inventadas, y cada respuesta mala cuesta tokens de LLM y credibilidad. La buena noticia: la solución no es cambiar de modelo de embeddings, es cambiar de estrategia de recuperación.

¿Qué es la búsqueda híbrida en RAG?

La búsqueda híbrida es una estrategia de recuperación que ejecuta dos motores en paralelo, uno léxico (BM25) y uno semántico (embeddings), y combina sus resultados en una sola lista ordenada. Cada motor cubre el punto débil del otro: BM25 acierta con coincidencias exactas de palabras clave, los embeddings aciertan con sinónimos, paráfrasis y contexto.

Las dos piezas que necesitas entender:

  • BM25: algoritmo clásico de recuperación léxica basado en frecuencia de términos. Transparente, rápido y sorprendentemente fuerte cuando la consulta contiene palabras exactas del documento.
  • Recuperación densa (dense): convierte consulta y documentos en vectores con un modelo de embeddings y busca por similitud coseno. Capta significado, no literalidad.

¿Qué es Reciprocal Rank Fusion (RRF)?

RRF es un método para fusionar varias listas ordenadas usando solo la posición de cada documento, no su puntuación. A cada documento le asigna un valor según la fórmula 1 / (k + rank) en cada lista, y suma esos valores. Con k = 60 por defecto, un documento que aparece alto en BM25 y en dense sube por encima de los que solo destacan en uno.

La gracia de RRF es que no necesitas normalizar puntuaciones. Las escalas de BM25 y de similitud coseno son distintas e incomparables, y RRF las ignora trabajando solo con rangos. Eso lo hace ideal como primer filtro antes de algo más caro.

Comparativa: qué método recupera mejor y cuándo

MétodoFuerte enDébil enCoste/latencia
BM25 (léxico)Códigos, siglas, términos exactosSinónimos, paráfrasisMuy bajo
Dense (embeddings)Significado, contexto, multilingüeTokens raros, literalidadBajo-medio
Híbrido (BM25 + dense + RRF)Cobertura amplia (recall alto)Orden fino del top 5Medio
Híbrido + re-rankingPrecisión del top 5-10Latencia y coste extraMedio-alto

La lectura práctica: el híbrido con RRF es el mínimo razonable para cualquier RAG en producción. En un benchmark público sobre documentos con texto y tablas (arXiv, 2026), fusionar BM25 y dense con RRF mejoró todas las métricas frente a cada método por separado, con hasta +8,1 puntos de Recall@5 sobre BM25 solo. El re-ranking es el paso opcional que separa "decente" de "muy bueno".

Implementación paso a paso

La arquitectura recomendada es de dos etapas: recuperar amplio y barato, luego afinar caro y preciso. Es la misma idea de separar responsabilidades por capas: cada etapa hace una cosa y la hace bien.

Paso 1: recupera candidatos de los dos motores

Pide más de lo que vas a usar. Si el top final son 5 chunks, recupera 50 de cada motor para darle margen a la fusión.

# Recupera en paralelo: léxico (BM25) y semántico (dense)
# retrieval_k alto para que RRF tenga candidatos suficientes
sparse_hits = bm25.search(query, top_k=50)        # coincidencias exactas
dense_hits = vector_db.query(query, top_k=50)     # similitud semántica

Salida esperada: dos listas de hasta 50 documentos cada una, con solapamiento parcial. El chunk correcto suele estar en al menos una.

Paso 2: fusiona con RRF

La fórmula completa cabe en pocas líneas. No necesitas librería externa.

# RRF: suma 1/(k+rank) por documento en cada lista; k=60 estabiliza
def reciprocal_rank_fusion(listas, k=60):
    scores = {}
    for lista in listas:
        for rank, doc in enumerate(lista):
            scores[doc.id] = scores.get(doc.id, 0) + 1 / (k + rank)
    return sorted(scores, key=scores.get, reverse=True)

fused = reciprocal_rank_fusion([dense_hits, sparse_hits])

Salida esperada: una lista única ordenada donde los documentos que aparecían en ambos motores suben a la cabeza.

Paso 3: re-ranking con cross-encoder

Un cross-encoder lee la consulta y cada candidato juntos y devuelve una puntuación de relevancia directa, no un vector. Por eso es más preciso que la similitud coseno, y por eso es más caro: hay que pasar cada par consulta-documento por el modelo. Lo aplicas solo sobre el top fusionado (por ejemplo 50 o 100), nunca sobre todo el corpus.

# Cross-encoder re-puntúa los candidatos fusionados y deja el top 5
from sentence_transformers import CrossEncoder

reranker = CrossEncoder("BAAI/bge-reranker-v2-m3")  # multilingüe, va bien en español
pares = [(query, doc.text) for doc in fused[:50]]
puntuaciones = reranker.predict(pares)
top_final = [fused[i] for i in puntuaciones.argsort()[::-1][:5]]

Para español, bge-reranker-v2-m3 funciona bien y es open source. Si prefieres no gestionar el modelo, Cohere Rerank y el reranking alojado de Pinecone hacen lo mismo vía API.

Aplicación práctica: cuándo usar cada nivel

No todo RAG necesita las tres capas. La regla que aplico al diseñar el pipeline:

  • Solo dense: prototipos, corpus pequeño, consultas conversacionales sin jerga. Es donde casi todo el mundo empieza, igual que al preparar datos en el chunking de PDFs para IA.
  • Híbrido (BM25 + dense + RRF): en cuanto el corpus tiene códigos, nombres propios, referencias legales o documentación técnica. Es el salto con mejor relación esfuerzo-resultado.
  • Híbrido + re-ranking: cuando la calidad del top 3 es crítica, soporte al cliente, búsqueda en normativa, asistentes que citan fuentes.

En escenarios reales de documentación interna, el patrón híbrido es el que más mueve la aguja sin tocar el modelo de embeddings. Y si necesitas algo más estructurado que texto plano, un grafo de conocimiento como capa de recuperación es la siguiente parada.

En Producción

El salto del tutorial a producción se nota en tres frentes: latencia, coste y evaluación.

Latencia. El cross-encoder es el cuello de botella. Re-puntuar 100 candidatos añade cientos de milisegundos. Mitígalo recortando el número de candidatos que entran al reranker (50 suele bastar) y ejecutando BM25 y dense de forma concurrente, no secuencial.

Coste. Si usas un reranker alojado, se factura por búsqueda. Según la documentación de Cohere, Rerank cobra por consulta procesada; para un proyecto pequeño o mediano presupuesta unos pocos euros por cada 1.000 búsquedas, dentro de un rango de 10 a 50 € al mes en APIs si el tráfico es moderado. El reranker open source en tu propia infra cambia coste de API por coste de cómputo (idealmente una GPU pequeña o inferencia CPU para volúmenes bajos).

Evaluación. No adoptes una técnica por su titular. Mide Recall@k y MRR sobre un set de consultas reales antes y después. En ejemplos publicados por practicantes, pasar a híbrido subió el MRR de 0,41 a 0,67, y añadir cross-encoder lo empujó por encima de 0,80. Tus números dependerán de tu corpus, así que valídalos. Medir antes de creer es la misma disciplina que aplicas al explicar qué hace un modelo con LIME y SHAP.

Errores comunes y depuración

Error: el híbrido devuelve peor que dense solo. Causa: recuperas pocos candidatos por motor (top_k bajo), RRF no tiene material para fusionar. Solución: sube retrieval_k a 50-100 por motor y deja el corte fino al re-ranking.

Error: BM25 no encuentra términos que están en el documento. Causa: tokenización o stemming inadecuados para español (acentos, plurales). Solución: usa un analizador con soporte de español y revisa que los acentos no se pierdan al indexar.

Error: el reranker no cambia el orden. Causa: le pasas el texto completo del chunk, que excede el contexto del modelo y se trunca. Solución: recorta cada candidato a un fragmento representativo antes de re-puntuar.

Preguntas frecuentes

¿La búsqueda híbrida sustituye a un buen modelo de embeddings?

No, lo complementa. La búsqueda híbrida añade recuperación léxica encima de tu vector search para cubrir literalidad. Un mejor embedding sube la calidad semántica, pero no recupera un código exacto que el usuario escribió tal cual.

¿Necesito re-ranking si ya uso RRF?

Depende de cuánto importe el orden del top 3. RRF mejora el recall (que el chunk correcto esté en la lista), el cross-encoder mejora la precisión (que salga el primero). Si tu LLM solo recibe 3-5 chunks, el re-ranking marca la diferencia.

¿Qué reranker uso para contenido en español?

Para open source, bge-reranker-v2-m3 es multilingüe y rinde bien en español. Si prefieres API gestionada, Cohere Rerank y el reranking de Pinecone cubren más de 100 idiomas sin que mantengas el modelo.

Conclusión

Hemos visto que el vector search puro tiene un punto ciego con la literalidad, y que la búsqueda híbrida lo tapa fusionando BM25 con embeddings vía Reciprocal Rank Fusion. La clave está en la arquitectura de dos etapas: recupera amplio y barato con RRF, afina caro y preciso con un cross-encoder solo sobre los candidatos. Y mide siempre con Recall@k y MRR antes de fiarte de ningún titular de mejora.

Si ya tienes un RAG en marcha, el experimento de esta semana es claro: añade BM25 junto a tu dense, fusiona con RRF y compara métricas sobre tus consultas reales. ¿Has montado búsqueda híbrida en producción y te ha cambiado los números? Cuéntamelo en los comentarios o en Twitter @sergiomarquezp_. En el próximo artículo entro en evaluación de RAG con RAGAS, cómo poner número a "esto recupera bien" sin engañarte.

Compartir X LinkedIn