Búsqueda híbrida: BM25 + vectoriel
La búsqueda híbrida lanza en paralelo una búsqueda por palabras clave (BM25) y una búsqueda vectorial sobre la misma pregunta, luego fusiona las dos clasificaciones, normalmente mediante Reciprocal Rank Fusion (RRF). Recupera casos en los que el embedding falla (identificadores, nombres propios, términos raros) sin perder la comprensión de las paráfrasis. Qdrant, Weaviate y Elasticsearch la ofrecen nativamente; con ChromaDB, se construye en treinta líneas de Python.
Una base vectorial recupera el sentido, no las coincidencias literales: una referencia como «RG/2024-117» o un número de ticket se le escapa. En cambio, la búsqueda por palabras clave no entiende que «automóvil» y «coche» se refieren a la misma cosa. Esta guía muestra cómo combinar ambos tipos de búsqueda, qué método de fusión elegir, qué hacen Qdrant y Weaviate, y una trampa de tokenización en francés que arruina silenciosamente la parte BM25.
#¿Por qué la búsqueda puramente vectorial falla en algunas preguntas?
La búsqueda vectorial transforma cada pasaje en un vector que resume su significado general y luego devuelve los pasajes cuyo vector está más cerca del de la pregunta. Esta compresión funciona muy bien para las paráfrasis y mal para todo lo que deba encontrarse literalmente: un identificador, un código de error, un nombre de persona, una sigla interna. Para el embedding, «PROD-4817» y «PROD-4871» se parecen, y un ticket sin relación puede aparecer por delante del correcto. La búsqueda híbrida responde a este defecto: añade una segunda clasificación basada en la presencia exacta de las palabras y fusiona ambas para que cada método compense los puntos ciegos del otro.
- Identificadores y referencias
- Números de ticket, de expediente, de contrato, SKU, códigos de error: son cadenas que un embedding no conserva de forma fiable y que BM25 encuentra en cuanto aparecen en el pasaje.
- Vocabulario especializado poco frecuente
- Términos médicos, jurídicos o técnicos poco comunes: el peso de una palabra rara es alto en BM25, mientras que su embedding puede ser vago.
- Consultas muy cortas
- Dos palabras como « factura 2024 » no aportan mucho para un embedding; las palabras clave, en cambio, se comparan tal como están.
- Paráfrasis y reformulaciones
- El caso inverso: «contrato de duración determinada» y «CDD» o «coche» y «automóvil» no comparten ninguna palabra; solo la búsqueda vectorial los acerca
#BM25: lo que hace la búsqueda por palabras clave
Tus documentos, tu IA: un RAG local fiable sobre tus PDF, notas y correos — sin enviar nada a la nube.
- Espacio en línea de por vida
- PDF + archivos
- Reembolsado 30 j
BM25 es la función de clasificación léxica que utilizan Lucene, Elasticsearch y OpenSearch, y que SQLite ofrece en su módulo FTS5 (la documentación describe su función bm25() como una función que devuelve un valor que indica el grado de coincidencia de la fila con la consulta). Se basa en tres ideas: una palabra poco frecuente pesa más que una palabra frecuente, una palabra repetida cuenta cada vez menos a partir de cierto número de apariciones y un pasaje largo se penaliza ligeramente frente a uno corto. El parámetro k1 regula esta saturación: según la descripción de Elastic, limita la influencia que un solo término de la consulta puede tener sobre la puntuación de un documento.
- Fortalezas
- Términos raros, identificadores, consultas cortas, vocabulario especializado, ningún modelo que cargar ni entrenar, índice compacto, resultado explicable (se sabe qué palabra hizo que el pasaje apareciera entre los resultados).
- Debilidades
- Sinónimos, reformulaciones, paráfrasis, errores de tecleo; sin lematización, «firmado» y «firma» son dos palabras diferentes.
- Requisitos previos
- Una tokenización cuidadosa: minúsculas, eliminación de acentos y, si procede, reducción a la raíz. Es el punto en el que la mayoría de las implementaciones propias se equivocan; ver más abajo.
#Búsqueda híbrida: el principio
Se ejecutan las dos búsquedas sobre la misma pregunta; cada una devuelve su lista de candidatos (20 a 50 pasajes) y luego se fusionan ambas listas en una única clasificación. La mejora se basa en una observación sencilla: los pasajes que aparecen en ambas listas son casi siempre pertinentes y, además, cada método recupera pasajes que el otro ha pasado por alto. Weaviate define la búsqueda híbrida en estos términos: combina los resultados de una búsqueda vectorial y una búsqueda por palabras clave fusionando ambos conjuntos de resultados, con un método de fusión y pesos relativos configurables. La mejora cuantificada depende del corpus y de las preguntas: ninguna cifra general es fiable y hay que medirla con tus propios documentos (véase más abajo).
#Fusionar sin normalizar: Fusión de Rango Recíproco
El error clásico consiste en sumar los puntajes brutos. Un puntaje BM25 es un número positivo sin límite; un puntaje de similitud vectorial es una distancia o un coseno, dentro de un rango limitado: las dos escalas son incomparables, y el menor cambio de corpus las desplaza. La Fusión de Rango Recíproco soluciona el problema utilizando solo los rangos. La documentación de Elasticsearch la presenta como un método que no requiere ajustes y cuyos indicadores de relevancia no necesitan estar relacionados entre sí.
Un fragmento clasificado en primer lugar por BM25 y en quinto por la búsqueda vectorial obtiene 1/61 + 1/65, es decir, aproximadamente 0,0318; un fragmento clasificado en decimoquinto lugar en ambas listas obtiene 2/75, es decir, aproximadamente 0,0267. La constante k atenúa la ventaja del primer puesto: cuanto mayor es, más cuentan los puestos más alejados. Elasticsearch documenta esta constante con el nombre rank_constant, con un valor predeterminado de 60, y un tamaño de ventana (rank_window_size) que fija la longitud de cada lista antes de la fusión. Una ventana más grande mejora la relevancia a costa del rendimiento, precisa la documentación.
#¿Y la fusión por puntuaciones?
Algunos motores ofrecen una alternativa: normalizar las puntuaciones de cada lista y luego combinarlas aplicando un peso. Weaviate documenta ambos métodos: una clasificación por posiciones y una fusión por puntuaciones relativas. Esta última es el método predeterminado desde la versión 1.24 y es necesaria para usar autocut con el operador híbrido. Qdrant ofrece RRF y DBSF; esta última conserva las puntuaciones brutas, pero normaliza su distribución (media y desviación estándar) antes de combinarlas. La fusión por posiciones es más robusta cuando no se conoce la distribución de las puntuaciones; la fusión por puntuaciones permite un ajuste más fino cuando se pueden realizar mediciones.
#Qué herramienta elegir: motores con búsqueda híbrida nativa
| Herramienta | Híbrido nativo | Fusión | Ajuste del peso |
|---|---|---|---|
| Qdrant | Sí, mediante la API Query (disponible desde la versión 1.10) | RRF o DBSF | Peso por solicitud y constante k ajustables en las versiones recientes |
| Weaviate | Sí, operador hybrid | Posiciones o puntuaciones relativas (opción predeterminada desde la versión 1.24) | Parámetro alpha: 1 = búsqueda exclusivamente vectorial, 0 = búsqueda exclusivamente por palabras clave |
| Elasticsearch | Sí, recuperador rrf | RRF | rank_constant (60 por defecto) y rank_window_size |
| SQLite FTS5 + extensión vectorial | Por ensamblar | Por escribir | A tu elección |
| ChromaDB + rank_bm25 | Para montar en Python | Por escribir (RRF en 6 líneas) | A tu elección |
La diferencia de fondo no es la fusión en sí, que cabe en unas pocas líneas, sino el índice: un motor nativo mantiene ambos índices actualizados conjuntamente, mientras que la solución casera mantiene el índice BM25 en memoria y debe reconstruirlo cada vez que se añade un documento. Para un corpus de unos pocos miles de pasajes que cambia rara vez, la solución casera es más que suficiente. Para corpus más grandes, o en cuanto los documentos cambian a diario, un motor nativo evita las incoherencias entre los dos índices. La guía sobre Weaviate presenta esta herramienta en detalle.
#Implementación propia: ChromaDB, rank_bm25 y RRF
El código siguiente ensambla las tres piezas. Corrige un defecto frecuente en los ejemplos: la función de normalización debe eliminar los signos diacríticos antes de filtrar; de lo contrario, cada letra acentuada corta una palabra en dos.
Dos precauciones prácticas. Primero, los identificadores deben ser idénticos en ambos índices: aquí, el índice de la lista, convertido en cadena, sirve de identificador de Chroma. Segundo, el índice BM25 en memoria desaparece al detener el programa: reconstrúyelo al arrancar, lo que tarda unos segundos para decenas de miles de fragmentos de texto, o guarda la lista de documentos junto a la base.
#Ajustar el equilibrio entre BM25 y la búsqueda vectorial
Por defecto, RRF trata las dos listas por igual. Si tu corpus contiene muchas referencias (jurisprudencia, tickets, catálogos), da más peso a BM25; si las preguntas son conversacionales, mantén el equilibrio o favorece la búsqueda vectorial. En la función anterior, basta con pasar weights=[0.6, 0.4] para dar un peso del 60 % a BM25. Qdrant permite el mismo ajuste: su documentación indica que el peso de cada consulta es 1 por defecto, lo que reproduce la fórmula original de RRF, y que la constante k es ajustable en las versiones recientes. En Weaviate, se ajusta alpha: 1 equivale a una búsqueda puramente vectorial y 0 a una búsqueda exclusivamente por palabras clave.
| Corpus y preguntas | Peso inicial BM25 / vectorial | Lo que debes vigilar |
|---|---|---|
| Referencias, números, nombres propios (ámbito jurídico, tickets, catálogos) | 60 / 40 | ¿Las preguntas por identificador aparecen en primer lugar? |
| Documentación redactada, preguntas en lenguaje natural | 40 / 60 | ¿Las paráfrasis recuperan el pasaje correcto? |
| Corpus mixto o desconocido | 50 / 50 | Recall@5 con 30 a 50 preguntas reales |
| Siglas y jerga profesional | 55 / 45, con un diccionario de sinónimos para la parte de BM25 | ¿La sigla y su forma desarrollada recuperan el mismo pasaje? |
#Después de la fusión: añadir un reranker
La fusión da un conjunto de candidatos más variado que cada método por separado. Un reranker puede después ordenar este conjunto leyendo cada pasaje junto con la pregunta: las dos técnicas se combinan. El orden habitual es búsqueda híbrida, luego fusión, después aplicar un reranker a los primeros 20 a 50 candidatos y, por último, incluir los 3 a 5 mejores pasajes en el prompt. La guía sobre el reranker detalla esta última etapa, y la guía sobre chunking explica por qué el tamaño de los pasajes influye tanto en BM25 como en los embeddings.
#Preguntas frecuentes sobre búsqueda híbrida
¿Qué es la búsqueda híbrida en un RAG?+
¿Hay que normalizar las puntuaciones BM25 y vectoriales antes de sumarlas?+
¿Qué valor de k elegir en RRF?+
¿Se puede hacer búsqueda híbrida con ChromaDB?+
¿Ralentiza mucho la búsqueda híbrida las consultas?+
¿Cómo saber si la búsqueda híbrida vale la pena para mis documentos?+
- Añadir un reranker a tu pipeline
- Estrategias de chunking
- Weaviate: búsqueda híbrida y multitenencia
- RAG local con ChromaDB y Ollama
- Los mejores modelos de embeddings en francés
- Fuente: Qdrant, consultas híbridas
- Fuente: Weaviate, búsqueda híbrida
- Fuente: Elasticsearch, Reciprocal Rank Fusion
- Fuente: SQLite FTS5, función bm25()
¿Un comentario, un error, una precisión? Avísanos, eso mejora la guía para todos.