RAG nutzt hybride Suche mit Milvus und LlamaIndex
Die hybride Suche nutzt die Stärken sowohl der semantischen Suche als auch des Abgleichs von Schlüsselwörtern, um genauere und kontextbezogene Ergebnisse zu liefern. Durch die Kombination der Vorteile von semantischer Suche und Keyword-Matching ist die hybride Suche besonders effektiv bei komplexen Information Retrieval Aufgaben.
Dieses Notebook zeigt, wie Milvus für die hybride Suche in LlamaIndex RAG-Pipelines verwendet werden kann. Wir beginnen mit der empfohlenen Standard-Hybridsuche (semantisch + BM25) und erkunden dann andere alternative Sparse-Embedding-Methoden und die Anpassung des Hybrid-Rerankers.
Voraussetzungen
Abhängigkeiten installieren
Bevor Sie beginnen, stellen Sie sicher, dass Sie die folgenden Abhängigkeiten installiert haben:
$ pip install llama-index-vector-stores-milvus
$ pip install llama-index-embeddings-openai
$ pip install llama-index-llms-openai
Wenn Sie Google Colab verwenden, müssen Sie möglicherweise die Runtime neu starten (navigieren Sie zum Menü "Runtime" am oberen Rand der Benutzeroberfläche und wählen Sie "Restart session" aus dem Dropdown-Menü).
Konten einrichten
Dieses Tutorial verwendet OpenAI für Texteinbettungen und die Generierung von Antworten. Sie müssen den OpenAI-API-Schlüssel vorbereiten.
import openai
openai.api_key = "sk-"
Um den Milvus-Vektorspeicher zu verwenden, geben Sie Ihren Milvus-Server URI (und optional mit der TOKEN) an. Um einen Milvus-Server zu starten, können Sie einen Milvus-Server einrichten, indem Sie der Milvus-Installationsanleitung folgen oder einfach Zilliz Cloud kostenlos ausprobieren.
Die Volltextsuche wird derzeit in Milvus Standalone, Milvus Distributed und Zilliz Cloud unterstützt, aber noch nicht in Milvus Lite (für eine zukünftige Implementierung geplant). Wenden Sie sich an support@zilliz.com für weitere Informationen.
URI = "http://localhost:19530"
# TOKEN = ""
Beispieldaten laden
Führen Sie die folgenden Befehle aus, um Beispieldokumente in das Verzeichnis "data/paul_graham" zu laden:
$ 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'
Verwenden Sie dann SimpleDirectoryReaderLoad, um den Aufsatz "What I Worked On" von Paul Graham zu laden:
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 ...
Hybride Suche mit BM25
In diesem Abschnitt wird gezeigt, wie man eine hybride Suche mit BM25 durchführt. Zu Beginn wird MilvusVectorStore initialisiert und ein Index für die Beispieldokumente erstellt. Die Standardkonfiguration verwendet:
- Dichte Einbettungen aus dem Standard-Einbettungsmodell (OpenAI's
text-embedding-ada-002) - BM25 für die Volltextsuche, wenn enable_sparse True ist
- RRFRanker mit k=60 zum Kombinieren der Ergebnisse, wenn die hybride Suche aktiviert ist
# 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').
Hier finden Sie weitere Informationen zu den Argumenten für die Konfiguration von Dense- und Sparse-Feldern in der MilvusVectorStore:
dichtes Feld
enable_dense (bool): Ein boolesches Flag zur Aktivierung oder Deaktivierung der dichten Einbettung. Die Voreinstellung ist True.dim (int, optional): Die Dimension der Einbettungsvektoren für die Sammlung.embedding_field (str, optional): Der Name des Feldes für die dichte Einbettung der Sammlung, Standardwert ist DEFAULT_EMBEDDING_KEY.index_config (dict, optional): Die für die Erstellung des Dense-Embedding-Index verwendete Konfiguration. Der Standardwert ist None.search_config (dict, optional): Die Konfiguration, die für die Suche im dichten Milvus-Index verwendet wird. Beachten Sie, dass dies mit dem durchindex_configangegebenen Indextyp kompatibel sein muss. Der Standardwert ist None.similarity_metric (str, optional): Die Ähnlichkeitsmetrik, die für die dichte Einbettung verwendet werden soll. Derzeit werden IP, COSINE und L2 unterstützt.
sparse field
enable_sparse (bool): Ein boolesches Flag zum Aktivieren oder Deaktivieren der Sparse-Einbettung. Der Standardwert ist False.sparse_embedding_field (str): Der Name des Sparse-Embedding-Feldes, Standardwert ist DEFAULT_SPARSE_EMBEDDING_KEY.sparse_embedding_function (Union[BaseSparseEmbeddingFunction, BaseMilvusBuiltInFunction], optional): Wenn enable_sparse True ist, sollte dieses Objekt bereitgestellt werden, um Text in eine Sparse-Einbettung zu konvertieren. Wenn None, wird die Standard-Sparse-Embedding-Funktion (BM25BuiltInFunction) verwendet, oder es wird BGEM3SparseEmbedding verwendet, wenn eine Sammlung ohne integrierte Funktionen vorhanden ist.sparse_index_config (dict, optional): Die Konfiguration, die zur Erstellung des Sparse Embedding Index verwendet wird. Der Standardwert ist None.
Um die hybride Suche während der Abfragephase zu aktivieren, setzen Sie vector_store_query_mode auf "hybrid". Dadurch werden Suchergebnisse aus der semantischen Suche und der Volltextsuche kombiniert und neu eingestuft. Testen wir es mit einer Beispielabfrage: "Was hat der Autor bei Viaweb gelernt?":
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.
Text-Analysator anpassen
Analyzer spielen eine wichtige Rolle bei der Volltextsuche, indem sie Sätze in Token zerlegen und lexikalische Verarbeitungen durchführen, z. B. Stemming und Stoppwortentfernung. Sie sind in der Regel sprachspezifisch. Weitere Einzelheiten finden Sie im Milvus Analyzer Guide.
Milvus unterstützt zwei Arten von Analyzern: Eingebaute Analyzer und benutzerdefinierte Analyzer. Wenn enable_sparse auf True gesetzt ist, verwendet MilvusVectorStore standardmäßig BM25BuiltInFunction mit Standardkonfigurationen, wobei der standardmäßig eingebaute Analyzer verwendet wird, der Text auf der Grundlage von Interpunktion tokenisiert.
Um einen anderen Analyzer zu verwenden oder den vorhandenen anzupassen, können Sie beim Erstellen von BM25BuiltInFunction Werte für das Argument analyzer_params angeben. Setzen Sie diese Funktion dann als sparse_embedding_function in 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)
Hybride Suche mit anderer spärlicher Einbettung
Neben der Kombination der semantischen Suche mit BM25 unterstützt Milvus auch die hybride Suche mit einer Sparse Embedding-Funktion wie BGE-M3. Das folgende Beispiel verwendet die eingebaute BGEM3SparseEmbeddingFunction, um Sparse Embeddings zu erzeugen.
Zunächst müssen wir das Paket FlagEmbedding installieren:
$ pip install -q FlagEmbedding
Dann erstellen wir den Vektorspeicher und den Index unter Verwendung des OpenAI-Standardmodells für die Densen-Einbettung und des eingebauten BGE-M3 für die Sparse-Einbettung:
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]
Führen wir nun eine hybride Suchanfrage mit einer Beispielfrage durch:
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.
Anpassen der Sparse Embedding Funktion
Sie können auch die Sparse Embedding-Funktion anpassen, solange sie von BaseSparseEmbeddingFunction erbt, einschließlich der folgenden Methoden:
encode_queries: Diese Methode wandelt Texte in eine Liste von Sparse Embeddings für Abfragen um.encode_documents: Diese Methode wandelt Text in eine Liste von Sparse Embeddings für Dokumente um.
Die Ausgabe jeder Methode sollte dem Format der Sparse Embeddings entsprechen, das eine Liste von Wörterbüchern ist. Jedes Wörterbuch sollte einen Schlüssel (eine ganze Zahl) haben, der die Dimension angibt, und einen entsprechenden Wert (eine Fließkommazahl), der die Größe der Einbettung in dieser Dimension angibt (z. B. {1: 0.5, 2: 0.3}).
Hier ist zum Beispiel eine benutzerdefinierte Sparse Embedding-Funktion, die BGE-M3 verwendet:
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
Anpassen des Hybrid-Rerankers
Milvus unterstützt zwei Arten von Reranking-Strategien: Reciprocal Rank Fusion (RRF) und Weighted Scoring. Der Standard-Ranker in der MilvusVectorStore Hybridsuche ist RRF mit k=60. Um den hybriden Ranker anzupassen, ändern Sie die folgenden Parameter:
hybrid_ranker (str): Gibt den Typ des Rankers an, der in hybriden Suchanfragen verwendet wird. Derzeit wird nur ["RRFRanker", "WeightedRanker"] unterstützt. Die Voreinstellung ist "RRFRanker".hybrid_ranker_params (dict, optional): Konfigurationsparameter für den hybriden Ranker. Die Struktur dieses Wörterbuchs hängt von dem spezifischen Ranker ab, der verwendet wird:- Für "RRFRanker" sollte es enthalten:
- "k" (int): Ein bei der Reciprocal Rank Fusion (RRF) verwendeter Parameter. Dieser Wert wird zur Berechnung der Rank Scores als Teil des RRF-Algorithmus verwendet, der mehrere Ranking-Strategien zu einem einzigen Score kombiniert, um die Suchrelevanz zu verbessern. Der Standardwert ist 60, wenn er nicht angegeben wird.
- Für "WeightedRanker" wird erwartet:
- "weights" (Liste von Floats): Eine Liste mit genau zwei Gewichten:
- Das Gewicht für die dichte Einbettungskomponente.
- Die Gewichtung für die spärliche Einbettungskomponente. Diese Gewichte werden verwendet, um die Bedeutung der dichten und spärlichen Komponenten der Einbettungen im hybriden Suchprozess auszugleichen. Die Standardgewichte sind [1.0, 1.0], wenn sie nicht angegeben werden.
- "weights" (Liste von Floats): Eine Liste mit genau zwei Gewichten:
- Für "RRFRanker" sollte es enthalten:
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.