← Back to list

Dominando la Búsqueda Híbrida: Una Guía Técnica Profunda sobre Qdrant, Vectores Dispersos y Fusión…

En la era de la información, encontrar datos relevantes con rapidez y precisión es más crucial que nunca. La búsqueda semántica, basada en…

Jesus Oviedo Riquelme · 2025-07-01 03:39 · 0 claps · 19.7 min read
#qdrant #vector-database #dense-vector
Open on Medium ↗
Wiki topics: RAG · RAG & Retrieval

Dominando la Búsqueda Híbrida: Una Guía Técnica Profunda sobre Qdrant, Vectores Dispersos y Fusión de Relevancia

En la era de la información, encontrar datos relevantes con rapidez y precisión es más crucial que nunca. La búsqueda semántica, basada en vectores densos y modelos de lenguaje, ha transformado esta tarea al comprender la intención y el contexto detrás de una consulta, más allá de las coincidencias exactas de palabras clave. Esto permite, por ejemplo, que una búsqueda de “barcos de vela para recreo” arroje resultados sobre “yates”, algo antes impensado.

Sin embargo, este enfoque tiene límites. Cuando se requiere precisión exacta — como buscar un producto por su código “XG-42-B7” o un nombre específico como “Karen Spärck Jones” — la búsqueda semántica pura puede fallar al generalizar demasiado. Aquí es donde la búsqueda híbrida se vuelve esencial, combinando la comprensión contextual de los vectores densos con la exactitud de la búsqueda por palabras clave, usando vectores dispersos.

Este artículo explora cómo lograr esta sinergia usando Qdrant, una base de datos vectorial diseñada para búsquedas híbridas. Se abordarán los fundamentos de los vectores densos y dispersos, el algoritmo BM25, y se mostrará cómo implementar pipelines híbridos con su API, brindando tanto teoría como práctica para construir sistemas de búsqueda avanzados.

Fundamentos de la Búsqueda Híbrida en Qdrant

El Dúo Dinámico: Vectores Densos vs. Vectores Dispersos

En el corazón de la búsqueda híbrida se encuentra la interacción de dos tipos de representaciones vectoriales fundamentalmente diferentes pero complementarias: los vectores densos y los dispersos. Comprender sus naturalezas, fortalezas y debilidades es el primer paso para dominarlos.

Vectores Densos (el Artista conceptual)

Un vector denso es una representación numérica donde la mayoría de los valores son distintos de cero. Cada dimensión en este vector no corresponde a una palabra específica, sino que captura una faceta abstracta del significado semántico, aprendida por un modelo de inteligencia artificial.

  • Metáfora: un vector denso es como el resumen de un crítico de arte sobre una pintura. No describe cada pincelada individual, sino que captura la esencia, el estilo, la emoción y el contexto de la obra en su totalidad.
  • Generación: estos vectores son el producto de modelos de deep learning, principalmente arquitecturas Transformer como BERT o Sentence-Transformers. Estos modelos se entrenan con vastos corpus de texto para aprender a codificar el significado del lenguaje en un espacio vectorial de alta dimensionalidad.
  • Caso de Uso: son ideales para la búsqueda semántica, donde la consulta y los documentos pueden no compartir ninguna palabra clave pero sí el mismo significado. Permiten encontrar “ropa de invierno” cuando se busca “abrigos y bufandas”.

Vectores Dispersos (el Archivista meticuloso)

Un vector disperso, en contraste, es un vector de muy alta dimensionalidad donde la abrumadora mayoría de los elementos son cero. Solo las dimensiones que corresponden a términos específicos (palabras) presentes en el documento tienen valores no nulos. Una característica clave en Qdrant es que estos vectores no tienen una longitud fija; su tamaño se adapta dinámicamente, lo que los hace extremadamente eficientes en términos de almacenamiento.

  • Metáfora: un vector disperso es como el índice de un libro de texto. No contiene el texto completo, pero indica con precisión absoluta en qué páginas aparece el término “fotosíntesis”. Es un especialista en la localización exacta.
  • Generación: tradicionalmente, se generan mediante algoritmos estadísticos como TF-IDF y, de manera más prominente en la búsqueda moderna, BM25. También existen modelos neuronales más nuevos, como SPLADE, que aprenden a generar representaciones dispersas semánticamente enriquecidas.
  • Caso de Uso: son indispensables para la búsqueda por palabras clave, donde la coincidencia exacta de términos como “iPhone 15 Pro Max”, un nombre propio o un código de error específico es crítica y no negociable.

