Практические советы и рекомендации для разработчиков, создающих RAG-приложения
Векторный поиск — это не так просто!
Векторный поиск, также известный как поиск по векторному сходству или поиск ближайших соседей, — это метод, используемый при извлечении данных для RAG-приложений и систем информационного поиска, чтобы находить элементы или точки данных, которые похожи или тесно связаны с заданным вектором запроса. При работе с большими наборами данных его часто представляют как нечто простое. Общее представление таково: можно просто подать данные в embedding model, чтобы сгенерировать векторные эмбеддинги, а затем перенести эти векторы в вашу векторную базу данных, чтобы получить нужные результаты.
как выполнить векторный поиск
Многие поставщики векторных баз данных продвигают свои возможности с помощью таких описаний, как "easy," "user-friendly," и "simple." Они утверждают, что вы можете добиться значимых результатов всего несколькими строками кода, обходя сложности машинного обучения, ИИ, ETL-процессов или детальной настройки системы.
И они правы; векторный поиск так же прост, как использование базовой численной библиотеки вроде NumPy. Чтобы продемонстрировать эту идею, я написал короткую демонстрацию всего в десять строк кода на Python с использованием алгоритма k-ближайших соседей (KNN). Этот простой подход эффективен и точен для небольших приложений с наборами данных до тысячи или десяти тысяч векторов.
import numpy as np
# Function to calculate Euclidean distance
def euclidean_distance(a, b):
return np.linalg.norm(a - b)
# Function to perform KNN
def knn(data, target, k):
# Calculate distances between target and all points in data
distances = [euclidean_distance(d, target) for d in data]
# Combine distances with data indices
distances = np.array(list(zip(distances, range(len(data)))))
# Sort by distance
sorted_distances = distances[distances[:, 0].argsort()]
# Get the top k closest indices
closest_k_indices = sorted_distances[:k, 1].astype(int)
# Return the top k closest vectors
return data[closest_k_indices]
Однако этот подход не сработает, когда ваш набор данных вырастет до умеренного уровня — более миллиона или десяти миллионов векторов. Это просто потому, что реальные приложения должны взаимодействовать с пользователями, быть доступными и всегда намного сложнее. Создание масштабируемого реального приложения требует тщательного учета различных факторов помимо написания кода, включая качество поиска, масштабируемость, доступность, мультитенантность, стоимость, безопасность и многое другое!
Так что давайте будем честны. Помните поговорку: "It works on my machine?" С векторным поиском то же самое: ваш прототип всегда работает; векторный поиск в production часто сложен, так каковы же лучшие практики создания приложения на базе векторного поиска в production?
Чтобы помочь вам справиться с этими вызовами, мы поделимся тремя важными советами по эффективному развертыванию вашей векторной базы данных в production-среде вашего RAG-приложения с Milvus:
Спроектируйте эффективную схему: Тщательно продумайте структуру ваших данных и то, как к ним будут выполняться запросы, чтобы создать схему, оптимизирующую производительность и масштабируемость.
Планируйте масштабируемость: Предусмотрите будущий рост и спроектируйте архитектуру так, чтобы она выдерживала увеличение объемов данных и пользовательского трафика.
Выберите оптимальный индекс и тонко настройте производительность: Выберите наиболее подходящий метод индексирования для вашего сценария использования и постоянно отслеживайте и корректируйте настройки производительности.
Следуя этим лучшим практикам, вы будете на верном пути к созданию надежного и эффективного приложения на базе векторного поиска. В комментариях ниже поделитесь своим опытом и любыми дополнительными советами, которые оказались для вас полезными!
Разработка эффективной стратегии схемы
Схема определяет структуру базы данных, включая таблицы, поля, связи и типы данных. Этот организованный каркас обеспечивает согласованное и предсказуемое хранение данных, упрощая управление, выполнение запросов и обслуживание. Выбор подходящей схемы особенно важен для векторных баз данных, таких как Milvus, которые работают с векторами и различными типами структурированных данных, включая метаданные и скалярные данные. Эти данные могут улучшить фильтрованный поиск и повысить общее качество результатов поиска. В этом разделе будут рассмотрены ключевые факторы, которые следует учитывать при выборе наиболее эффективной стратегии схемы.
Динамическая и фиксированная схема
В системах баз данных динамические и фиксированные схемы представляют два основных подхода к структурированию данных. Динамические схемы обеспечивают гибкость, упрощая вставку и извлечение данных без необходимости обширного выравнивания данных или процессов ETL. Этот подход идеально подходит для приложений, требующих быстрых изменений структуры данных. С другой стороны, разработчики ценят фиксированные схемы за их эффективность производительности и экономию памяти благодаря компактным форматам хранения.
Гибридный подход к схеме может быть полезен разработчикам, работающим над эффективными приложениями векторных баз данных. Этот метод сочетает надежность фиксированных схем для ключевых путей данных с гибкостью динамических схем для поддержки разнообразных сценариев использования. Например, в рекомендательной системе такие элементы, как названия продуктов и идентификаторы продуктов, могут различаться по важности в зависимости от контекста. Используя гибридную схему, разработчики могут обеспечить оптимальную производительность там, где это необходимо, сохраняя при этом способность адаптироваться к изменяющимся требованиям к данным.
Настройка первичных ключей и ключей секционирования
Первичные ключи и ключи секционирования — два важных понятия в векторных базах данных. На примере векторной базы данных Milvus мы можем глубже рассмотреть, как эти ключи функционируют в векторных базах данных.
Архитектура Milvus сегментирует данные на несколько компонентов: фиксированные и динамические поля (совместно называемые полезной нагрузкой), обязательное векторное поле и системные поля, такие как временные метки и универсальные уникальные идентификаторы (UUID), которые похожи на используемые в обычных реляционных базах данных.
Первичные ключи: В Milvus первичный ключ часто служит уникальным идентификатором, который в сценарии RAG может применяться к идентификатору фрагмента. К этому ключу часто обращаются, и его можно настроить на автоматическую генерацию. Он играет роль в быстром поиске и извлечении конкретных записей данных в базе данных.
Ключи секционирования: При создании коллекции в Milvus можно указать ключ секционирования. Этот ключ позволяет Milvus хранить сущности данных в разных секциях на основе значений их ключей, эффективно организуя данные в управляемые сегменты. Простой способ понять ключи секционирования — рассмотреть использование ключа секционирования, если существуют наборы данных, по которым вы хотите выполнять фильтрацию. Например, в условиях мультитенантности необходимы изоляция данных и эффективное распределение, поэтому хранение их в отдельных секциях может помочь этого добиться. Ключи секционирования также полезны для масштабируемости, поскольку разбиение данных на шарды с помощью хеширования позволяет базе данных более эффективно управлять крупномасштабными пользовательскими базами и мультитенантностью.
И первичные ключи, и ключи секционирования являются основополагающими для поддержания структурной целостности и операционной эффективности векторных баз данных, что делает их незаменимыми для обработки обширных наборов данных и обеспечения быстрого доступа к данным и их извлечения.
Выбор типов векторных эмбеддингов
При выборе векторных эмбеддингов для приложений RAG необходимо выбрать правильную ML-модель для создания векторов и понимать различные доступные типы эмбеддингов: плотные, разреженные и бинарные эмбеддинги.
Три популярные категории векторных эмбеддингов
Плотные эмбеддинги — наиболее часто используемый тип в приложениях векторных баз данных для поиска по семантическому сходству. Они известны своей надежностью и общей применимостью к различным типам данных. Популярные модели плотных эмбеддингов включают OpenAI, BGE и Cohere.
Разреженные эмбеддинги набирают популярность благодаря своей эффективности при поиске данных вне домена. Недавние достижения в таких моделях, как Splade и BGE M3, повысили их полезность в гетерогенном поиске, делая их универсальным выбором для разнообразных приложений.
Бинарные эмбеддинги, характеризующиеся бинарным форматом (нули и единицы), разработаны с учетом эффективности использования памяти, что делает их идеальными для специальных сценариев использования, таких как секвенирование белков. Модели вроде Meta ESM-2 обычно используются для генерации этих эмбеддингов, предоставляя целевые решения для конкретных поисковых потребностей.
Чтобы обеспечить точность результатов поиска для приложений RAG, нам нужно использовать не только плотные эмбеддинги. Поэтому нам необходимо найти решения, поддерживающие различные алгоритмы индексирования для эффективного и результативного поиска по разным типам векторных эмбеддингов. Milvus поддерживает различные индексы для управления плотными, разреженными, бинарными и даже гибридными разреженными и плотными эмбеддингами, обеспечивая эффективный поиск по различным измерениям данных и гарантируя оптимальную производительность в приложениях векторных баз данных.
Проектирование вашей схемы: практический пример
Давайте объединим эти элементы, которые мы только что рассмотрели, чтобы помочь нам эффективно спроектировать архитектуру схемы, которая повысит точность наших результатов поиска:
| Имя поля | Тип | Описание | Пример значения |
|---|---|---|---|
| chunkID | Int64 | Первичный ключ, уникально идентифицирует различные части документа | 123456789 |
| userID | Int64 | Ключ партиционирования, разделение данных основано на userID, чтобы гарантировать выполнение поиска в пределах одного userID | 987654321 |
| docID | Int64 | Уникальный идентификатор документа, используется для связывания различных фрагментов одного и того же документа | 555666777 |
| chunkData | varchar | Часть документа, содержащая несколько сотен байтов текста | "Это часть документа..." |
| dynamicParams | JSON | Хранит динамические параметры документа, такие как имя, URL-адрес источника и т. д. | {"name": "Example Document", "source": "example.com"} |
| sparseVector | Specific format | Данные, представляющие разреженный вектор. Конкретный формат будет иметь ненулевые значения только в определенных позициях для представления разреженности. | [0.1, 0, 0, 0.8, 0.4] |
| denseVector | Specific format | Данные, представляющие плотный вектор. Конкретный формат будет иметь фиксированное число измерений со значениями в каждом. | [0.2, 0.3, 0.4, 0.1] |
Демонстрация схемы для типичного приложения Retrieval Augmented Generation (RAG)
При рассмотрении этой схемы важно отметить наличие дополнительных полей, выходящих за пределы первичного ключа и векторного поля. Эти дополнительные поля играют роль в построении и использовании вашей векторной базы данных. Давайте разберемся:
Основы: К ним относятся ваш первичный ключ (chunkID) и ваши эмбеддинги (denseVector), которые нужны каждой записи в базе данных.
Поддержка мультитенантности: Мы добавили поле для разделения данных по тенантам нашего решения с добавлением userID. Это добавление помогает разделять и управлять доступом к данным для каждого пользователя, повышая безопасность и персонализацию.
Уточнение результатов поиска: Мы добавили несколько других полей, чтобы помочь нам с этим уточнением, включая:
docID: Это поле указывает на происхождение фрагмента и может использоваться для применения функции группового поиска Milvus. В нашем примере мы разделили документ на фрагменты и сохранили репрезентативное векторное вложение в поле denseVector, а в этом поле (docID) мы храним связанную информацию о документе. Вы можете включить аргументgroup_by_fieldв операцию search(), чтобы группировать результаты по идентификатору документа и находить релевантные документы вместо похожих отрывков или фрагментов. Это помогает возвращать релевантные документы, а не отдельные фрагменты из одного и того же документа.dynamicParams: Это поле можно фильтровать, что позволяет управлять данными и извлекать данные, соответствующие вашим потребностям. Это настоящий кладезь метаданных, таких как название документа, исходный URL и т. д. Мы задаем поле json для хранения нескольких пар «ключ-значение» в одном поле.sparseVector: Это поле содержит разреженное вложение фрагмента, что позволяет нам выполнять ANN-поиск и извлекать результаты на основе скалярного значения, связанного с разреженным вектором.
Диаграмма показывает, что мы можем собирать результаты из отдельных запросов, а затем переранжировать их, чтобы уточнить результаты поиска.
Разрабатывая схему, включающую эти дополнительные поля, вы можете создать более надежную и гибкую векторную базу данных, соответствующую требованиям вашего RAG-приложения. Она позволяет использовать преимущества векторного поиска, одновременно включая традиционные методы управления данными и их извлечения.
План масштабируемости
После того как вы добились успеха с вашим MVP RAG-приложением, пора начинать подготовку к развертыванию в продакшене. Это предполагает прогнозирование будущего роста и проектирование архитектуры, способной справляться с увеличивающимися объемами данных и пользовательским трафиком.
Чтобы обеспечить эффективное масштабирование вашего приложения, важно понимать, что масштабируемость в векторных базах данных создает уникальные проблемы по сравнению с традиционными реляционными базами данных, поскольку данные хранятся в большом централизованном индексе. Такая схема может привести к двум основным проблемам: низкой скорости индексации и ухудшению качества индекса из-за частых обновлений, что, в свою очередь, может снизить качество поиска.
Стратегия шардинга Milvus
Milvus может справляться с этими проблемами, разделяя весь набор данных на управляемые сегменты; мы можем выполнять отложенные обновления или уплотнять сегменты, как только они становятся нестабильными, поддерживая стабильное качество поиска. Такая сегментация способствует эффективной балансировке нагрузки, позволяя равномерно распределять запросы по всем вычислительным ядрам.
Использование разделов для мультитенантности также может помочь с масштабируемостью и производительностью, поскольку поиск ограничивается данными, релевантными разделу. Такой подход эффективно организует данные и повышает безопасность и конфиденциальность, ограничивая видимость подходящими пользователями. Кроме того, Milvus может эффективно управлять до десяти миллиардов точек данных в одной коллекции. Это очень много!
Для мультитенантных приложений с числом арендаторов менее 10 000 управление данными по коллекциям обеспечивает больший контроль над данными. Однако ключи разделов могут эффективно поддерживать неограниченное число арендаторов, динамически сегментируя данные для сервисов с миллионами пользователей.
Milvus — это распределенная система, разработанная для легкой обработки больших объемов запросов. А самое лучшее? Простое добавление дополнительных узлов может значительно повысить производительность, открывая широкий спектр возможностей для ваших приложений. Для небольших наборов данных, которым изначально не требуются значительные ресурсы, увеличение резервов памяти — обычно в два-три раза относительно текущего выделения — может эффективно удвоить количество запросов в секунду (QPS). Эта масштабируемая архитектура гарантирует, что по мере роста ваших данных возможности вашей базы данных смогут расти вместе с ними, обеспечивая эффективную и надежную производительность во всех аспектах.
Выбор, оценка и настройка вашего индекса
На этапе прототипирования загрузка всех данных в память является распространенной практикой для более быстрой обработки и упрощения разработки. Однако по мере перехода в production и роста объема данных становится невозможно хранить всё в памяти. Это связано с тем, что:
Память ограничена и дорога по сравнению с дисковым хранилищем.
Большие наборы данных могут превышать доступный объем памяти.
Загрузка всех данных в память может значительно увеличить время запуска и потребление ресурсов.
Чтобы эффективно обрабатывать более крупные наборы данных в production, необходимо выбрать подходящую стратегию индексирования. Правильный индекс может оптимизировать производительность вашего RAG-приложения с точки зрения скорости запросов, требований к хранилищу и задержки.
Индексы, поддерживаемые Milvus
Эта диаграмма помогает визуализировать различия между различными индексами на основе трех ключевых метрик:
Запросы в секунду (QPS): эта метрика показывает, сколько поисковых запросов индекс может обработать в секунду, отражая его пропускную способность и эффективность.
Хранилище: это объем дискового пространства, необходимый для хранения индекса, который может влиять на затраты на инфраструктуру и масштабируемость.
Задержка означает время, необходимое для обработки одного запроса и возврата результатов, что влияет на отзывчивость вашего приложения.
Сравнивая эти метрики для различных индексов, вы можете решить, какой индекс лучше всего подходит для вашего конкретного сценария использования и требований к производительности.
В Milvus мы предоставляем гибкую систему выбора индексов, адаптированную к различным потребностям в хранении и производительности:
GPU Index — это наш лучший вариант для высокопроизводительных сред, поддерживающий быструю обработку и извлечение данных.
Memory Index — это вариант среднего уровня, который балансирует производительность и емкость, предлагая стабильные показатели запросов в секунду (QPS) и возможность масштабирования до терабайтов хранилища со средней задержкой около 10 миллисекунд.
Disk Index может управлять десятками терабайтов с приемлемой задержкой около 100 миллисекунд, что подходит для более крупных наборов данных, менее чувствительных ко времени. Milvus — единственная векторная база данных с открытым исходным кодом, поддерживающая дисковый индекс.
Swap Index обеспечивает обмен данными между S3 или другими решениями объектного хранилища и памятью. Такой подход значительно снижает затраты — примерно в десять раз — при эффективном управлении задержкой. Типичное время доступа составляет около 100 миллисекунд, но для реже используемых («холодных») данных оно может увеличиваться до нескольких секунд, что делает его применимым для офлайн-сценариев и приложений, чувствительных к стоимости.
После выбора индекса вы можете оценить его производительность на основе времени построения, точности, производительности и потребления ресурсов. Например, неоптимизированный индекс может поддерживать только 20 запросов в секунду без необходимости какого-либо построения. Оптимизация индекса может значительно повысить QPS, потенциально увеличивая его в десять раз с каждой итерацией настройки, хотя и ценой увеличения времени просмотра.
Чтобы эффективно выбрать и тонко настроить индекс, следует:
Выбрать подходящий тип индекса на основе ваших конкретных потребностей.
Настроить параметры индекса для оптимизации производительности.
Провести бенчмаркинг ваших сценариев использования, чтобы убедиться, что индекс работает ожидаемым образом.
Настроить параметры поиска для дальнейшего повышения производительности.
Если вы не уверены в процессе оптимизации, используйте возможности инструментов бенчмаркинга, таких как VectorDBBench. Этот инструмент, разработанный и выпущенный с открытым исходным кодом компанией Zilliz, может оценивать все основные векторные базы данных. Он позволяет проводить комплексные эксперименты и тонко настраивать вашу систему для достижения оптимальной производительности.
Для быстрой справки мы подготовили удобную шпаргалку, в которой описана производительность каждого индекса в нашем каталоге GPU-индексов. Этот ресурс поможет вам оптимизировать производительность и экономическую эффективность, направив вас к индексу, лучше всего соответствующему потребностям вашего приложения.
Шпаргалка по индексам
Итоги
В этом подробном руководстве мы рассмотрели многогранный мир векторных баз данных и практические подходы, необходимые для максимального повышения их эффективности и масштабируемости. От основ проектирования схемы до сложностей управления большими наборами данных — мы охватили ключевые стратегии и лучшие практики, которые разработчикам необходимо знать при работе с векторными базами данных.
По мере развития векторных баз данных осведомленность об этих аспектах позволит разработчикам создавать более надежные, эффективные и масштабируемые приложения. Независимо от того, являетесь ли вы опытным специалистом по базам данных или новичком в этой области, представленные здесь сведения помогут вам с большей уверенностью и компетентностью ориентироваться в сложностях векторных баз данных.
Читать далее

Top 10 Context Engineering Techniques You Should Know for Production RAG
A practical guide to context engineering for production LLM systems, covering RAG, context processing, memory, agents, and multimodal context.

AI Agents Are Quietly Transforming E-Commerce — Here’s How
Discover how AI agents transform e-commerce with autonomous decision-making, enhanced product discovery, and vector search capabilities for today's retailers.

VidTok: Rethinking Video Processing with Compact Tokenization
VidTok tokenizes videos to reduce redundancy while preserving spatial and temporal details for efficient processing.



