Что такое Pymilvus?
Введение
Pymilvus — это Python SDK, созданный для Milvus и Zilliz Cloud. Это клиент на основе gRPC, который использует общий Milvus protobuf, единый для всех SDK. Для новых и продвинутых пользователей он предоставляет доступ ко всем функциям, предлагаемым Milvus, и является одним из наших самых популярных SDK.
Проблемы, с которыми мы сталкивались
Ключевая идея векторной базы данных Milvus — предоставить пользователям как можно больше рычагов управления, чтобы помочь точно настроить систему под их конкретный сценарий использования. Однако приблизительный поиск по своей сути является приблизительным; универсального алгоритма не существует. Каждый из алгоритмов имеет свои преимущества в соотношении вычислений, скорости и recall/точности. Более того, каждый из алгоритмов можно настроить так, чтобы достичь разного баланса между предыдущими категориями. Например, HNSW против IVF-PQ. IVF-PQ оптимизирован для вычислений и скорости, значительно снижая нагрузку на память и время поиска ценой существенно сниженного recall. HNSW — противоположность; HNSW жертвует вычислениями ради скорости и recall. Интересно то, что их сильные стороны можно менять с помощью конфигурации. Вся эта конфигурация находится только на уровне индекса. На уровне кластера необходимо находить баланс между стоимостью и скоростью, что уникально для каждого пользователя. Некоторым пользователям может быть не важна высокая доступность, и они не хотят репликации. Некоторым пользователям может быть не важна согласованность, и они предпочли бы меньшую согласованность ради преимущества в скорости. Некоторые пользователи могут захотеть назначить TTL своим данным, чтобы снизить затраты на хранение, в то время как другие хотят, чтобы каждая операция резервировалась. Хотя такая настраиваемость отлично подходит для крупных пользователей, где выжимание баланса полезно на наборах данных размером в миллиарды, для небольших пользователей все эти рычаги лишь добавляют слишком много путаницы.
Помимо того, что эти рычаги являются проблемой, на данный момент Zilliz Cloud и Milvus не взаимозаменяемы из-за индексов и параметров подключения.
Что такое MilvusClient
MilvusClient — это попытка упростить API для большинства пользователей. Многие пользователи не хотят иметь дело с подключениями, схемами, индексированием, загрузкой, параметрами и т. д. MilvusClient скрывает все эти аспекты, оборачивая Pymilvus SDK простым API, одинаковым как для Milvus, так и для Zilliz. На данный момент он предлагает:
- insert_data()
- upsert_data()
- search_data()
- query_data()
- get_vectors_by_pk()
- delete_by_pk()
- add_partition()
- remove_partition()
Было решено, что эти функции являются ключевыми функциями, которые понадобятся любому базовому пользователю. В настоящий момент операции предлагаются Pymilvus, но есть много дополнительных вещей, которые необходимо сделать, чтобы убедиться, что они работают корректно.
insert_data:
При использовании MilvusClient нет необходимости создавать схему для коллекции. Вместо этого схема автоматически генерируется из вставляемых данных. Это включает определение необходимой FieldSchema и способа ее организации. Многие пользователи считали создание этой схемы болезненным моментом, поэтому мы делаем это за кулисами. Как только будет поддерживаться динамическая схема, мы сможем изменить реализацию без изменений для пользователя.
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
Для MilvusClient мы хотели придерживаться формата данных, похожего на другие проекты в этой области, — списка словарей. Этот формат прост для понимания и работы и эквивалентен Documents, используемым в LlamaIndex и LangChain. Однако этот формат несовместим с pymilvus, поскольку insert в pymilvus принимает колоночные данные в виде списка списков. Этот колоночный формат данных неудобен в работе, так как требует упорядочивать списки точно в том порядке, в котором была создана схема, и не предлагает никакой гибкости. Кроме того, сообщения об ошибках, получаемые при неправильно отформатированных данных, могли бы быть лучше.
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
]
С учетом всех будущих изменений, связанных со схемой, JSON и т. д., наличие обертки вокруг простой вставки позволит нам выполнять тяжелую работу и оставить пользователю простой API.
upsert_data:
В версии 2.2 upsert в Milvus не существует. Чтобы выполнить upsert, необходимо выполнить delete -> insert. Поскольку многие пользователи ищут эту возможность, мы решили включить ее в клиент. Как только эта функция будет добавлена в pymilvus, мы сможем легко изменить ее, не требуя изменений в коде пользователя.
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:
Ключевыми изменениями в команде поиска стали параметры поиска по умолчанию и преобразование выходных данных в список словарей.
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:
Ключевыми изменениями в команде query стало преобразование выходных данных в список словарей.
get_vectors_by_pk:
Извлечение векторов в pymilvus выполняется через querying. Многие пользователи не знают, что запрос должен выполняться на основе фильтра по первичному ключу. Кроме того, при использовании первичного ключа varchar выражение в запросе должно содержать экранированные кавычки, что является утомительным и неизвестным пользователям.
# 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:
Аналогично get_vectors_by_pk.
add_partition and delete_partition:
Для логики partition пользователям необходимо знать, что изменение partitions требует выгрузки и загрузки collection. Теперь это обрабатывается за кулисами.
Conclusion:
В целом, основная цель этого клиента — добавить простые в использовании операции, которые отсутствуют или не оптимизированы на стороне pymilvus. По мере улучшения pymilvus мы сможем оптимизировать эти операции за кулисами и сохранить простой в использовании API.
Читать далее

Zilliz Cloud Update: Tiered Storage, Business Critical Plan, Cross-Region Backup, and Pricing Changes
This release offers a rebuilt tiered storage with lower costs, a new Business Critical plan for enhanced security, and pricing updates, among other features.

Why Not All VectorDBs Are Agent-Ready
Explore why choosing the right vector database is critical for scaling AI agents, and why traditional solutions fall short in production.

Building RAG Pipelines for Real-Time Data with Cloudera and Milvus
explore how Cloudera can be integrated with Milvus to effectively implement some of the key functionalities of RAG pipelines.