La siguiente tabla resume las diferencias clave, sirviendo como una herramienta de diagnóstico para determinar por qué un enfoque híbrido es a menudo la estrategia óptima.

La precisión de la Búsqueda por palabras claves y BM25

La búsqueda por palabras clave, o búsqueda léxica, opera sobre los vectores dispersos. Su eficiencia proviene de una estructura de datos clásica en la recuperación de información: el índice invertido. Este índice mapea cada término del vocabulario a una lista de los documentos que lo contienen, permitiendo una recuperación casi instantánea sin necesidad de escanear cada documento.

En el centro de la puntuación de relevancia para esta búsqueda se encuentra Okapi BM25. Considerado el estándar de oro, BM25 es una evolución sofisticada del venerable TF-IDF, desarrollado en los años 80 y 90 y cuya robustez lo mantiene relevante incluso hoy en día.

El poder de BM25 no radica en un simple conteo de palabras, sino en un refinado modelo probabilístico que intenta responder a la pregunta: “¿Cuál es la probabilidad de que este documento sea relevante para esta consulta?”. Su éxito duradero se debe a cómo equilibra elegantemente múltiples heurísticas de relevancia en una única fórmula cohesiva, lo que explica por qué sigue siendo una “línea de base difícil de superar”. La fórmula general para puntuar un documento

Analicemos sus componentes clave:

  • TF (Term Frequency), f(qi​,D): mide la frecuencia con la que un término de la consulta aparece en un documento. Sin embargo, BM25 introduce el concepto crucial de saturación de frecuencia de término. A diferencia de TF-IDF, que asume una relación lineal, BM25 entiende que la relevancia no aumenta infinitamente con cada aparición de una palabra. La décima mención de “perro” en un documento no lo hace diez veces más relevante que la primera. Este efecto de saturación, controlado por el parámetro k1​, evita que documentos largos llenos de repeticiones de palabras clave dominen injustamente los resultados.
  • IDF (Inverse Document Frequency), IDF(qi​): este componente otorga más peso a los términos que son raros en toda la colección de documentos y menos peso a los términos comunes como “el”, “un” o “es”. Su fundamento reside en la teoría de la información: un término raro como “cromodinámica” transmite mucha más información y es un indicador de relevancia más fuerte que un término común como “ciencia”.
  • Normalización de Longitud del Documento: El término ∣D∣ avgdl​ compara la longitud del documento actual (∣D∣) con la longitud promedio de todos los documentos en la colección (avgdl). Controlado por el parámetro b, este componente ajusta la puntuación para no penalizar en exceso a los documentos largos (que naturalmente tienen más probabilidades de tener frecuencias de término más altas) ni favorecer desproporcionadamente a los documentos muy cortos.
  • Parámetros de Ajuste (k1​ y b): Estos son los diales que permiten adaptar el comportamiento de BM25 a las características específicas de un conjunto de datos. k1​ controla la rapidez con la que la frecuencia de un término se satura, mientras que b ajusta la intensidad de la normalización de la longitud del documento. Aunque los valores por defecto (típicamente k1 ​∈ [1.2,2.0] y b = 0.75) funcionan bien en general, su ajuste puede optimizar aún más la relevancia.

Puesta en Marcha: Preparando el Entorno Qdrant

Antes de construir nuestros pipelines de búsqueda, es necesario configurar el entorno de trabajo. Este proceso es sencillo y se basa en herramientas estándar como Docker y Python.

Instalación de Dependencias

El primer paso es instalar las bibliotecas de Python necesarias. El cliente de Qdrant se puede instalar con el extra [fastembed], que agrupa las dependencias para la generación de embeddings tanto densos como dispersos, simplificando la configuración.

pip install "qdrant-client[fastembed]>=1.14.2"

Es importante asegurarse de utilizar versiones compatibles de las bibliotecas para evitar conflictos, como se especifica en la documentación.

Ejecución de Qdrant con Docker

La forma más sencilla y recomendada de ejecutar Qdrant es a través de su imagen oficial de Docker. El siguiente comando descargará la imagen si es necesario y pondrá en marcha un contenedor.

docker run -p 6333:6333 -p 6334:6334 \
   -v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
   qdrant/qdrant

Conexión con el Cliente de Qdrant

Una vez que la instancia de Qdrant está en ejecución, la interacción desde Python se realiza a través del QdrantClient.

from qdrant_client import QdrantClient

# Instanciar el cliente para conectarse a la instancia local de Qdrant
client = QdrantClient(host="localhost", port=6333)

Preparación y lectura de Datos

