Open In Colab GitHub Repository

RAG использует гибридный поиск с Milvus и LlamaIndex

Гибридный поиск использует сильные стороны семантического поиска и подбора ключевых слов для получения более точных и контекстно релевантных результатов. Сочетая преимущества семантического поиска и подбора ключевых слов, гибридный поиск особенно эффективен в сложных задачах поиска информации.

В этом блокноте показано, как использовать Milvus для гибридного поиска в конвейерах LlamaIndex RAG. Мы начнем с рекомендуемого по умолчанию гибридного поиска (семантический + BM25), а затем рассмотрим другие альтернативные методы разреженного встраивания и настройку гибридного реранкера.

Необходимые условия

Установите зависимости

Прежде чем приступить к работе, убедитесь, что у вас установлены следующие зависимости:

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

Если вы используете Google Colab, вам может потребоваться перезапустить среду выполнения (перейдите в меню "Runtime" в верхней части интерфейса и выберите "Restart session" из выпадающего меню).

Настройка учетных записей

В этом учебнике используется OpenAI для встраивания текста и генерации ответов. Вам необходимо подготовить ключ API OpenAI.

import openai

openai.api_key = "sk-"

Чтобы использовать векторное хранилище Milvus, укажите свой сервер Milvus URI (и, по желанию, TOKEN). Запустить сервер Milvus можно, следуя руководству по установке Milvus или просто бесплатно попробовав Zilliz Cloud.

Полнотекстовый поиск в настоящее время поддерживается в Milvus Standalone, Milvus Distributed и Zilliz Cloud, но пока не поддерживается в Milvus Lite (планируется к внедрению в будущем). За дополнительной информацией обращайтесь по адресу support@zilliz.com.

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

Загрузка примеров данных

Выполните следующие команды, чтобы загрузить примеры документов в каталог "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'

Затем используйте SimpleDirectoryReaderLoad для загрузки эссе "Над чем я работал" Пола Грэма:

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 ...

Гибридный поиск с BM25

В этом разделе показано, как выполнить гибридный поиск с помощью BM25. Для начала мы инициализируем MilvusVectorStore и создадим индекс для документов примера. В конфигурации по умолчанию используются:

  • Плотные вкрапления из модели вкраплений по умолчанию (OpenAI's text-embedding-ada-002).
  • BM25 для полнотекстового поиска, если enable_sparse равно True
  • RRFRanker с k=60 для объединения результатов, если включен гибридный поиск.
# 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').

Вот более подробная информация об аргументах для настройки плотных и разреженных полей на сайте MilvusVectorStore:

плотное поле

  • enable_dense (bool): Булевский флаг для включения или отключения плотного встраивания. По умолчанию - True.
  • dim (int, optional): Размерность векторов встраивания для коллекции.
  • embedding_field (str, optional): : Имя поля плотного встраивания для коллекции, по умолчанию DEFAULT_EMBEDDING_KEY.
  • index_config (dict, optional): Конфигурация, используемая для построения индекса плотного встраивания. По умолчанию - None.
  • search_config (dict, optional): Конфигурация, используемая для поиска в плотном индексе Milvus. Обратите внимание, что она должна быть совместима с типом индекса, указанным в index_config. По умолчанию - Нет.
  • similarity_metric (str, optional): Метрика сходства, используемая для плотного встраивания; в настоящее время поддерживаются IP, COSINE и L2.

поле sparse (разреженное)

  • enable_sparse (bool): Булевский флаг для включения или отключения плотного встраивания. По умолчанию False.
  • sparse_embedding_field (str): Имя поля разреженного встраивания, по умолчанию DEFAULT_SPARSE_EMBEDDING_KEY.
  • sparse_embedding_function (Union[BaseSparseEmbeddingFunction, BaseMilvusBuiltInFunction], optional): Если enable_sparse равно True, этот объект должен быть предоставлен для преобразования текста в разреженную вставку. Если None, то будет использована функция разреженного вложения по умолчанию (BM25BuiltInFunction), или используйте BGEM3SparseEmbedding для существующей коллекции без встроенных функций.
  • sparse_index_config (dict, optional): Конфигурация, используемая для построения индекса разреженного вложения. По умолчанию - None.

Чтобы включить гибридный поиск на этапе запроса, установите vector_store_query_mode в значение "hybrid". Это позволит объединить и ранжировать результаты как семантического, так и полнотекстового поиска. Проверим на примере запроса: "Чему автор научился в 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.

Настройка анализатора текста

Анализаторы играют важную роль в полнотекстовом поиске, разбивая предложения на лексемы и выполняя лексическую обработку, например, стемминг и удаление стоп-слов. Как правило, они зависят от конкретного языка. Для получения более подробной информации обратитесь к Руководству по анализаторам Milvus.

Milvus поддерживает два типа анализаторов: Встроенные анализаторы и Пользовательские анализаторы. По умолчанию, если enable_sparse имеет значение True, MilvusVectorStore использует BM25BuiltInFunction с настройками по умолчанию, применяя стандартный встроенный анализатор, который токенизирует текст на основе пунктуации.

Чтобы использовать другой анализатор или настроить существующий, вы можете указать значения аргумента analyzer_params при построении BM25BuiltInFunction. Затем задайте эту функцию в качестве sparse_embedding_function в 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)

