Milvus
Zilliz
Home
  • Integraciones
  • Home
  • Docs
  • Integraciones

  • Orquestación

  • LlamaIndex

  • Búsqueda híbrida

Open In Colab GitHub Repository

RAG utiliza la búsqueda híbrida con Milvus y LlamaIndex

La búsqueda híbrida aprovecha los puntos fuertes tanto de la recuperación semántica como de la concordancia de palabras clave para ofrecer resultados más precisos y contextualmente relevantes. Al combinar las ventajas de la búsqueda semántica y la concordancia de palabras clave, la búsqueda híbrida es especialmente eficaz en tareas complejas de recuperación de información.

Este cuaderno muestra cómo utilizar Milvus para la búsqueda híbrida en las cadenas RAG de LlamaIndex. Comenzaremos con la búsqueda híbrida predeterminada recomendada (semántica + BM25) y luego exploraremos otros métodos alternativos de incrustación dispersa y la personalización del reranker híbrido.

Requisitos previos

Instalar dependencias

Antes de empezar, asegúrate de tener instaladas las siguientes dependencias:

$ pip install llama-index-vector-stores-milvus
$ pip install llama-index-embeddings-openai
$ pip install llama-index-llms-openai

Si utilizas Google Colab, es posible que tengas que reiniciar el tiempo de ejecución (accede al menú "Tiempo de ejecución" situado en la parte superior de la interfaz y selecciona "Reiniciar sesión" en el menú desplegable).

Configurar cuentas

Este tutorial utiliza OpenAI para la incrustación de texto y la generación de respuestas. Es necesario preparar la clave API de OpenAI.

import openai

openai.api_key = "sk-"

Para utilizar el almacén vectorial Milvus, especifique su servidor Milvus URI (y opcionalmente con el TOKEN). Para iniciar un servidor Milvus, puede configurar un servidor Milvus siguiendo la guía de instalación de Milvus o simplemente probando Zilliz Cloud de forma gratuita.

La búsqueda de texto completo está actualmente soportada en Milvus Standalone, Milvus Distributed y Zilliz Cloud, pero todavía no en Milvus Lite (prevista para una futura implementación). Póngase en contacto con support@zilliz.com para obtener más información.

URI = "http://localhost:19530"
# TOKEN = ""

Cargar datos de ejemplo

Ejecute los siguientes comandos para descargar documentos de ejemplo en el directorio "data/paul_graham":

$ mkdir -p 'data/paul_graham/'
$ wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/paul_graham/paul_graham_essay.txt' -O 'data/paul_graham/paul_graham_essay.txt'

A continuación, utilice SimpleDirectoryReaderLoad para cargar el ensayo "What I Worked On" de Paul Graham:

from llama_index.core import SimpleDirectoryReader

documents = SimpleDirectoryReader("./data/paul_graham/").load_data()

# Let's take a look at the first document
print("Example document:\n", documents[0])
Example document:
 Doc ID: f9cece8c-9022-46d8-9d0e-f29d70e1dbbe
Text: What I Worked On  February 2021  Before college the two main
things I worked on, outside of school, were writing and programming. I
didn't write essays. I wrote what beginning writers were supposed to
write then, and probably still are: short stories. My stories were
awful. They had hardly any plot, just characters with strong feelings,
which I ...

Búsqueda híbrida con BM25