Para los ejemplos de este artículo, se utilizará un conjunto de datos de startups disponible públicamente. Primero, se descarga el archivo JSON.

wget https://storage.googleapis.com/generall-shared-data/startups_demo.json

Luego, se utiliza Python para leer este archivo y prepararlo para la inserción en Qdrant. Los datos se estructurarán en una lista de documentos para la vectorización y una lista de metadatos que servirán como payload para cada punto.

import json

documents_for_insertion = []
metadata_payloads = []

with open("startups_demo.json", "r") as f:
    for line in f:
        obj = json.loads(line)

        # El texto principal se usará para generar los vectores
        documents_for_insertion.append(obj.get("description", ""))

        # El resto de los datos se convierte en el payload
        metadata_payloads.append(obj)

Con el entorno configurado y los datos listos, ya se puede proceder a implementar la primera capa de nuestra búsqueda híbrida: la búsqueda por coincidencia exacta.

Búsqueda por Coincidencia Exacta: Vectores Dispersos en Acción

Para implementar una búsqueda léxica robusta, primero se debe configurar Qdrant para manejar vectores dispersos y luego poblarlo con datos codificados mediante BM25.

Creación de una Colección para BM25

El primer paso es crear una “colección” en Qdrant, esta colección debe ser configurada explícitamente para soportar vectores dispersos.

from qdrant_client import models

collection_name = "startups_sparse_search"
sparse_vector_name = "bm25_text"

client.create_collection(
    collection_name=collection_name,
    vectors_config={}, # No se necesitan vectores densos para este ejemplo
    sparse_vectors_config={
        sparse_vector_name: models.SparseVectorParams(
            modifier=models.Modifier.IDF
        )
    },
    timeout=60
)

En este código, sparse_vectors_config le indica a Qdrant que prepare un índice especializado para un vector disperso con el nombre bm25_text.

FastEmbed y su Implementación de BM25

Una de las mayores simplificaciones en el ecosistema de Qdrant es la integración nativa con la biblioteca FastEmbed. FastEmbed incluye una implementación lista para usar que genera vectores dispersos basados en BM25. Esto revela una poderosa abstracción de diseño: para el cliente de Qdrant, el algoritmo BM25 se trata como cualquier otro "modelo" de embedding. Acepta texto y produce un vector. Esta unificación de la interfaz (texto -> vector) para métodos tan dispares como un Transformer (que produce vectores densos) y BM25 (que produce vectores dispersos) es un diseño deliberado que reduce drásticamente la complejidad para el desarrollador. No es necesario gestionar dos flujos de trabajo de preprocesamiento separados; simplemente se define el "modelo" a utilizar, y el cliente se encarga del resto.

Inserción de Puntos en la Colección

Con la colección creada, el siguiente paso es codificar los documentos como vectores dispersos BM25 e insertarlos. El cliente de Qdrant maneja esto de manera transparente.

import uuid

points = [
    models.PointStruct(
        id=uuid.uuid4().hex,
        vector={sparse_vector_name: models.Document(
            text=v_text, 
            model="Qdrant/bm25"
        )},
        payload=v_payload
    )
    for v_text, v_payload in zip(documents_for_insertion, metadata_payloads)
]

# Insertar en batches pequeños para evitar timeout
batch_size = 50

for i in range(0, len(points), batch_size):
    batch = points[i:i+batch_size]
    client.upsert(
        collection_name=collection_name,
        points=batch,
        wait=True  # Espera confirmación del servidor
    )

La API de Consulta Universal de Qdrant

Para pasar de búsquedas simples a pipelines complejos, Qdrant ofrece un punto de entrada unificado y extraordinariamente flexible: el método .query_points. Esta API es mucho más que una simple búsqueda de k-vecinos más cercanos; es una interfaz composable que permite construir consultas de múltiples pasos y lógicas de búsqueda híbrida en una única llamada, reduciendo la latencia de red y simplificando el código de la aplicación.

El poder de .query_points reside en su capacidad para combinar varios parámetros que definen las etapas de la búsqueda. La siguiente tabla desglosa los componentes clave que se utilizarán para construir las búsquedas multi-etapa e híbridas en las secciones posteriores.

Con este vocabulario establecido, ahora es posible construir flujos de recuperación de información mucho más sofisticados.

Ejecución de una Búsqueda con Vectores Dispersos

Una vez que los datos están indexados, realizar una búsqueda es sencillo. La consulta se dirige explícitamente al vector disperso que se ha creado.

query_text = "A mobile application for booking travel tickets"