Гибридный поиск с другими разреженными вкраплениями

Помимо сочетания семантического поиска с BM25, Milvus также поддерживает гибридный поиск с использованием функции разреженного вложения, такой как BGE-M3. В следующем примере для генерации разреженных вкраплений используется встроенный BGEM3SparseEmbeddingFunction.

Сначала нам нужно установить пакет FlagEmbedding:

$ pip install -q FlagEmbedding

Затем построим векторное хранилище и индекс, используя модель OpenAI по умолчанию для денситного вложения и встроенную функцию BGE-M3 для разреженного вложения:

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]

Теперь выполним гибридный поисковый запрос с примером вопроса:

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.

Настройка функции разреженного встраивания

Вы также можете настроить функцию разреженного встраивания, если она наследует от BaseSparseEmbeddingFunction, включая следующие методы:

  • encode_queries: Этот метод преобразует тексты в список разреженных вкраплений для запросов.
  • encode_documents: Этот метод преобразует текст в список разреженных вкраплений для документов.

Выходные данные каждого метода должны соответствовать формату разреженного вкрапления, которое представляет собой список словарей. Каждый словарь должен иметь ключ (целое число), представляющий измерение, и соответствующее значение (float), представляющее величину вкрапления в этом измерении (например, {1: 0.5, 2: 0.3}).

Например, вот реализация пользовательской функции разреженного вложения с использованием 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

Настройка гибридного реранкера

Milvus поддерживает два типа стратегий ранжирования: Reciprocal Rank Fusion (RRF) и Weighted Scoring. По умолчанию в гибридном поиске MilvusVectorStore используется RRF с k=60. Чтобы настроить гибридный ранжировщик, измените следующие параметры:

  • hybrid_ranker (str): Определяет тип ранжировщика, используемого в гибридных поисковых запросах. В настоящее время поддерживаются только ["RRFRanker", "WeightedRanker"]. По умолчанию установлено значение "RRFRanker".
  • hybrid_ranker_params (dict, optional): Параметры конфигурации для гибридного ранжировщика. Структура этого словаря зависит от конкретного используемого ранжировщика:
    • Для "RRFRanker" он должен включать:
      • "k" (int): Параметр, используемый в Reciprocal Rank Fusion (RRF). Это значение используется для расчета ранговых оценок в рамках алгоритма RRF, который объединяет несколько стратегий ранжирования в одну оценку для улучшения релевантности поиска. Если значение не указано, по умолчанию оно равно 60.
    • Для "WeightedRanker" ожидается:
      • "weights" (список float): Список из ровно двух весов:
        1. Вес для компонента плотного вложения.
        2. Эти веса используются для баланса значимости плотных и разреженных компонентов вкраплений в процессе гибридного поиска. Если вес не указан, то по умолчанию он равен [1.0, 1.0].
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.

Попробуйте Managed Milvus бесплатно

Zilliz Cloud работает без проблем, поддерживается Milvus и в 10 раз быстрее.

Начать
Обратная связь

Была ли эта страница полезной?