توليد تعبيرات تصفية استعلام ميلفوس باستخدام نماذج اللغة الكبيرة
سنوضح في هذا البرنامج التعليمي كيفية استخدام نماذج اللغة الكبيرة (LLMs) لتوليد تعبيرات تصفية ميلفوس تلقائيًا من استعلامات اللغة الطبيعية. هذا النهج يجعل الاستعلام عن قاعدة البيانات المتجهة أكثر سهولة من خلال السماح للمستخدمين بالتعبير عن شروط التصفية المعقدة بلغة إنجليزية بسيطة، والتي يتم تحويلها بعد ذلك إلى بناء الجملة المناسب لـ Milvus.
يدعم Milvus قدرات تصفية متطورة بما في ذلك:
- المشغلات الأساسية: مشغلات المقارنة مثل
==،!=، ،>،<،>=,<= - المشغلات المنطقية: المشغلات المنطقية مثل
and،or، ،notللشروط المعقدة - عمليات السلسلة: مطابقة الأنماط مع
likeووظائف السلسلة الأخرى - عمليات المصفوفات: العمل مع حقول المصفوفات باستخدام
array_contains،array_length، إلخ. - عمليات JSON: الاستعلام عن حقول JSON باستخدام الدوال المتخصصة
من خلال دمج LLMs مع وثائق ميلفوس، يمكننا إنشاء نظام ذكي يفهم استعلامات اللغة الطبيعية ويولد تعبيرات تصفية صحيحة نحويًا. سيتناول هذا البرنامج التعليمي عملية إعداد هذا النظام، مع تسليط الضوء على فعاليته في سيناريوهات التصفية المختلفة.
التبعيات والبيئة
$ pip install --upgrade pymilvus openai requests docling beautifulsoup4
print("Environment setup complete!")
إعداد متغيرات البيئة
قم بتكوين بيانات اعتماد OpenAI API الخاصة بك لتمكين إنشاء التضمين وإنشاء تعبيرات التصفية المستندة إلى LLM. استبدل 'your_openai_api_key' بمفتاح OpenAI API الفعلي الخاص بك.
import os
import openai
os.environ["OPENAI_API_KEY"] = "your_openai_api_key"
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise ValueError("Please set the OPENAI_API_KEY environment variable!")
openai.api_key = api_key
print("API key loaded.")
إنشاء مجموعة عينة
الآن دعنا ننشئ نموذجًا لمجموعة Milvus مع بيانات المستخدم. ستحتوي هذه المجموعة على كل من الحقول القياسية (للتصفية) والتضمينات المتجهة (للبحث الدلالي). سنستخدم نموذج تضمين النص في OpenAI لإنشاء تمثيلات متجهة لمعلومات المستخدم.
from pymilvus import MilvusClient, FieldSchema, CollectionSchema, DataType
import os
from openai import OpenAI
import uuid
client = MilvusClient(uri="http://localhost:19530")
openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
embedding_model = "text-embedding-3-small"
embedding_dim = 1536
fields = [
FieldSchema(
name="pk",
dtype=DataType.VARCHAR,
is_primary=True,
auto_id=False,
max_length=100,
),
FieldSchema(name="name", dtype=DataType.VARCHAR, max_length=128),
FieldSchema(name="age", dtype=DataType.INT64),
FieldSchema(name="city", dtype=DataType.VARCHAR, max_length=128),
FieldSchema(name="hobby", dtype=DataType.VARCHAR, max_length=128),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=embedding_dim),
]
schema = CollectionSchema(fields=fields, description="User data embedding example")
collection_name = "user_data_collection"
if client.has_collection(collection_name):
client.drop_collection(collection_name)
# Strong consistency waits for all loads to complete, adding latency with large datasets
# client.create_collection(
# collection_name=collection_name, schema=schema, consistency_level="Strong"
# )
client.create_collection(collection_name=collection_name, schema=schema)
index_params = client.prepare_index_params()
index_params.add_index(
field_name="embedding",
index_type="IVF_FLAT",
metric_type="COSINE",
params={"nlist": 128},
)
client.create_index(collection_name=collection_name, index_params=index_params)
data_to_insert = [
{"name": "John", "age": 23, "city": "Shanghai", "hobby": "Drinking coffee"},
{"name": "Alice", "age": 29, "city": "New York", "hobby": "Reading books"},
{"name": "Bob", "age": 31, "city": "London", "hobby": "Playing chess"},
{"name": "Eve", "age": 27, "city": "Paris", "hobby": "Painting"},
{"name": "Charlie", "age": 35, "city": "Tokyo", "hobby": "Cycling"},
{"name": "Grace", "age": 22, "city": "Berlin", "hobby": "Photography"},
{"name": "David", "age": 40, "city": "Toronto", "hobby": "Watching movies"},
{"name": "Helen", "age": 30, "city": "Sydney", "hobby": "Cooking"},
{"name": "Frank", "age": 28, "city": "Beijing", "hobby": "Hiking"},
{"name": "Ivy", "age": 26, "city": "Seoul", "hobby": "Dancing"},
{"name": "Tom", "age": 33, "city": "Madrid", "hobby": "Writing"},
]
def get_embeddings(texts):
return [
rec.embedding
for rec in openai_client.embeddings.create(
input=texts, model=embedding_model, dimensions=embedding_dim
).data
]
texts = [
f"{item['name']} from {item['city']} is {item['age']} years old and likes {item['hobby']}."
for item in data_to_insert
]
embeddings = get_embeddings(texts)
insert_data = []
for item, embedding in zip(data_to_insert, embeddings):
item_with_embedding = {
"pk": str(uuid.uuid4()),
"name": item["name"],
"age": item["age"],
"city": item["city"],
"hobby": item["hobby"],
"embedding": embedding,
}
insert_data.append(item_with_embedding)
client.insert(collection_name=collection_name, data=insert_data)
print(f"Collection '{collection_name}' has been created and data has been inserted.")
طباعة 3 عينة من البيانات
تنشئ الشيفرة أعلاه مجموعة ميلفوس بالبنية التالية:
- pk: حقل المفتاح الأساسي (VARCHAR)
- الاسم: اسم المستخدم (VARCHAR)
- العمر: عمر المستخدم (INT64)
- المدينة: مدينة المستخدم (VARCHAR)
- الهواية: هواية المستخدم (VARCHAR)
- التضمين: التضمين المتجه (FLOAT_VECTOR، 1536 بُعدًا)
قمنا بإدراج 11 عينة من المستخدمين مع معلوماتهم الشخصية وإنشاء تضمينات لإمكانيات البحث الدلالي. يتم تحويل معلومات كل مستخدم إلى نص وصفي يلتقط اسمه وموقعه وعمره واهتماماته قبل تضمينها. دعونا نتحقق من أن مجموعتنا قد تم إنشاؤها بنجاح وتحتوي على البيانات المتوقعة من خلال الاستعلام عن بعض السجلات النموذجية.
from pymilvus import MilvusClient
import os
from openai import OpenAI
client = MilvusClient(uri="http://localhost:19530")
collection_name = "user_data_collection"
client.load_collection(collection_name=collection_name)
result = client.query(
collection_name=collection_name,
filter="",
output_fields=["name", "age", "city", "hobby"],
limit=3,
)
for record in result:
print(record)
جمع وثائق تعبيرات مرشح ميلفوس التوثيقية
لمساعدة النموذج اللغوي الكبير على فهم بناء جملة تعبيرات مرشح ميلفوس بشكل أفضل، نحتاج إلى تزويده بالوثائق الرسمية ذات الصلة. سنستخدم مكتبة docling لكشط عدة صفحات رئيسية من موقع ميلفوس الرسمي.
تحتوي هذه الصفحات على معلومات مفصلة حول:
- المشغلات المنطقية
and،،or،notللشروط المنطقية المعقدة - المشغلات الأساسية: مشغلات المقارنة مثل:
==،!=، ،>،<،>=,<= - قوالب التصفية: أنماط التصفية المتقدمة وبناء الجملة
- مطابقة السلسلة: مطابقة الأنماط مع
likeوعمليات السلسلة الأخرى
ستعمل هذه الوثائق كقاعدة معرفية لـ LLM لتوليد تعبيرات تصفية دقيقة.
import docling
from docling.document_converter import DocumentConverter
converter = DocumentConverter()
docs = [
converter.convert(url)
for url in [
"https://milvus.io/docs/boolean.md",
"https://milvus.io/docs/basic-operators.md",
"https://milvus.io/docs/filtering-templating.md",
]
]
for doc in docs[:3]:
print(doc.document.export_to_markdown())
يوفر كشط الوثائق تغطية شاملة لبنية مرشح ميلفوس. ستمكّن هذه القاعدة المعرفية إدارة التعلم الآلي الخاصة بنا من فهم الفروق الدقيقة في بناء تعبيرات التصفية، بما في ذلك الاستخدام الصحيح للمشغل، والإحالة إلى الحقول، ومجموعات الشروط المعقدة.
توليد الفلتر المدعوم من LLM
والآن بعد أن أصبح لدينا سياق التوثيق، دعنا نعدّ نظام LLM لتوليد تعبيرات التصفية. سننشئ موجهًا منظمًا يجمع بين الوثائق التي تم كشطها واستعلامات المستخدم لإنتاج تعبيرات تصفية Milvus صحيحة نحويًا.
يستخدم نظام توليد الفلاتر الخاص بنا موجهًا مصممًا بعناية والذي
- يوفر السياق: يتضمن وثائق Milvus الكاملة كمادة مرجعية.
- يضع القيود: يضمن أن يستخدم LLM فقط بناء الجملة والميزات الموثقة.
- يفرض الدقة: يتطلب تعبيرات صحيحة من الناحية النحوية
- يحافظ على التركيز: إرجاع تعبير المرشح فقط دون تفسيرات
دعنا نختبر ذلك باستخدام استعلام لغة طبيعية ونرى مدى جودة أداء LLM.
from openai import OpenAI
import json
from IPython.display import display, Markdown
context = "\n".join([doc.document.export_to_markdown() for doc in docs])
prompt = f"""
You are an expert Milvus vector database engineer. Your task is to convert a user's natural language query into a valid Milvus filter expression, using the provided Milvus documentation as your knowledge base.
Follow these rules strictly:
1. Only use the provided documents as your source of knowledge.
2. Ensure the generated filter expression is syntactically correct.
3. If there isn't enough information in the documents to create an expression, state that directly.
4. Only return the final filter expression. Do not include any explanations or extra text.
---
**Milvus Documentation Context:**
{context}
---
**User Query:**
{user_query}
---
**Filter Expression:**
"""
client = OpenAI()
def generate_filter_expr(user_query):
"""
Generates a Milvus filter expression from a user query using GPT-4o-mini.
"""
completion = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": prompt},
{"role": "user", "content": user_query},
],
temperature=0.0,
)
return completion.choices[0].message.content
user_query = "Find people older than 30 who live in London, Tokyo, or Toronto"
filter_expr = generate_filter_expr(user_query)
print(f"Generated filter expression: {filter_expr}")
نجحت LLM في إنشاء تعبير مرشح يجمع بين عدة شروط:
- مقارنة العمر باستخدام
> - مطابقة مدن متعددة باستخدام المشغل
in - الإحالة إلى الحقل المناسب وبناء الجملة
هذا يوضح قوة توفير سياق توثيق شامل لتوجيه إنشاء مرشح LLM.
اختبر الفلتر المُنشَأ
الآن دعونا نختبر تعبير المرشح الذي تم إنشاؤه باستخدامه في عملية بحث فعلية في ميلفوس. سنقوم بدمج البحث الدلالي مع التصفية الدقيقة للعثور على المستخدمين الذين يطابقون كلاً من هدف الاستعلام والمعايير المحددة.
from pymilvus import MilvusClient
from openai import OpenAI
import os
client = MilvusClient(uri="http://localhost:19530")
openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
clean_filter = (
filter_expr.replace("```", "").replace('filter="', "").replace('"', "").strip()
)
print(f"Using filter: {clean_filter}")
query_embedding = (
openai_client.embeddings.create(
input=[user_query], model="text-embedding-3-small", dimensions=1536
)
.data[0]
.embedding
)
search_results = client.search(
collection_name="user_data_collection",
data=[query_embedding],
limit=10,
filter=clean_filter,
output_fields=["pk", "name", "age", "city", "hobby"],
search_params={
"metric_type": "COSINE",
"params": {"nprobe": 10},
},
)
print("Search results:")
for i, hits in enumerate(search_results):
print(f"Query {i}:")
for hit in hits:
print(f" - {hit}")
print()
تحليل النتائج
تُظهر نتائج البحث التكامل الناجح بين المرشحات التي تم إنشاؤها من قبل LLM مع البحث المتجه في ميلفوس. حدد الفلتر بشكل صحيح المستخدمين الذين
- أكبر من 30 عامًا
- يعيشون في لندن أو طوكيو أو تورنتو
- مطابقة السياق الدلالي للاستعلام
يجمع هذا النهج بين دقة التصفية المهيكلة ومرونة مدخلات اللغة الطبيعية، مما يجعل قواعد بيانات المتجهات أكثر سهولة للمستخدمين الذين قد لا يكونون على دراية بصيغة استعلام محددة.