search_results = client.query_points(
    collection_name=collection_name,
    query=models.Document(
        text=query_text,
        model="Qdrant/bm25",
    ),
    using=sparse_vector_name,
    limit=3,
    with_payload=True,
)

for hit in search_results.points:
    print(f"Score: {hit.score}\n  Name: {hit.payload['name']}\n  Description: {hit.payload['description']}\n\n")
Score: 29.27134
  Name: Busbud
  Description: Making Bus Travel Easy.
Busbud makes bus travel easy. Busbud makes it a breeze to search, compare and book city-to-city bus tickets, anywhere in the world. Busbud.com and the free mobile application are the most comprehensive source of city-to-city bus schedules and tickets around the ...

Score: 24.56239
  Name: Distribusion Technologies
  Description: Simplifying Intercity Bus Distribution
Distribusion wants to make booking an intercity bus ticket as simple as booking a flight or hotel. We connect intercity bus operators with travel agencies, travel websites or mobility apps in a worldwide distribution network.

Score: 21.969345
  Name: Chauf4U
  Description: Online/Mobile Platform Connecting Tourism Businesses with Consumers
The company uses an online or mobile application to receive transportation requests and then sends these trip requests to their drivers and/or bases. Customers will use the website or mobile app to hail a ride, book future reservations or purchase tickets for local ...

Análisis de los resultados y puntuaciones

Al examinar los resultados, es fundamental notar que las puntuaciones devueltas por una búsqueda BM25 no son puntuaciones de similitud de coseno en un rango normalizado. Son puntuaciones de relevancia logarítmicas que pueden ser positivas y no están acotadas de la misma manera. Este hecho es de vital importancia, ya que subraya por qué no se pueden simplemente “sumar” o “promediar” las puntuaciones de una búsqueda dispersa y una búsqueda densa. Se necesita un método de fusión más sofisticado, como se verá más adelante.

Búsqueda Multi-Etapa: Refinando Resultados con prefetch

Una de las estrategias más efectivas en la recuperación de información moderna es el enfoque “coarse-to-fine” o de “grano grueso a fino”. La idea es simple pero poderosa: en lugar de aplicar un método de búsqueda muy preciso pero computacionalmente costoso a toda la base de datos, se divide el proceso en dos etapas.

  • Etapa 1 (Coarse Retrieval): se utiliza un método rápido y “barato” (como una búsqueda con vectores de baja dimensionalidad o cuantizados) para recuperar un conjunto amplio de candidatos potencialmente relevantes (por ejemplo los 100 mejores).
  • Etapa 2 (Fine Re-ranking): se aplica un método más lento pero mucho más preciso (como un modelo de re-ranking o vectores de alta precisión) únicamente sobre ese pequeño conjunto de candidatos para obtener la clasificación final.

Metáfora: este proceso es análogo a buscar un libro en una biblioteca gigante. Primero, se va a la sección correcta, como “Ciencia Ficción” (la recuperación coarse). Luego, en lugar de revisar toda la biblioteca, se examinan cuidadosamente los lomos de los libros solo en esa sección para encontrar el título exacto (el re-ranking fine).

La implementación de prefetch en Qdrant es una característica de rendimiento crítica que permite este patrón de manera eficiente. No se trata simplemente de una conveniencia de la API; es una optimización fundamental. Al aplicar la consulta principal solo sobre los resultados del prefetch, se restringe drásticamente el dominio de la segunda búsqueda, la más costosa. Esto tiene implicaciones directas en la latencia y el costo computacional, haciendo que el uso de modelos de vanguardia como ColBERT o cross-encoders sea práctico en entornos de producción.

Implementación en Qdrant

Para demostrarlo, se creará una colección con dos tipos de vectores densos: uno pequeño y rápido (“dense_small”) y otro más grande y preciso (“dense_large”).

from qdrant_client import models
import uuid

collection_name = "multistage_collection"
documents_for_insertion = documents_for_insertion[:1000]
metadata_payloads = metadata_payloads[:1000]

# Crear una colección con dos vectores densos con nombre
client.create_collection(
    collection_name=collection_name,
    vectors_config={
        "dense_small": models.VectorParams(size=384, distance=models.Distance.COSINE),
        "dense_large": models.VectorParams(size=768, distance=models.Distance.COSINE)
    },
    timeout=60
)

# Aquí se insertan puntos que contengan ambos vectores
points = [ 
    models.PointStruct(
        id=uuid.uuid4().hex,
        vector={
            "dense_small": models.Document(
                text=v_text,
                model="BAAI/bge-small-en",
            ),
            "dense_large": models.Document(
                text=v_text, 
                model="BAAI/bge-base-en",
            ),
        },
        payload=v_payload
    )
    for v_text, v_payload in zip(documents_for_insertion, metadata_payloads)
]

