Menghasilkan Ekspresi Filter Kueri Milvus dengan Model Bahasa Besar

Dalam tutorial ini, kami akan mendemonstrasikan cara menggunakan Model Bahasa Besar (LLM) untuk secara otomatis menghasilkan ekspresi filter Milvus dari kueri bahasa alami. Pendekatan ini membuat kueri basis data vektor menjadi lebih mudah diakses dengan mengizinkan pengguna untuk mengekspresikan kondisi penyaringan yang kompleks dalam bahasa Inggris sederhana, yang kemudian dikonversi ke sintaks Milvus yang tepat.

Milvus mendukung kemampuan pemfilteran yang canggih termasuk:

  • Operator Dasar: Operator perbandingan seperti ==, !=, >, <, >=, <=
  • Operator Boolean: Operator logika seperti and, or, not untuk kondisi yang kompleks
  • Operasi String: Pencocokan pola dengan like dan fungsi string lainnya
  • Operasi Larik: Bekerja dengan bidang larik menggunakan array_contains, array_length, dll.
  • Operasi JSON: Mengajukan pertanyaan ke bidang JSON dengan operator khusus

Dengan mengintegrasikan LLM dengan dokumentasi Milvus, kita dapat membuat sistem cerdas yang memahami kueri bahasa alami dan menghasilkan ekspresi filter yang benar secara sintaksis. Tutorial ini akan memandu Anda dalam proses menyiapkan sistem ini, menyoroti keefektifannya dalam berbagai skenario penyaringan.

Ketergantungan dan Lingkungan

$ pip install --upgrade pymilvus openai requests docling beautifulsoup4
print("Environment setup complete!")

Menyiapkan variabel lingkungan

Konfigurasikan kredensial API OpenAI Anda untuk mengaktifkan pembuatan embedding dan pembuatan ekspresi filter berbasis LLM. Ganti 'your_openai_api_key' dengan kunci API OpenAI Anda yang sebenarnya.

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.")

Membuat Koleksi Sampel

Sekarang mari kita buat contoh koleksi Milvus dengan data pengguna. Koleksi ini akan berisi bidang skalar (untuk penyaringan) dan penyematan vektor (untuk pencarian semantik). Kita akan menggunakan model penyematan teks OpenAI untuk menghasilkan representasi vektor dari informasi pengguna.

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.")

Kode di atas membuat koleksi Milvus dengan struktur sebagai berikut:

  • pk: Bidang kunci utama (VARCHAR)
  • nama: Nama pengguna (VARCHAR)
  • usia: Usia pengguna (INT64)
  • kota: Kota pengguna (VARCHAR)
  • hobi: Hobi pengguna (VARCHAR)
  • penyematan: Penyematan vektor (FLOAT_VECTOR, 1536 dimensi)

Kami telah memasukkan 11 sampel pengguna dengan informasi pribadi mereka dan menghasilkan penyematan untuk kemampuan pencarian semantik. Setiap informasi pengguna diubah menjadi teks deskriptif yang menangkap nama, lokasi, usia, dan minat mereka sebelum disematkan. Mari kita verifikasi bahwa koleksi kita telah berhasil dibuat dan berisi data yang diharapkan dengan melakukan kueri pada beberapa catatan sampel.

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)

Mengumpulkan Dokumentasi Ekspresi Filter Milvus

Untuk membantu model bahasa besar lebih memahami sintaks ekspresi filter Milvus, kita perlu menyediakan dokumentasi resmi yang relevan. Kita akan menggunakan pustaka docling untuk mengambil beberapa halaman utama dari situs web resmi Milvus.

Halaman-halaman ini berisi informasi rinci tentang:

  • Operator Boolean: and, or, not untuk kondisi logika yang kompleks
  • Operator dasar: Operator perbandingan seperti ==, !=, >, <, >=, <=
  • Template pemfilteran: Pola dan sintaks pemfilteran tingkat lanjut
  • Pencocokan string: Pencocokan pola dengan like dan operasi string lainnya

Dokumentasi ini akan berfungsi sebagai basis pengetahuan bagi LLM kami untuk menghasilkan ekspresi filter yang akurat.

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())

Penggalian dokumentasi menyediakan cakupan yang komprehensif dari sintaks filter Milvus. Basis pengetahuan ini akan memungkinkan LLM kami untuk memahami nuansa konstruksi ekspresi filter, termasuk penggunaan operator yang tepat, referensi bidang, dan kombinasi kondisi yang kompleks.

Pembuatan Filter yang Didukung LLM

Sekarang kita telah memiliki konteks dokumentasi, mari kita siapkan sistem LLM untuk menghasilkan ekspresi filter. Kita akan membuat prompt terstruktur yang menggabungkan dokumentasi yang telah di-scan dengan pertanyaan pengguna untuk menghasilkan ekspresi filter Milvus yang benar secara sintaksis.

Sistem pembuatan filter kami menggunakan prompt yang dibuat dengan hati-hati:

  1. Menyediakan konteks: Mencakup dokumentasi Milvus yang lengkap sebagai bahan referensi
  2. Menetapkan batasan: Memastikan LLM hanya menggunakan sintaks dan fitur yang terdokumentasi
  3. Menegakkan akurasi: Memerlukan ekspresi yang benar secara sintaksis
  4. Mempertahankan fokus: Hanya mengembalikan ekspresi filter tanpa penjelasan

Mari kita uji dengan kueri bahasa alami dan lihat seberapa baik kinerja 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 berhasil menghasilkan ekspresi filter yang menggabungkan beberapa kondisi:

  • Perbandingan usia menggunakan >
  • Pencocokan beberapa kota menggunakan operator in
  • Perujukan dan sintaksis bidang yang tepat

Hal ini menunjukkan kekuatan dari penyediaan konteks dokumentasi yang komprehensif untuk memandu pembuatan filter LLM.

Menguji Filter yang Dihasilkan

Sekarang mari kita uji ekspresi filter yang telah dihasilkan dengan menggunakannya dalam operasi pencarian Milvus yang sebenarnya. Kami akan menggabungkan pencarian semantik dengan penyaringan yang tepat untuk menemukan pengguna yang sesuai dengan maksud kueri dan kriteria tertentu.

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()

Analisis Hasil

Hasil pencarian menunjukkan keberhasilan integrasi filter yang dihasilkan LLM dengan pencarian vektor Milvus. Penyaringan mengidentifikasi dengan tepat pengguna yang:

  • Berusia lebih dari 30 tahun
  • Tinggal di London, Tokyo, atau Toronto
  • Sesuai dengan konteks semantik kueri

Pendekatan ini menggabungkan ketepatan pemfilteran terstruktur dengan fleksibilitas input bahasa alami, sehingga database vektor lebih mudah diakses oleh pengguna yang mungkin tidak terbiasa dengan sintaksis kueri tertentu.