Generierung von Milvus-Abfragefilterausdrücken mit großen Sprachmodellen
In diesem Tutorial wird gezeigt, wie man Large Language Models (LLMs) verwendet, um automatisch Milvus-Filterausdrücke aus natürlichsprachlichen Abfragen zu generieren. Dieser Ansatz macht die Abfrage von Vektordatenbanken zugänglicher, da Benutzer komplexe Filterbedingungen in einfachem Englisch ausdrücken können, die dann in die richtige Milvus-Syntax umgewandelt werden.
Milvus unterstützt ausgefeilte Filterfunktionen, darunter:
- Grundlegende Operatoren: Vergleichsoperatoren wie
==,!=,>,<,>=,<= - Boolesche Operatoren: Logische Operatoren wie
and,or,notfür komplexe Bedingungen - String-Operationen: Mustervergleich mit
likeund anderen String-Funktionen - Array-Operationen: Arbeiten mit Array-Feldern mit
array_contains,array_length, etc. - JSON-Operationen: Abfrage von JSON-Feldern mit spezialisierten Operatoren
Durch die Integration von LLMs mit der Milvus-Dokumentation können wir ein intelligentes System schaffen, das natürlichsprachliche Abfragen versteht und syntaktisch korrekte Filterausdrücke erzeugt. Dieses Tutorial führt durch den Prozess der Einrichtung dieses Systems und zeigt seine Effektivität in verschiedenen Filterszenarien auf.
Abhängigkeiten und Umgebung
$ pip install --upgrade pymilvus openai requests docling beautifulsoup4
print("Environment setup complete!")
Einrichten von Umgebungsvariablen
Konfigurieren Sie Ihre OpenAI-API-Anmeldedaten, um die Erzeugung von Einbettungen und LLM-basierten Filterausdrücken zu ermöglichen. Ersetzen Sie 'your_openai_api_key' durch Ihren tatsächlichen OpenAI-API-Schlüssel.
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.")
Erstellen Sie eine Beispielsammlung
Lassen Sie uns nun eine Milvus-Beispielsammlung mit Benutzerdaten erstellen. Diese Sammlung wird sowohl skalare Felder (für die Filterung) als auch Vektoreinbettungen (für die semantische Suche) enthalten. Wir werden das Texteinbettungsmodell von OpenAI verwenden, um Vektordarstellungen von Benutzerinformationen zu erzeugen.
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.")
Drucken von 3 Beispieldaten
Der obige Code erzeugt eine Milvus-Sammlung mit der folgenden Struktur:
- pk: Primärschlüsselfeld (VARCHAR)
- name: Nutzername (VARCHAR)
- Alter: Alter des Benutzers (INT64)
- Stadt: Ort des Benutzers (VARCHAR)
- Hobby: Benutzer Hobby (VARCHAR)
- Einbettung: Vektoreinbettung (FLOAT_VECTOR, 1536 Dimensionen)
Wir haben 11 Beispielbenutzer mit ihren persönlichen Informationen eingefügt und generieren Einbettungen für semantische Suchfunktionen. Die Informationen jedes Benutzers werden in einen beschreibenden Text umgewandelt, der den Namen, den Ort, das Alter und die Interessen erfasst, bevor sie eingebettet werden. Überprüfen wir, ob unsere Sammlung erfolgreich erstellt wurde und die erwarteten Daten enthält, indem wir ein paar Beispieldatensätze abfragen.
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)
Sammeln der Milvus-Filterausdruck-Dokumentation
Damit das große Sprachmodell die Syntax der Milvus-Filterausdrücke besser verstehen kann, müssen wir es mit der entsprechenden offiziellen Dokumentation versorgen. Wir werden die Bibliothek docling verwenden, um mehrere Schlüsselseiten von der offiziellen Milvus-Website zu scrapen.
Diese Seiten enthalten detaillierte Informationen über:
- Boolesche Operatoren:
and,or,notfür komplexe logische Bedingungen - Grundlegende Operatoren: Vergleichsoperatoren wie
==,!=,>,<,>=,<= - Filterungsvorlagen: Erweiterte Filtermuster und Syntax
- String-Matching: Mustervergleich mit
likeund anderen String-Operationen
Diese Dokumentation dient als Wissensbasis für unseren LLM, um genaue Filterausdrücke zu erzeugen.
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())
Das Scraping der Dokumentation bietet eine umfassende Abdeckung der Milvus-Filtersyntax. Diese Wissensbasis wird unseren LLM in die Lage versetzen, die Feinheiten der Konstruktion von Filterausdrücken zu verstehen, einschließlich der richtigen Verwendung von Operatoren, Feldreferenzen und komplexen Bedingungskombinationen.
LLM-gestützte Filtergenerierung
Nachdem wir nun den Dokumentationskontext haben, wollen wir das LLM-System so einrichten, dass es Filterausdrücke generiert. Wir werden eine strukturierte Eingabeaufforderung erstellen, die die gescrapte Dokumentation mit Benutzerabfragen kombiniert, um syntaktisch korrekte Milvus-Filterausdrücke zu erzeugen.
Unser System zur Filtergenerierung verwendet eine sorgfältig ausgearbeitete Eingabeaufforderung, die:
- Kontext bereitstellt: Die komplette Milvus-Dokumentation als Referenzmaterial einbezieht
- Beschränkungen festlegt: Sicherstellt, dass der LLM nur dokumentierte Syntax und Funktionen verwendet
- Genauigkeit erzwingt: Erfordert syntaktisch korrekte Ausdrücke
- Behält den Fokus bei: Gibt nur den Filterausdruck ohne Erklärungen zurück
Lassen Sie uns dies mit einer Abfrage in natürlicher Sprache testen und sehen, wie gut der LLM funktioniert.
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}")
Der LLM hat erfolgreich einen Filterausdruck generiert, der mehrere Bedingungen kombiniert:
- Altersvergleich mit
> - Abgleich mehrerer Städte mit dem Operator
in - Korrekte Feldreferenzierung und Syntax
Dies demonstriert die Stärke der Bereitstellung eines umfassenden Dokumentationskontextes, um die LLM-Filtergenerierung zu leiten.
Testen Sie den generierten Filter
Lassen Sie uns nun unseren generierten Filterausdruck testen, indem wir ihn in einem tatsächlichen Milvus-Suchvorgang verwenden. Wir werden die semantische Suche mit einer präzisen Filterung kombinieren, um Benutzer zu finden, die sowohl der Abfrageabsicht als auch den spezifischen Kriterien entsprechen.
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()
Analyse der Ergebnisse
Die Suchergebnisse zeigen die erfolgreiche Integration von LLM-generierten Filtern mit der Milvus-Vektorsuche. Der Filter identifizierte korrekt Benutzer, die:
- älter als 30 Jahre sind
- in London, Tokio oder Toronto leben
- mit dem semantischen Kontext der Suchanfrage übereinstimmen
Dieser Ansatz kombiniert die Präzision strukturierter Filter mit der Flexibilität natürlichsprachlicher Eingaben und macht Vektordatenbanken auch für Nutzer zugänglich, die mit der spezifischen Abfragesyntax nicht vertraut sind.