# Insertar en batches pequeños para evitar timeout
batch_size = 50

for i in range(0, len(points), batch_size):
    batch = points[i:i+batch_size]
    client.upsert(
        collection_name=collection_name,
        points=batch,
        wait=True  # Espera confirmación del servidor
    )

Ahora, se puede ejecutar una consulta multi-etapa. La sub-consulta prefetch usará el vector dense_small para obtener 20 candidatos. La consulta principal luego aplicará una búsqueda con el vector dense_large solo sobre esos 20 candidatos para encontrar los 5 mejores.

query_text = "What loyalty platforms are designed to strengthen the identity of local communities?"

search_results = client.query_points(
    collection_name=collection_name,
    prefetch=[
        models.Prefetch(
            query=models.Document(
                text=query_text,
                model="BAAI/bge-small-en",
            ),
            using="dense_small",
            limit=20,
        ),
    ],
    query=models.Document(
        text=query_text,
        model="BAAI/bge-base-en", 
    ),
    using="dense_large",
    limit=5,
    with_payload=True,
)

for hit in search_results.points:
    print(f"Score: {hit.score}\n  Name: {hit.payload['name']}\n  Description: {hit.payload['description']}\n\n")
Score: 0.8429852
  Name: SweetPerk
  Description: Hyper-local mobile loyalty
SweetPerk is a shopper incentive and loyalty platform that is built and branded to the unique identity of a community to promote local commerce. It partners with business improvement districts to build a locally co-branded mobile app which creates local affiliation ...

Score: 0.8353574
  Name: FanFueled
  Description: FanFueled Community Engagement Systems 
FanFueled Engagement Systems empower brands to build, congregate, mobilize and monetize fan communities better than any other loyalty platform on the market.

Score: 0.8275845
  Name: Invitation codes
  Description: The referral community
Invitation App is a social network where people post their referral codes and collect rewards on autopilot.

Score: 0.8259291
  Name: Ox&Pen
  Description: loyalty and rewads program focused on generating repeat business for local merchants
Ox&Pen is a web and mobile based universal loyalty and rewards program that provides local business owners with a technology-based marketing platform in which to attract and retain customers. Our program rewards members with points for purchases, check-ins, and ...

Score: 0.8107947
  Name: UberLoop
  Description: Mobile rewards, loyalty and marketing platform
UberLoop is a mobile rewards, loyalty, and marketing platform for businesses to attract, engage, and retain customers.

Este patrón es la base para la búsqueda híbrida y otras técnicas avanzadas, demostrando cómo la arquitectura de Qdrant está diseñada para la eficiencia y la precisión.

La Sinergia Definitiva: Búsqueda Híbrida

La búsqueda híbrida representa la culminación de los conceptos anteriores. Utiliza múltiples métodos de búsqueda, típicamente la búsqueda semántica (vectores densos) y la búsqueda por palabras clave (vectores dispersos), y fusiona sus resultados para producir una única lista de clasificación que aprovecha las fortalezas de ambos enfoques.

Antes de la implementación, es crucial distinguir dos términos clave:

  • Fusión: el proceso de combinar múltiples listas de resultados clasificados (por ejemplo, una de la búsqueda densa y otra de la dispersa) en una sola lista unificada.
  • Re-ranking: El proceso de tomar una única lista de resultados y reordenarla utilizando un criterio diferente, como se vio en la sección de búsqueda multi-etapa.

Reciprocal Rank Fusion (RRF): el estándar para la Fusión

Cuando se tienen dos listas de resultados con sistemas de puntuación incompatibles (similitud de coseno para densos, puntuaciones logarítmicas para BM25), una simple suma o promedio de puntuaciones es ineficaz. La solución estándar de la industria es Reciprocal Rank Fusion (RRF). La adopción de RRF no es exclusiva de Qdrant; plataformas como Azure AI Search y OpenSearch, junto con el consenso de la comunidad de expertos, la han establecido como la mejor práctica de facto. Aprender RRF no es solo aprender una característica de Qdrant, sino un concepto fundamental y transferible en la recuperación de información moderna.

La belleza de RRF radica en su simplicidad y robustez. Ignora los valores de puntuación absolutos y se centra únicamente en el rango (la posición) de un documento en cada lista de resultados. Esto lo hace inmune a las diferentes escalas de puntuación y resistente a valores atípicos.

