Generar Expresiones de Filtro de Consulta Milvus con Grandes Modelos de Lenguaje
En este tutorial, demostraremos cómo utilizar Modelos de Lenguaje Grande (LLMs) para generar automáticamente expresiones de filtro Milvus a partir de consultas en lenguaje natural. Este enfoque hace que la consulta de bases de datos vectoriales sea más accesible al permitir a los usuarios expresar condiciones de filtrado complejas en inglés sencillo, que luego se convierten a la sintaxis Milvus adecuada.
Milvus admite sofisticadas capacidades de filtrado, entre las que se incluyen:
- Operadores básicos: Operadores de comparación como
==,!=,>,<,>=,<= - Operadores booleanos: Operadores lógicos como
and,or,notpara condiciones complejas - Operaciones con cadenas: Comparación de patrones con
likey otras funciones de cadena - Operaciones con matrices: Trabajo con campos de array utilizando
array_contains,array_length, etc. - Operaciones JSON: Consulta de campos JSON con operadores especializados
Integrando los LLM con la documentación de Milvus, podemos crear un sistema inteligente que entienda las consultas en lenguaje natural y genere expresiones de filtro sintácticamente correctas. Este tutorial recorrerá el proceso de configuración de este sistema, destacando su eficacia en varios escenarios de filtrado.
Dependencias y entorno
$ pip install --upgrade pymilvus openai requests docling beautifulsoup4
print("Environment setup complete!")
Configurar variables de entorno
Configure sus credenciales de la API de OpenAI para permitir la generación de incrustaciones y la creación de expresiones de filtro basadas en LLM. Sustituya 'your_openai_api_key' por su clave de API de OpenAI real.
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.")
Crear una colección de muestra
Ahora vamos a crear una colección Milvus de muestra con datos de usuario. Esta colección contendrá tanto campos escalares (para filtrado) como incrustaciones vectoriales (para búsqueda semántica). Utilizaremos el modelo de incrustación de texto de OpenAI para generar representaciones vectoriales de la información del usuario.
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.")
Imprimir 3 datos de muestra
El código anterior crea una colección Milvus con la siguiente estructura:
- pk: Campo clave primaria (VARCHAR)
- name: Nombre de usuario (VARCHAR)
- age: Edad del usuario (INT64)
- ciudad: Ciudad del usuario (VARCHAR)
- hobby: Afición del usuario (VARCHAR)
- incrustación: Incrustación vectorial (FLOAT_VECTOR, 1536 dimensiones)
Hemos insertado 11 usuarios de muestra con su información personal y generamos incrustaciones para las capacidades de búsqueda semántica. La información de cada usuario se convierte en un texto descriptivo que recoge su nombre, ubicación, edad e intereses antes de ser incrustado. Verifiquemos que nuestra colección se ha creado correctamente y que contiene los datos esperados consultando algunos registros de muestra.
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)
Recopilación de documentación de expresiones de filtro Milvus
Para ayudar al gran modelo de lenguaje a entender mejor la sintaxis de las expresiones de filtrado de Milvus, necesitamos proporcionarle documentación oficial relevante. Utilizaremos la biblioteca docling para extraer varias páginas clave del sitio web oficial de Milvus.
Estas páginas contienen información detallada sobre:
- Operadores booleanos:
and,or,notpara condiciones lógicas complejas - Operadores básicos: Operadores de comparación como
==,!=,>,<,>=,<= - Patrones defiltrado: Patrones y sintaxis de filtrado avanzados
- Coincidencia de cadenas: Coincidencia de patrones con
likey otras operaciones con cadenas
Esta documentación servirá de base de conocimientos para que nuestro LLM genere expresiones de filtrado precisas.
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())
El raspado de la documentación proporciona una cobertura exhaustiva de la sintaxis de los filtros Milvus. Esta base de conocimientos permitirá a nuestro LLM comprender los matices de la construcción de expresiones de filtrado, incluido el uso adecuado de operadores, la referencia a campos y las combinaciones de condiciones complejas.
Generación de filtros con LLM
Ahora que tenemos el contexto de la documentación, configuremos el sistema LLM para generar expresiones de filtro. Crearemos una consulta estructurada que combine la documentación extraída con las consultas del usuario para producir expresiones de filtro Milvus sintácticamente correctas.
Nuestro sistema de generación de filtros utiliza un indicador cuidadosamente elaborado que:
- Proporciona contexto: Incluye la documentación completa de Milvus como material de referencia
- Establece restricciones: Garantiza que el LLM sólo utilice la sintaxis y las características documentadas.
- Exige precisión: Requiere expresiones sintácticamente correctas
- Mantiene el enfoque: Devuelve sólo la expresión filtrada sin explicaciones
Probemos esto con una consulta en lenguaje natural y veamos qué tal funciona el 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}")
El LLM ha generado correctamente una expresión de filtro que combina varias condiciones:
- Comparación de edad mediante
> - Comparación de varias ciudades mediante el operador
in - Sintaxis y referencias de campo correctas
Esto demuestra el poder de proporcionar un contexto de documentación completo para guiar la generación de filtros LLM.
Prueba del filtro generado
Ahora vamos a probar nuestra expresión de filtro generada utilizándola en una operación de búsqueda Milvus real. Combinaremos la búsqueda semántica con un filtrado preciso para encontrar usuarios que coincidan tanto con la intención de la consulta como con los criterios específicos.
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()
Análisis de resultados
Los resultados de la búsqueda demuestran el éxito de la integración de los filtros generados por LLM con la búsqueda vectorial de Milvus. El filtro identificó correctamente a los usuarios que:
- Tienen más de 30 años
- Viven en Londres, Tokio o Toronto
- Coinciden con el contexto semántico de la consulta
Este enfoque combina la precisión del filtrado estructurado con la flexibilidad de la entrada en lenguaje natural, lo que hace que las bases de datos vectoriales sean más accesibles para los usuarios que pueden no estar familiarizados con la sintaxis específica de la consulta.