Pencarian teks lengkap dengan Milvus dan Haystack
Pencarian teks lengkap adalah metode tradisional untuk mengambil dokumen dengan mencocokkan kata kunci atau frasa tertentu dalam teks. Metode ini memberi peringkat hasil berdasarkan skor relevansi yang dihitung dari faktor-faktor seperti frekuensi istilah. Meskipun pencarian semantik lebih baik dalam memahami makna dan konteks, pencarian teks lengkap unggul dalam pencocokan kata kunci yang tepat, menjadikannya pelengkap yang berguna untuk pencarian semantik. Algoritma BM25 banyak digunakan untuk menentukan peringkat dalam pencarian teks lengkap dan memainkan peran penting dalam Retrieval-Augmented Generation (RAG).
Milvus 2.5 memperkenalkan kemampuan pencarian teks lengkap menggunakan BM25. Pendekatan ini mengubah teks menjadi vektor jarang yang mewakili skor BM25. Anda cukup memasukkan teks mentah dan Milvus akan secara otomatis menghasilkan dan menyimpan vektor jarang, tanpa perlu melakukan pembuatan embedding jarang secara manual.
Haystack sekarang mendukung fitur Milvus ini, sehingga mudah untuk menambahkan pencarian teks lengkap ke aplikasi RAG. Anda dapat menggabungkan pencarian teks lengkap dengan pencarian semantik vektor padat untuk pendekatan hibrida yang mendapat manfaat dari pemahaman semantik dan ketepatan pencocokan kata kunci. Kombinasi ini meningkatkan akurasi pencarian dan memberikan hasil yang lebih baik kepada pengguna.
Tutorial ini mendemonstrasikan cara mengimplementasikan pencarian teks lengkap dan hibrida di aplikasi Anda menggunakan Haystack dan Milvus.
Untuk menggunakan penyimpanan vektor Milvus, tentukan server Milvus Anda URI (dan secara opsional dengan TOKEN). Untuk memulai server Milvus, Anda dapat mengatur server Milvus dengan mengikuti panduan instalasi Milvus atau mencoba Zilliz Cloud(Milvus yang dikelola secara penuh) secara gratis.
- Pencarian teks lengkap saat ini tersedia di Milvus Standalone, Milvus Distributed, dan Zilliz Cloud, meskipun belum didukung di Milvus Lite (yang memiliki fitur ini yang direncanakan untuk implementasi di masa mendatang). Hubungi support@zilliz.com untuk informasi lebih lanjut.
- Sebelum melanjutkan dengan tutorial ini, pastikan Anda memiliki pemahaman dasar tentang pencarian teks lengkap dan penggunaan dasar integrasi Haystack Milvus.
Prasyarat
Sebelum menjalankan buku panduan ini, pastikan Anda telah menginstal dependensi berikut ini:
$ pip install --upgrade --quiet pymilvus milvus-haystack
Jika Anda menggunakan Google Colab, untuk mengaktifkan dependensi yang baru saja terinstal, Anda mungkin perlu memulai ulang runtime (klik menu "Runtime" di bagian atas layar, dan pilih "Restart session" dari menu tarik-turun).
Kita akan menggunakan model dari OpenAI. Anda harus menyiapkan kunci api OPENAI_API_KEY sebagai variabel lingkungan.
import os
os.environ["OPENAI_API_KEY"] = "sk-***********"
Menyiapkan data
Impor paket-paket yang diperlukan dalam buku catatan ini. Kemudian siapkan beberapa contoh dokumen.
from haystack import Pipeline
from haystack.components.embedders import OpenAIDocumentEmbedder, OpenAITextEmbedder
from haystack.components.writers import DocumentWriter
from haystack.utils import Secret
from milvus_haystack import MilvusDocumentStore, MilvusSparseEmbeddingRetriever
from haystack.document_stores.types import DuplicatePolicy
from milvus_haystack.function import BM25BuiltInFunction
from milvus_haystack import MilvusDocumentStore
from milvus_haystack.milvus_embedding_retriever import MilvusHybridRetriever
from haystack.utils import Secret
from haystack.components.builders import PromptBuilder
from haystack.components.generators import OpenAIGenerator
from haystack import Document
documents = [
Document(content="Alice likes this apple", meta={"category": "fruit"}),
Document(content="Bob likes swimming", meta={"category": "sport"}),
Document(content="Charlie likes white dogs", meta={"category": "pets"}),
]
Mengintegrasikan pencarian teks lengkap ke dalam sistem RAG akan menyeimbangkan pencarian semantik dengan pencarian berbasis kata kunci yang tepat dan dapat diprediksi. Anda juga dapat memilih untuk hanya menggunakan pencarian teks lengkap meskipun disarankan untuk menggabungkan pencarian teks lengkap dengan pencarian semantik untuk hasil pencarian yang lebih baik. Di sini, untuk tujuan demonstrasi, kami akan menunjukkan pencarian teks lengkap saja dan pencarian hibrida.
Pencarian BM25 tanpa penyematan
Membuat Pipeline pengindeksan
Untuk pencarian teks lengkap, Milvus MilvusDocumentStore menerima parameter builtin_function. Melalui parameter ini, Anda dapat mengoper sebuah instance dari BM25BuiltInFunction, yang mengimplementasikan algoritme BM25 di sisi server Milvus. Tetapkan builtin_function yang ditentukan sebagai instance fungsi BM25. Sebagai contoh:
connection_args = {"uri": "http://localhost:19530"}
# connection_args = {"uri": YOUR_ZILLIZ_CLOUD_URI, "token": Secret.from_env_var("ZILLIZ_CLOUD_API_KEY")}
document_store = MilvusDocumentStore(
connection_args=connection_args,
sparse_vector_field="sparse_vector", # The sparse vector field.
text_field="text",
builtin_function=[
BM25BuiltInFunction( # The BM25 function converts the text into a sparse vector.
input_field_names="text",
output_field_names="sparse_vector",
)
],
consistency_level="Bounded", # Supported values are (`"Strong"`, `"Session"`, `"Bounded"`, `"Eventually"`).
drop_old=True, # Drop the old collection if it exists and recreate it.
)
Untuk connection_args:
- Anda dapat menyiapkan server Milvus yang lebih berkinerja pada docker atau kubernetes. Dalam pengaturan ini, gunakan alamat server, misalnya
http://localhost:19530, sebagaiuri. - Jika Anda ingin menggunakan Zilliz Cloud, layanan cloud yang dikelola sepenuhnya untuk Milvus, sesuaikan
uridantoken, yang sesuai dengan kunci Public Endpoint dan Api di Zilliz Cloud.
Buatlah pipeline pengindeksan untuk menulis dokumen ke dalam penyimpanan dokumen Milvus.
writer = DocumentWriter(document_store=document_store, policy=DuplicatePolicy.NONE)
indexing_pipeline = Pipeline()
indexing_pipeline.add_component("writer", writer)
indexing_pipeline.run({"writer": {"documents": documents}})
{'writer': {'documents_written': 3}}
Membuat pipeline pengambilan
Buat pipeline pengambilan yang mengambil dokumen dari penyimpanan dokumen Milvus menggunakan MilvusSparseEmbeddingRetriever, yang merupakan pembungkus dari document_store.
retrieval_pipeline = Pipeline()
retrieval_pipeline.add_component(
"retriever", MilvusSparseEmbeddingRetriever(document_store=document_store)
)
question = "Who likes swimming?"
retrieval_results = retrieval_pipeline.run({"retriever": {"query_text": question}})
retrieval_results["retriever"]["documents"][0]
Document(id=bd334348dd2087c785e99b5a0009f33d9b8b8198736f6415df5d92602d81fd3e, content: 'Bob likes swimming', meta: {'category': 'sport'}, score: 1.2039074897766113)
Pencarian Hibrida dengan pencarian semantik dan pencarian teks lengkap
Membuat pipeline pengindeksan
Dalam pencarian hibrida, kami menggunakan fungsi BM25 untuk melakukan pencarian teks lengkap, dan menentukan bidang vektor padat vector, untuk melakukan pencarian semantik.
document_store = MilvusDocumentStore(
connection_args=connection_args,
vector_field="vector", # The dense vector field.
sparse_vector_field="sparse_vector", # The sparse vector field.
text_field="text",
builtin_function=[
BM25BuiltInFunction( # The BM25 function converts the text into a sparse vector.
input_field_names="text",
output_field_names="sparse_vector",
)
],
consistency_level="Bounded", # Supported values are (`"Strong"`, `"Session"`, `"Bounded"`, `"Eventually"`).
drop_old=True, # Drop the old collection and recreate it.
)
Buat pipeline pengindeksan yang mengubah dokumen menjadi sematan. Dokumen-dokumen tersebut kemudian ditulis ke penyimpanan dokumen Milvus.
writer = DocumentWriter(document_store=document_store, policy=DuplicatePolicy.NONE)
indexing_pipeline = Pipeline()
indexing_pipeline.add_component("dense_doc_embedder", OpenAIDocumentEmbedder())
indexing_pipeline.add_component("writer", writer)
indexing_pipeline.connect("dense_doc_embedder", "writer")
indexing_pipeline.run({"dense_doc_embedder": {"documents": documents}})
print("Number of documents:", document_store.count_documents())
Calculating embeddings: 100%|██████████| 1/1 [00:01<00:00, 1.15s/it]
Number of documents: 3
Membuat pipeline pengambilan
Buat pipeline pengambilan yang mengambil dokumen dari penyimpanan dokumen Milvus menggunakan MilvusHybridRetriever, yang berisi document_store dan menerima parameter tentang pencarian hibrida.
# from pymilvus import WeightedRanker
retrieval_pipeline = Pipeline()
retrieval_pipeline.add_component("dense_text_embedder", OpenAITextEmbedder())
retrieval_pipeline.add_component(
"retriever",
MilvusHybridRetriever(
document_store=document_store,
# top_k=3,
# reranker=WeightedRanker(0.5, 0.5), # Default is RRFRanker()
),
)
retrieval_pipeline.connect("dense_text_embedder.embedding", "retriever.query_embedding")
<haystack.core.pipeline.pipeline.Pipeline object at 0x3383ad990>
🚅 Components
- dense_text_embedder: OpenAITextEmbedder
- retriever: MilvusHybridRetriever
🛤️ Connections
- dense_text_embedder.embedding -> retriever.query_embedding (List[float])
Ketika melakukan pencarian hibrida menggunakan MilvusHybridRetriever, kita bisa mengatur parameter topK dan reranker secara opsional. Secara otomatis akan menangani penyematan vektor dan fungsi bawaan dan akhirnya menggunakan reranker untuk menyempurnakan hasilnya. Detail implementasi yang mendasari proses pencarian disembunyikan dari pengguna.
Untuk informasi lebih lanjut tentang pencarian hybrid, Anda dapat merujuk ke pengenalan Pencarian Hybrid.
question = "Who likes swimming?"
retrieval_results = retrieval_pipeline.run(
{
"dense_text_embedder": {"text": question},
"retriever": {"query_text": question},
}
)
retrieval_results["retriever"]["documents"][0]
Document(id=bd334348dd2087c785e99b5a0009f33d9b8b8198736f6415df5d92602d81fd3e, content: 'Bob likes swimming', meta: {'category': 'sport'}, score: 0.032786883413791656, embedding: vector of size 1536)
Menyesuaikan penganalisis
Penganalisis sangat penting dalam pencarian teks lengkap dengan memecah kalimat menjadi token dan melakukan analisis leksikal seperti stemming dan penghilangan kata henti. Penganalisis biasanya bersifat spesifik untuk bahasa tertentu. Anda dapat merujuk ke panduan ini untuk mempelajari lebih lanjut tentang penganalisis di Milvus.
Milvus mendukung dua jenis penganalisis: Penganalisis bawaan dan Penganalisis Khusus. Secara default, BM25BuiltInFunction akan menggunakan penganalisis bawaan standar, yang merupakan penganalisis paling dasar yang menandai teks dengan tanda baca.
Jika Anda ingin menggunakan penganalisis yang berbeda atau menyesuaikan penganalisis, Anda dapat memasukkan parameter analyzer_params dalam inisialisasi BM25BuiltInFunction.
analyzer_params_custom = {
"tokenizer": "standard",
"filter": [
"lowercase", # Built-in filter
{"type": "length", "max": 40}, # Custom filter
{"type": "stop", "stop_words": ["of", "to"]}, # Custom filter
],
}
document_store = MilvusDocumentStore(
connection_args=connection_args,
vector_field="vector",
sparse_vector_field="sparse_vector",
text_field="text",
builtin_function=[
BM25BuiltInFunction(
input_field_names="text",
output_field_names="sparse_vector",
analyzer_params=analyzer_params_custom, # Custom analyzer parameters.
enable_match=True, # Whether to enable match.
)
],
consistency_level="Bounded",
drop_old=True,
)
# write documents to the document store
writer = DocumentWriter(document_store=document_store, policy=DuplicatePolicy.NONE)
indexing_pipeline = Pipeline()
indexing_pipeline.add_component("dense_doc_embedder", OpenAIDocumentEmbedder())
indexing_pipeline.add_component("writer", writer)
indexing_pipeline.connect("dense_doc_embedder", "writer")
indexing_pipeline.run({"dense_doc_embedder": {"documents": documents}})
Calculating embeddings: 100%|██████████| 1/1 [00:00<00:00, 1.39it/s]
{'dense_doc_embedder': {'meta': {'model': 'text-embedding-ada-002-v2',
'usage': {'prompt_tokens': 11, 'total_tokens': 11}}},
'writer': {'documents_written': 3}}
Kita dapat melihat skema koleksi Milvus dan memastikan penganalisis yang disesuaikan diatur dengan benar.
document_store.col.schema
{'auto_id': False, 'description': '', 'fields': [{'name': 'text', 'description': '', 'type': <DataType.VARCHAR: 21>, 'params': {'max_length': 65535, 'enable_match': True, 'enable_analyzer': True, 'analyzer_params': {'tokenizer': 'standard', 'filter': ['lowercase', {'type': 'length', 'max': 40}, {'type': 'stop', 'stop_words': ['of', 'to']}]}}}, {'name': 'id', 'description': '', 'type': <DataType.VARCHAR: 21>, 'params': {'max_length': 65535}, 'is_primary': True, 'auto_id': False}, {'name': 'vector', 'description': '', 'type': <DataType.FLOAT_VECTOR: 101>, 'params': {'dim': 1536}}, {'name': 'sparse_vector', 'description': '', 'type': <DataType.SPARSE_FLOAT_VECTOR: 104>, 'is_function_output': True}], 'enable_dynamic_field': True, 'functions': [{'name': 'bm25_function_7b6e15a4', 'description': '', 'type': <FunctionType.BM25: 1>, 'input_field_names': ['text'], 'output_field_names': ['sparse_vector'], 'params': {}}]}
Untuk detail konsep lebih lanjut, misalnya, analyzer, tokenizer, filter, enable_match, analyzer_params, silakan merujuk ke dokumentasi penganalisis.
Menggunakan Pencarian Hibrida dalam pipeline RAG
Kita telah mempelajari cara menggunakan fungsi bawaan BM25 dasar di Haystack dan Milvus dan menyiapkan document_store yang telah dimuat. Mari kita perkenalkan implementasi RAG yang dioptimalkan dengan pencarian hybrid.
Diagram ini menunjukkan proses Hybrid Retrieve & Reranking, menggabungkan BM25 untuk pencocokan kata kunci dan pencarian vektor padat untuk pencarian semantik. Hasil dari kedua metode tersebut digabungkan, diberi peringkat ulang, dan diteruskan ke LLM untuk menghasilkan jawaban akhir.
Pencarian hibrida menyeimbangkan ketepatan dan pemahaman semantik, meningkatkan akurasi dan ketahanan untuk beragam pertanyaan. Metode ini mengambil kandidat dengan pencarian teks lengkap BM25 dan pencarian vektor, memastikan pencarian yang semantik, memahami konteks, dan akurat.
Mari kita coba implementasi RAG yang dioptimalkan dengan pencarian hybrid.
prompt_template = """Answer the following query based on the provided context. If the context does
not include an answer, reply with 'I don't know'.\n
Query: {{query}}
Documents:
{% for doc in documents %}
{{ doc.content }}
{% endfor %}
Answer:
"""
rag_pipeline = Pipeline()
rag_pipeline.add_component("text_embedder", OpenAITextEmbedder())
rag_pipeline.add_component(
"retriever", MilvusHybridRetriever(document_store=document_store, top_k=1)
)
rag_pipeline.add_component("prompt_builder", PromptBuilder(template=prompt_template))
rag_pipeline.add_component(
"generator",
OpenAIGenerator(
api_key=Secret.from_token(os.getenv("OPENAI_API_KEY")),
generation_kwargs={"temperature": 0},
),
)
rag_pipeline.connect("text_embedder.embedding", "retriever.query_embedding")
rag_pipeline.connect("retriever.documents", "prompt_builder.documents")
rag_pipeline.connect("prompt_builder", "generator")
results = rag_pipeline.run(
{
"text_embedder": {"text": question},
"retriever": {"query_text": question},
"prompt_builder": {"query": question},
}
)
print("RAG answer:", results["generator"]["replies"][0])
RAG answer: Bob likes swimming.
Untuk informasi lebih lanjut tentang cara menggunakan milvus-haystack, silakan merujuk ke repositori resmi milvus-haystack.