El algoritmo funciona de la siguiente manera :

  • Se obtienen las listas de resultados clasificadas de cada búsqueda (densa y dispersa).
  • Para cada documento en cada lista, se calcula una puntuación de rango recíproco con la fórmula: 1 / (k + rank).
  • El rank es la posición del documento en la lista (1, 2, 3,...).
  • k es una constante (comúnmente establecida en 60) que sirve para amortiguar el impacto de los documentos con un ranking muy bajo, evitando que dominen la puntuación.
  • Para cada documento único que aparece en cualquiera de las listas, se suman sus puntuaciones RRF de todas las listas.
  • La lista de resultados final se ordena según esta nueva puntuación RRF combinada.

Implementación de Búsqueda Híbrida en Qdrant

Gracias a la API query_points, implementar la búsqueda híbrida con RRF en Qdrant es notablemente elegante.

import requests

# documentos de libros 
docs_url = 'https://drive.usercontent.google.com/u/0/uc?id=1zKMTAd7kXTkBuuW0JY68D4Y4wQNxMLR5&export=download'
docs_response = requests.get(docs_url)
documents = docs_response.json()
from qdrant_client import models

collection_name="hybrid_collection"

# Crear coleccion
client.create_collection(
    collection_name=collection_name,
    vectors_config={
        "dense_text": models.VectorParams(
            size=512,
            distance=models.Distance.COSINE,
        ),
    },
    sparse_vectors_config={
        "sparse_text": models.SparseVectorParams(
            modifier=models.Modifier.IDF,
        )
    },
    timeout=60
)
import uuid

# Cargar puntos en los vectores
client.upsert(
    collection_name=collection_name,
    points=[
        models.PointStruct(
            id=uuid.uuid4().hex,
            vector={
                "dense_text": models.Document(
                    text= f"{doc['descripcion']} {doc['clasificacion']}",
                    model="jinaai/jina-embeddings-v2-small-en",
                ),
                "sparse_text": models.Document(
                    text=f"{doc['descripcion']} {doc['clasificacion']}",
                    model="Qdrant/bm25",
                ),
            },
            payload=doc
        )
        for doc in documents
    ]
)
def busqueda(query_text, v_limit=2):
    # Busqueda hibrida
    hybrid_results = client.query_points(
        collection_name=collection_name,
        # Se definen dos prefetch, uno para cada tipo de búsqueda
        prefetch=[
            # Prefetch para la búsqueda densa
            models.Prefetch(
                query=models.Document(text=query_text, model="jinaai/jina-embeddings-v2-small-en"),
                using="dense_text", 
                limit=(v_limit * 4)),
            # Prefetch para la búsqueda dispersa
            models.Prefetch(
                query=models.Document(text=query_text, model="Qdrant/bm25"), 
                using="sparse_text", 
                limit=(v_limit * 4))
        ],
        # La consulta principal es una operación de fusión RRF    
        query=models.FusionQuery(fusion=models.Fusion.RRF),
        with_payload=True,
        limit=v_limit # Devolver los 2mejores resultados después de la fusión
    )  

    return hybrid_results
query_text = "¿Qué novela combina elementos políticos con una visión pesimista del futuro?"

results = busqueda(query_text)

for hit in results.points:
    print(f"Score RRF: {hit.score}")
    print(f"  Titulo: {hit.payload['titulo']}, Autor: {hit.payload['autor']}, Clasficacion: {hit.payload['clasificacion']}")
    print(f"  Descripcion: {hit.payload['descripcion']}\n\n")
Score RRF: 0.5
  Titulo: La metamorfosis (97), Autor: Jane Austen, Clasficacion: Romance
  Descripcion: La historia de Elizabeth Bennet y su relación con el orgulloso Sr. Darcy en la Inglaterra del siglo XIX.

Score RRF: 0.5
  Titulo: Orgullo y prejuicio (8), Autor: George Orwell, Clasficacion: Distopía
  Descripcion: Una novela distópica sobre un régimen totalitario que vigila todos los aspectos de la vida humana.

En esta única llamada a la API, Qdrant ejecuta las búsquedas densa y dispersa en paralelo, toma los resultados de ambas, aplica el algoritmo RRF internamente y devuelve una única lista de resultados clasificada, que combina la relevancia semántica y léxica.

Explorando Horizontes Avanzados

