¿Qué es Pymilvus?
Introducción
Pymilvus es el SDK de Python creado para Milvus y Zilliz Cloud. Es un cliente basado en gRPC que utiliza un protobuf común de Milvus compartido entre todos los SDK. Tanto para usuarios nuevos como avanzados, proporciona acceso a todas las funciones ofrecidas por Milvus y es uno de nuestros SDK más populares.
Problemas que estábamos encontrando
La idea clave en torno a la base de datos vectorial Milvus es dar a los usuarios tantas palancas como sea posible para ayudar a ajustar el sistema a su caso de uso específico. Sin embargo, la búsqueda aproximada es, en esencia, aproximada; no existe un algoritmo universal. Cada uno de los algoritmos destaca en cómputo vs. velocidad vs. recall/precisión. Aún más, cada uno de los algoritmos puede configurarse para lograr un equilibrio diferente entre las categorías anteriores. Por ejemplo, HNSW vs. IVF-PQ. IVF-PQ está optimizado para cómputo y velocidad, reduciendo significativamente la carga de memoria y los tiempos de búsqueda a costa de una reducción significativa del recall. HNSW es lo contrario; HNSW sacrifica cómputo por velocidad y recall. Lo interesante es que sus fortalezas pueden intercambiarse mediante configuración. Toda esta configuración está solo en el nivel del índice. En el nivel del clúster, hay equilibrios que deben hacerse en términos de costo vs. velocidad, lo cual es único para cada usuario. Puede que a algunos usuarios no les importe la alta disponibilidad y no quieran replicación. Puede que a algunos usuarios no les importe la consistencia y prefieran ser menos consistentes en beneficio de la velocidad. Algunos usuarios pueden querer adjuntar un TTL a sus datos para reducir los costos de almacenamiento, mientras que otros desean tener cada operación respaldada. Aunque esta personalización es excelente para usuarios grandes, donde exprimir el equilibrio resulta beneficioso en conjuntos de datos de miles de millones, para usuarios más pequeños, todas estas palancas simplemente añaden demasiada confusión.
Además de que estas palancas sean un problema, en este momento, Zilliz Cloud y Milvus no son intercambiables debido a los índices y los parámetros de conexión.
Qué es MilvusClient
MilvusClient es un intento de simplificar la API para la mayoría de los usuarios. Muchos usuarios no quieren lidiar con conexiones, esquemas, indexación, carga, parámetros, etc. MilvusClient oculta todos estos aspectos envolviendo el SDK de Pymilvus con una API simple que es idéntica tanto para Milvus como para Zilliz. En este momento, ofrece:
- insert_data()
- upsert_data()
- search_data()
- query_data()
- get_vectors_by_pk()
- delete_by_pk()
- add_partition()
- remove_partition()
Se decidió que estas funciones fueran las funciones clave que cualquier usuario básico necesitaría. En el momento actual, Pymilvus ofrece las operaciones, pero hay muchas cosas adicionales que deben hacerse para garantizar que funcionen correctamente.
insert_data:
Usando MilvusClient, no es necesario crear un esquema para una colección. En su lugar, el esquema se genera automáticamente a partir de los datos insertados. Esto implica determinar el FieldSchema requerido y cómo organizarlo. Muchos usuarios encontraron que crear este esquema era un punto problemático, por eso lo estamos haciendo entre bastidores. Una vez que se admita el esquema dinámico, podremos cambiar la implementación sin cambios para el usuario.
def _infer_fields(self, data):
"""Infer all the fields based on the input data."""
# TODO: Assuming ordered dict for 3.7
fields = {}
# Figure out each datatype of the input.
for key, value in data.items():
# Infer the corresponding datatype of the metadata
dtype = infer_dtype_bydata(value)
# Datatype isnt compatible
if dtype in (DataType.UNKNOWN, DataType.NONE):
logger.error(
"Failed to parse schema for collection %s, unrecognized dtype for key: %s",
self.collection_name,
key,
)
raise ValueError(f"Unrecognized datatype for {key}.")
# Create an entry under the field name
fields[key] = {}
fields[key]["name"] = key
fields[key]["dtype"] = dtype
# Area for attaching kwargs for certain datatypes
if dtype == DataType.VARCHAR:
fields[key]["max_length"] = 65_535
return fields
Para MilvusClient, queríamos ceñirnos a un formato de datos similar al de otros proyectos del área: una lista de diccionarios. Este formato es fácil de entender y usar, y es equivalente a los Documents que se encuentran en LlamaIndex y LangChain. Sin embargo, este formato es incompatible con pymilvus, ya que el insert de pymilvus recibe datos columnares como una lista de listas. Este formato de datos columnar no es fácil de usar, ya que implica ordenar las listas en el orden exacto en que se hizo el esquema y no ofrecía ninguna flexibilidad. Además, los mensajes de error recibidos por datos con formato incorrecto podrían ser mejores.
for k in data:
for key, value in k.items():
if key in self.fields:
insert_dict.setdefault(key, []).append(value)
for i in self.tqdm(range(0, len(data), batch_size), disable=not progress_bar):
# Convert dict to list of lists batch for insertion
try:
insert_batch = [
insert_dict[key][i : i + batch_size]
for key in self.fields
if key != ignore_pk
]
Con todos los cambios que vendrán en el futuro para esquemas, JSONs, etc., tener un wrapper alrededor de un insert simple nos permitirá hacer el trabajo pesado y dejar una API sencilla para el usuario.
upsert_data:
En la versión 2.2, upsert no existe en Milvus. Para realizar un upsert, es necesario hacer un delete -> insert. Como muchos usuarios buscan esta función, decidimos incluirla en el cliente. Una vez que la función se agregue a pymilvus, podremos cambiarla fácilmente sin requerir cambios en el código del usuario.
pks = [x[self.pk_field] for x in data]
self.delete_by_pk(pks, timeout)
ret = self.insert_data(
data=data,
timeout=timeout,
batch_size=batch_size,
partition=partition,
progress_bar=progress_bar,
)
search_data:
Las modificaciones clave al comando de búsqueda fueron los parámetros de búsqueda predeterminados y la conversión de las salidas en una lista de diccionarios.
ret = []
for hits in res:
query_result = []
for hit in hits:
ret_dict = {x: hit.entity.get(x) for x in return_fields}
query_result.append({"score": hit.score, "data": ret_dict})
ret.append(query_result)
query_data:
Las modificaciones clave al comando de consulta fueron la conversión de las salidas en una lista de diccionarios.
get_vectors_by_pk:
La extracción de vectores en pymilvus se hace mediante consultas. Lo que muchos usuarios no saben es que la consulta debe hacerse en función de un filtro de clave primaria. Además de esto, si se usa una clave primaria varchar, la expresión en la consulta tendría que tener comillas escapadas, algo tedioso y desconocido para los usuarios.
# Varchar pks need double quotes around the values
if self.fields[self.pk_field] == DataType.VARCHAR:
ids = ['"' + str(entry) + '"' for entry in pks]
expr = f"""{self.pk_field} in [{','.join(ids)}]"""
else:
ids = [str(entry) for entry in pks]
expr = f"{self.pk_field} in [{','.join(ids)}]"
delete_by_pk:
Similar a get_vectors_by_pk.
add_partition and delete_partition:
Para la lógica de particiones, los usuarios deben saber que cambiar particiones requiere descargar y cargar una colección. Esto ahora se gestiona entre bastidores.
Conclusión:
En general, el objetivo principal de este cliente es agregar operaciones fáciles de usar que no existen o no están optimizadas en el lado de pymilvus. A medida que pymilvus mejore, podremos optimizar estas operaciones entre bastidores y mantener una API fácil de usar.
Sigue leyendo

Demystifying the Milvus Sizing Tool
Explore how to use the Sizing Tool to select the optimal configuration for your Milvus deployment.

Optimizing Embedding Model Selection with TDA Clustering: A Strategic Guide for Vector Databases
Discover how Topological Data Analysis (TDA) reveals hidden embedding model weaknesses and helps optimize vector database performance.

Vector Databases vs. Key-Value Databases
Use a vector database for AI-powered similarity search; use a key-value database for high-throughput, low-latency simple data lookups.