Esta sección muestra cómo realizar una búsqueda híbrida utilizando BM25. Para empezar, inicializaremos MilvusVectorStore y crearemos un índice para los documentos de ejemplo. La configuración por defecto utiliza

  • Incrustaciones densas del modelo de incrustación por defecto (OpenAI's text-embedding-ada-002)
  • BM25 para la búsqueda de texto completo si enable_sparse es True
  • RRFRanker con k=60 para combinar resultados si la búsqueda híbrida está activada.
# Create an index over the documnts
from llama_index.vector_stores.milvus import MilvusVectorStore
from llama_index.core import StorageContext, VectorStoreIndex


vector_store = MilvusVectorStore(
    uri=URI,
    # token=TOKEN,
    dim=1536,  # vector dimension depends on the embedding model
    enable_sparse=True,  # enable the default full-text search using BM25
    overwrite=True,  # drop the collection if it already exists
)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(documents, storage_context=storage_context)
2025-04-17 03:38:16,645 [DEBUG][_create_connection]: Created new connection using: cf0f4df74b18418bb89ec512063c1244 (async_milvus_client.py:547)
Sparse embedding function is not provided, using default.
Default sparse embedding function: BM25BuiltInFunction(input_field_names='text', output_field_names='sparse_embedding').

Aquí hay más información sobre los argumentos para configurar campos densos y dispersos en MilvusVectorStore:

campo denso

  • enable_dense (bool): Un indicador booleano para activar o desactivar la incrustación densa. Por defecto es True.
  • dim (int, optional): La dimensión de los vectores de incrustación para la colección.
  • embedding_field (str, optional): El nombre del campo de incrustación densa para la colección, por defecto DEFAULT_EMBEDDING_KEY.
  • index_config (dict, optional): La configuración utilizada para construir el índice de incrustación densa. Por defecto es None.
  • search_config (dict, optional): La configuración utilizada para buscar en el índice denso de Milvus. Tenga en cuenta que debe ser compatible con el tipo de índice especificado por index_config. Por defecto None.
  • similarity_metric (str, optional): La métrica de similitud a utilizar para la incrustación densa, actualmente soporta IP, COSINE y L2.

campo disperso

  • enable_sparse (bool): Un indicador booleano para activar o desactivar la incrustación dispersa. Por defecto es False.
  • sparse_embedding_field (str): El nombre del campo de incrustación dispersa, por defecto DEFAULT_SPARSE_EMBEDDING_KEY.
  • sparse_embedding_function (Union[BaseSparseEmbeddingFunction, BaseMilvusBuiltInFunction], optional): Si enable_sparse es True, este objeto debe proporcionarse para convertir el texto a una incrustación dispersa. Si None, se utilizará la función de incrustación dispersa por defecto (BM25BuiltInFunction), o se utilizará BGEM3SparseEmbedding dada la colección existente sin funciones incorporadas.
  • sparse_index_config (dict, optional): La configuración utilizada para construir el índice de incrustación dispersa. Por defecto es Ninguno.

Para activar la búsqueda híbrida durante la fase de consulta, establezca vector_store_query_mode en "hybrid". Esto combinará y clasificará los resultados de la búsqueda semántica y de la búsqueda de texto completo. Hagamos una prueba con una consulta de ejemplo: "¿Qué aprendió el autor en Viaweb?":

import textwrap

query_engine = index.as_query_engine(
    vector_store_query_mode="hybrid", similarity_top_k=5
)
response = query_engine.query("What did the author learn at Viaweb?")
print(textwrap.fill(str(response), 100))
The author learned about retail, the importance of user feedback, and the significance of growth
rate as the ultimate test of a startup at Viaweb.

Personalizar el analizador de texto

Los analizadores desempeñan un papel fundamental en la búsqueda de texto completo, ya que descomponen las frases en tokens y llevan a cabo el procesamiento léxico, como la eliminación de palabras vacías y de palabras clave. Suelen ser específicos de cada idioma. Para más detalles, consulte la Guía de analizadores de Milvus.

Milvus admite dos tipos de analizadores: Analizadores incorporados y Analizadores personalizados. Por defecto, si enable_sparse está configurado como True, MilvusVectorStore utiliza BM25BuiltInFunction con las configuraciones por defecto, empleando el analizador incorporado estándar que tokeniza el texto basándose en la puntuación.

Para utilizar un analizador diferente o personalizar el existente, puede proporcionar valores al argumento analyzer_params cuando construya BM25BuiltInFunction. A continuación, establezca esta función como sparse_embedding_function en MilvusVectorStore.

from llama_index.vector_stores.milvus.utils import BM25BuiltInFunction

bm25_function = BM25BuiltInFunction(
    analyzer_params={
        "tokenizer": "standard",
        "filter": [
            "lowercase",  # Built-in filter
            {"type": "length", "max": 40},  # Custom cap size of a single token
            {"type": "stop", "stop_words": ["of", "to"]},  # Custom stopwords
        ],
    },
    enable_match=True,
)

vector_store = MilvusVectorStore(
    uri=URI,
    # token=TOKEN,
    dim=1536,
    enable_sparse=True,
    sparse_embedding_function=bm25_function,  # BM25 with custom analyzer
    overwrite=True,
)
2025-04-17 03:38:48,085 [DEBUG][_create_connection]: Created new connection using: 61afd81600cb46ee89f887f16bcbfe55 (async_milvus_client.py:547)

Búsqueda híbrida con otras incrustaciones dispersas

Además de combinar la búsqueda semántica con BM25, Milvus también admite la búsqueda híbrida utilizando una función de incrustación dispersa como BGE-M3. El siguiente ejemplo utiliza la función BGEM3SparseEmbeddingFunction para generar incrustaciones dispersas.

En primer lugar, necesitamos instalar el paquete FlagEmbedding:

$ pip install -q FlagEmbedding

A continuación, vamos a construir el almacén de vectores y el índice utilizando el modelo por defecto OpenAI para densen incrustación y la incorporada BGE-M3 para incrustación dispersa:

from llama_index.vector_stores.milvus.utils import BGEM3SparseEmbeddingFunction

vector_store = MilvusVectorStore(
    uri=URI,
    # token=TOKEN,
    dim=1536,
    enable_sparse=True,
    sparse_embedding_function=BGEM3SparseEmbeddingFunction(),
    overwrite=True,
)

storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(documents, storage_context=storage_context)
Fetching 30 files: 100%|██████████| 30/30 [00:00<00:00, 68871.99it/s]
2025-04-17 03:39:02,074 [DEBUG][_create_connection]: Created new connection using: ff4886e2f8da44e08304b748d9ac9b51 (async_milvus_client.py:547)
Chunks: 100%|██████████| 1/1 [00:00<00:00,  1.07it/s]

Ahora vamos a realizar una consulta de búsqueda híbrida con una pregunta de ejemplo:

query_engine = index.as_query_engine(
    vector_store_query_mode="hybrid", similarity_top_k=5
)
response = query_engine.query("What did the author learn at Viaweb??")
print(textwrap.fill(str(response), 100))
Chunks: 100%|██████████| 1/1 [00:00<00:00, 17.29it/s]


The author learned about retail, the importance of user feedback, the value of growth rate in a
startup, the significance of pricing strategy, the benefits of working on things that weren't
prestigious, and the challenges and rewards of running a startup.

Personalizar la función de incrustación dispersa

También puedes personalizar la función de incrustación dispersa siempre que herede de BaseSparseEmbeddingFunction, incluyendo los siguientes métodos:

  • encode_queries: Este método convierte los textos en una lista de incrustaciones dispersas para las consultas.
  • encode_documents: Este método convierte textos en listas de incrustaciones dispersas para documentos.

La salida de cada método debe seguir el formato de la incrustación dispersa, que es una lista de diccionarios. Cada diccionario debe tener una clave (un entero) que represente la dimensión, y un valor correspondiente (un flotante) que represente la magnitud de la incrustación en esa dimensión (por ejemplo, {1: 0,5, 2: 0,3}).

Por ejemplo, aquí se muestra la implementación de una función de incrustación dispersa personalizada utilizando BGE-M3:

from FlagEmbedding import BGEM3FlagModel
from typing import List
from llama_index.vector_stores.milvus.utils import BaseSparseEmbeddingFunction


class ExampleEmbeddingFunction(BaseSparseEmbeddingFunction):
    def __init__(self):
        self.model = BGEM3FlagModel("BAAI/bge-m3", use_fp16=False)

    def encode_queries(self, queries: List[str]):
        outputs = self.model.encode(
            queries,
            return_dense=False,
            return_sparse=True,
            return_colbert_vecs=False,
        )["lexical_weights"]
        return [self._to_standard_dict(output) for output in outputs]

    def encode_documents(self, documents: List[str]):
        outputs = self.model.encode(
            documents,
            return_dense=False,
            return_sparse=True,
            return_colbert_vecs=False,
        )["lexical_weights"]
        return [self._to_standard_dict(output) for output in outputs]

    def _to_standard_dict(self, raw_output):
        result = {}
        for k in raw_output:
            result[int(k)] = raw_output[k]
        return result

Personalizar el reranker híbrido

Milvus admite dos tipos de estrategias de reordenación: Reciprocal Rank Fusion (RRF) y Weighted Scoring. El ranker por defecto en MilvusVectorStore hybrid search es RRF con k=60. Para personalizar el ranker híbrido, modifique los siguientes parámetros:

  • hybrid_ranker (str): Especifica el tipo de clasificador utilizado en las consultas de búsqueda híbrida. Actualmente sólo admite ["RRFRanker", "WeightedRanker"]. Por defecto es "RRFRanker".
  • hybrid_ranker_params (dict, optional): Parámetros de configuración del clasificador híbrido. La estructura de este diccionario depende del clasificador específico que se utilice:
    • Para "RRFRanker", debe incluir:
      • "k" (int): Parámetro utilizado en la fusión de rangos recíprocos (RRF). Este valor se utiliza para calcular las puntuaciones de clasificación como parte del algoritmo RRF, que combina múltiples estrategias de clasificación en una única puntuación para mejorar la relevancia de la búsqueda. El valor por defecto es 60 si no se especifica.
    • Para "WeightedRanker", se espera
      • "pesos" (lista de float): Una lista de exactamente dos pesos:
        1. El peso para el componente de incrustación densa.
        2. Estas ponderaciones se utilizan para equilibrar la importancia de los componentes densos y dispersos de las incrustaciones en el proceso de recuperación híbrido. Los pesos por defecto son [1.0, 1.0] si no se especifican.
vector_store = MilvusVectorStore(
    uri=URI,
    # token=TOKEN,
    dim=1536,
    overwrite=False,  # Use the existing collection created in the previous example
    enable_sparse=True,
    hybrid_ranker="WeightedRanker",
    hybrid_ranker_params={"weights": [1.0, 0.5]},
)
index = VectorStoreIndex.from_vector_store(vector_store)
query_engine = index.as_query_engine(
    vector_store_query_mode="hybrid", similarity_top_k=5
)
response = query_engine.query("What did the author learn at Viaweb?")
print(textwrap.fill(str(response), 100))
2025-04-17 03:44:00,419 [DEBUG][_create_connection]: Created new connection using: 09c051fb18c04f97a80f07958856587b (async_milvus_client.py:547)
Sparse embedding function is not provided, using default.
No built-in function detected, using BGEM3SparseEmbeddingFunction().
Fetching 30 files: 100%|██████████| 30/30 [00:00<00:00, 136622.28it/s]
Chunks: 100%|██████████| 1/1 [00:00<00:00,  1.07it/s]


The author learned several valuable lessons at Viaweb, including the importance of understanding
growth rate as the ultimate test of a startup, the significance of user feedback in shaping the
software, and the realization that web applications were the future of software development.
Additionally, the experience at Viaweb taught the author about the challenges and rewards of running
a startup, the value of simplicity in software design, and the impact of pricing strategies on
attracting customers.

Try Managed Milvus for Free

Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

Get Started
Feedback

¿Fue útil esta página?