Si bien la búsqueda híbrida con BM25 y vectores densos es una técnica extremadamente potente y un estándar de la industria, el campo de la recuperación de información sigue evolucionando a un ritmo vertiginoso. Qdrant no solo soporta las mejores prácticas actuales, sino que también proporciona las herramientas para implementar técnicas de vanguardia que apuntan a una precisión aún mayor. Estas capacidades avanzadas posicionan a Qdrant como un motor de recuperación fundamental para arquitecturas de Generación Aumentada por Recuperación (RAG) de próxima generación, que requieren estrategias de recuperación mucho más sofisticadas que una simple búsqueda por similitud.

Re-ranking con Cross-Encoders

Los modelos de embedding como BERT, utilizados para generar vectores densos, son bi-encoders. Procesan la consulta y el documento por separado para crear sus respectivos vectores y luego calculan la similitud. Un cross-encoder, en cambio, toma el par (consulta, documento) como una única entrada y lo procesa simultáneamente. Esto le permite modelar interacciones mucho más finas y complejas entre las palabras de la consulta y del documento, lo que generalmente resulta en una puntuación de relevancia mucho más precisa.

Sin embargo, esta precisión tiene un costo: los cross-encoders son computacionalmente muy intensivos y demasiado lentos para buscar en una base de datos de millones de documentos. Por lo tanto, su rol no es el de la recuperación inicial, sino el de un re-clasificador de etapa final. El flujo de trabajo típico es:

  • Realizar una búsqueda híbrida (o multi-etapa) para obtener los N mejores candidatos (por ejemplo, N=50).
  • Pasar estos 50 pares (consulta, candidato) a un modelo cross-encoder.
  • Reordenar los 50 candidatos según las puntuaciones del cross-encoder para obtener la clasificación final.

El ecosistema de Qdrant está preparado para este patrón, con trabajos de integración para incorporar modelos cross-encoder en FastEmbed, permitiendo que este paso de re-ranking se realice de manera más fluida.

Representaciones Multi-Vectoriales

Otra frontera en la investigación es ir más allá de representar un documento con un único vector denso. Modelos como ColBERT (Contextualized Late Interaction over BERT) proponen representar un documento con un conjunto de vectores, típicamente un vector por cada token (palabra) del documento. Esto se conoce como “interacción tardía”. La relevancia ya no se calcula con una única similitud de coseno, sino a través de un proceso más complejo (MaxSim) que compara cada vector de la consulta con todos los vectores del documento, sumando las similitudes máximas. Esto permite una coincidencia a nivel de token extremadamente precisa, capturando matices que un único vector promediado podría perder.

El desafío es obvio: almacenar e indexar cientos de vectores por cada documento es extremadamente ineficiente y consume una cantidad masiva de memoria y recursos computacionales. La solución de Qdrant a este problema es, de nuevo, elegante y eficiente. Permite definir y almacenar campos multi-vectoriales sin un índice HNSW. Estos vectores a nivel de token se utilizan exclusivamente en una etapa de re-ranking, aplicada sobre los candidatos recuperados mediante un eficiente vector denso único.

Para configurar esto, se crea una colección que deshabilita explícitamente el índice HNSW para el campo multi-vectorial.

La consulta se realiza entonces utilizando el patrón de búsqueda multi-etapa, donde el prefetch utiliza el vector dense rápido y la consulta principal utiliza el campo colbert_multivec para un re-ranking preciso basado en MaxSim, todo en una sola llamada a la API.

Este recorrido ha mostrado que la búsqueda moderna no es una elección entre lo léxico y lo semántico, sino una colaboración estratégica entre ambos enfoques. La precisión de los vectores dispersos — potenciada por BM25 — y la comprensión contextual de los vectores densos forman juntos la base de los sistemas de búsqueda actuales.

Qdrant se presenta como mucho más que una base de datos vectorial: es un motor flexible para recuperar información. Su API unificada, .query_points, junto con características como prefetch y la fusión RRF, permite construir desde búsquedas simples hasta complejos pipelines híbridos con re-ranking multi-etapa.

Con soporte para tecnologías avanzadas como cross-encoders y modelos multi-vectoriales como ColBERT, Qdrant se posiciona como pieza clave en arquitecturas RAG de última generación. Se invita al lector a experimentar con los conceptos y ejemplos aquí presentados, ajustándolos a sus propios datos y necesidades. Con las herramientas que Qdrant ofrece, el futuro de la búsqueda inteligente está al alcance.

¡Conectemos!

👉 LinkedIn 👉 Twitter/X

Referencia

[embed]llm-zoomcamp/02-vector-search at main · DataTalksClub/llm-zoomcamp LLM Zoomcamp - a free online course about real-life applications of LLMs. In 10 weeks you will learn how to build an AI…github.com

