Open In Colab GitHub Repository

使用 Milvus 和 LlamaIndex 混合搜尋的 RAG

混合搜尋利用語意檢索和關鍵字比對兩者的優勢,提供更精確且與上下文相關的結果。透過結合語義檢索和關鍵字匹配的優點,混合搜尋在複雜的資訊檢索任務中尤其有效。

本筆記展示如何在LlamaIndexRAG 管道中使用 Milvus 進行混合搜尋。我們將從推薦的預設混合搜尋 (語意 + BM25) 開始,然後探討其他可替代的稀疏嵌入方法和自訂混合 reranker。

先決條件

安裝相依性

在開始之前,請確定您已安裝下列依賴項目:

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

如果您使用的是 Google Colab,您可能需要重新啟動運行時(導航到介面上方的「運行」功能表,並從下拉式功能表中選擇「重新啟動會話」)。

設定帳號

本教程使用 OpenAI 進行文字嵌入和答案產生。您需要準備OpenAI API 密鑰

import openai

openai.api_key = "sk-"

若要使用 Milvus 向量存儲,請指定您的 Milvus 伺服器URI (可選擇使用TOKEN)。若要啟動 Milvus 伺服器,您可以依照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 載入 Paul Graham 的文章「What I Worked On」:

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'stext-embedding-ada-002)
  • 如果 enable_sparse 為 True,則使用 BM25 進行全文搜尋
  • 如果啟用混合搜尋,則使用 k=60 的 RRFRanker 來合併結果
# 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 指定的索引類型相容。預設為 None。
  • similarity_metric (str, optional):用於密集嵌入的相似度指標,目前支援 IP、COSINE 和 L2。

稀疏欄位

  • enable_sparse (bool):用來啟用或停用 sparse embedding 的布林標記。預設為 False。
  • sparse_embedding_field (str):sparse embedding 欄位的名稱,預設為 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 的預設設定,採用標準的內建分析器,根據標點符號來標記文字。

若要使用不同的分析器或自訂現有的分析器,您可以在建立BM25BuiltInFunction 時提供analyzer_params 參數值。然後,在MilvusVectorStore 中將此函式設定為sparse_embedding_function

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 模型來建立向量儲存和索引,以進行 densen embedding,並使用內建的 BGE-M3 來進行 sparse embedding:

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.

自訂稀疏嵌入功能

您也可以自訂 sparse embedding 函數,只要它繼承自BaseSparseEmbeddingFunction ,包括下列方法:

  • encode_queries:此方法可將文字轉換成稀疏嵌入的查詢清單。
  • encode_documents:此方法將文字轉換為文件的稀疏內嵌清單。

每個方法的輸出都應該遵循稀疏內嵌的格式,也就是字典清單。每個詞典都應該有一個 key(整數),代表維度,以及相對應的值(浮點數),代表嵌入在該維度的大小(例如,{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

自訂混合 Reranker

Milvus 支援兩種reranking 策略:Reciprocal Rank Fusion (RRF) 和 Weighted Scoring。MilvusVectorStore 混合搜尋的預設排名器是 k=60 的 RRF。要自訂混合排名器,請修改下列參數:

  • hybrid_ranker (str):指定混合搜尋查詢使用的排名器類型。目前只支援 ["RRFRanker", "WeightedRanker"]。預設為 "RRFRanker"。
  • hybrid_ranker_params (dict, optional):混合排名器的設定參數。此字典的結構取決於所使用的特定排名器:
    • 對於 "RRFRanker",它應該包括
      • "k" (int):Reciprocal Rank Fusion (RRF) 中使用的參數。此值用於計算排名分數,作為 RRF 演算法的一部分,RRF 演算法會將多種排名策略合併為單一分數,以提高搜尋相關性。如果未指定,預設值為 60。
    • 對於 "WeightedRanker",它期望:
      • 「權重」(浮點數清單):一個正好包含兩個權重的清單:
        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.

免費嘗試托管的 Milvus

Zilliz Cloud 無縫接入,由 Milvus 提供動力,速度提升 10 倍。

開始使用
反饋

這個頁面有幫助嗎?