[embed]An Introduction to Vector Databases - Qdrant Discover what a vector database is, its core functionalities, and real-world applications.qdrant.tech

[embed]Hybrid Queries - Qdrant Qdrant is an Open-Source Vector Database and Vector Search Engine written in Rust. It provides fast and scalable vector…qdrant.tech

[embed]Modern Sparse Neural Retrieval: From Theory to Practice - Qdrant A comprehensive guide to modern sparse neural retrievers: COIL, TILDEv2, SPLADE, and more. Find out how they work and…qdrant.tech

[embed]Setup Hybrid Search with FastEmbed - Qdrant Qdrant is an Open-Source Vector Database and Vector Search Engine written in Rust. It provides fast and scalable vector…qdrant.tech

[embed]How to Use Multivector Representations with Qdrant Effectively - Qdrant Qdrant is an Open-Source Vector Database and Vector Search Engine written in Rust. It provides fast and scalable vector…qdrant.tech

[embed]Qdrant Summer of Code 2024 - ONNX Cross Encoders in Python - Qdrant A summary of my work and experience at Qdrant Summer of Code 2024.qdrant.tech

https://qdrant.tech/documentation/concepts/vectors/

[embed]A guide to hybrid search (using Splade) in Qdrant vector database Vector data bases are hot commodity now, owing to thier widespread use in RAG and other NLP applications. In this blog…viraajkadam.medium.com

[embed]RAG — VII (Reranking with RRF) Reciprocal Rank Fusion (RRF) is a rank aggregation technique designed to merge multiple ranked lists from diverse…medium.com

[embed]Unlocking the Power of BM25: Why It’s Outshining TF-IDF in the World of Search Hi fellow readers… If you’ve ever typed a query into a search engine and marveled at how quickly it returns relevant…medium.com

[embed]Sparse Versus Dense Vectors What are Sparse Vectorsmichaelcizmar.com

[embed]Advanced Multi-Stage, Multi-Vector Querying Using the ColBERT Approach in Qdrant Smart Retrieval → Brilliant Answering → Elevating AI Performancemedium.com

[embed]Qdrant/bm25 · Hugging Face We're on a journey to advance and democratize artificial intelligence through open source and open science.huggingface.co

[embed]Qdrant Hybrid Search - LlamaIndex Qdrant supports hybrid search by combining search results from sparse and dense vectors. dense vectors are the ones you…docs.llamaindex.ai

[embed]Hybrid search scoring (RRF) - Azure AI Search Describes the Reciprocal Rank Fusion (RRF) algorithm used to unify search scores from parallel queries in Azure AI…learn.microsoft.com

[embed]Dense Vectors: Capturing Meaning with Code | Pinecone Pinecone is a vector database for storing and searching through dense vectors. Why would you ever want to do that? Keep…www.pinecone.io

[embed]Mastering BM25: A Deep Dive into the Algorithm and Its Application in Milvus - Zilliz Learn We can easily implement the BM25 algorithm to turn a document and a query into a sparse vector with Milvus. Then, these…zilliz.com

[embed]Introducing reciprocal rank fusion for hybrid search - OpenSearch Learn how OpenSearch 2.19's new Reciprocal Rank Fusion (RRF) enhances hybrid search by intelligently merging results…opensearch.org

[embed]Comparing BM25 vs TF-IDF: Which is Better? Discover the differences between BM25 vs TF-IDF in this deep dive comparison. Find out which model excels in…myscale.com

[embed]Okapi BM25 - Wikipedia In information retrieval, Okapi BM25 ( BM is an abbreviation of best matching) is a ranking function used by search…en.wikipedia.org

[embed]Qdrant Sparse Vector | 🦜️🔗 LangChain Qdrant is an open-source, high-performance vector search engine/database.python.langchain.com

[embed]TF-IDF and BM25 for RAG- a complete guide TF-IDF and BM25 are commonly used techniques in information retrieval. while TF stands for Term-Frequency, IDF stands…www.ai-bites.net


메타데이터
post_id
f8e82d5afb06
slug
dominando-la-busqueda-hibrida-qdrant-vectores-dispersos-y-fusion-f8e82d5afb06
url
https://medium.com/@j92riquelme/dominando-la-busqueda-hibrida-qdrant-vectores-dispersos-y-fusion-f8e82d5afb06
canonical_url
https://medium.com/@j92riquelme/dominando-la-busqueda-hibrida-qdrant-vectores-dispersos-y-fusion-f8e82d5afb06
author_url
https://medium.com/@j92riquelme
status
ok
fetched_at
2026-07-25 21:11:06