Qdrant — открытая векторная база данных на Rust, которая хранит эмбеддинги и ищет ближайшие соседние точки по косинусной или другой метрике. В отличие от полнотекстового поиска, она сравнивает смысл: запрос про резервное копирование находит статью про бэкапы, даже если точных слов в ней нет. Qdrant распространяется под Apache 2.0, ставится одним контейнером, поддерживает фильтрацию по payload, квантование и распределённый режим. Разберём запуск, схему коллекции, загрузку данных и эксплуатацию.

Как устроена векторная коллекция

Коллекция — набор точек. У каждой точки есть идентификатор, один или несколько векторов и payload — произвольный JSON с метаданными. Поиск идёт по HNSW-индексу, который строится в памяти; payload-индексы ускоряют фильтры. Размерность вектора определяется моделью эмбеддингов и не может быть изменена после создания коллекции.

  • Cosine — угол между векторами, обычный выбор для текстовых эмбеддингов.
  • Dot — скалярное произведение, если векторы уже нормализованы.
  • Euclid — евклидово расстояние для координатных признаков.
  • Manhattan — сумма модулей разностей, устойчивее к выбросам.

Запуск в Docker

Для разработки достаточно одного контейнера. Порт 6333 отдаёт REST и веб-панель, 6334 — gRPC.

docker run -d --name qdrant \
  -p 6333:6333 -p 6334:6334 \
  -v qdrant_storage:/qdrant/storage \
  qdrant/qdrant

Панель доступна по адресу http://localhost:6333/dashboard. Для продакшена контейнер запускают с ограничением памяти: индекс HNSW живёт в RAM, и экономия на этом месте превращается в задержки.

Создание коллекции

Коллекция создаётся запросом PUT. В теле указывают размерность и метрику. Ниже — демонстрационная коллекция на четыре измерения: так все команды можно скопировать целиком, не генерируя настоящий вектор. В боевой коллекции размерность равна размерности модели — обычно 384, 768 или 1024.

curl -X PUT http://localhost:6333/collections/demo \
  -H 'Content-Type: application/json' \
  -d '{"vectors": {"size": 4, "distance": "Cosine"}}'

Ошибка «wrong vector size» означает, что модель не совпадает с объявленной размерностью.

Загрузка точек

Точки загружаются пакетами. Параметр wait=true заставляет сервер подтвердить запись до ответа; для массовых загрузок его отключают и следят за очередью.

curl -X PUT 'http://localhost:6333/collections/demo/points?wait=true' \
  -H 'Content-Type: application/json' \
  -d '{"points": [
    {"id": 1, "vector": [0.10, 0.20, 0.30, 0.40], "payload": {"lang": "ru", "source": "manual"}},
    {"id": 2, "vector": [0.90, 0.10, 0.05, 0.00], "payload": {"lang": "en", "source": "faq"}}
  ]}'

В реальном проекте векторы приходят из модели. С Python-клиентом удобно использовать FastEmbed: он скачивает модель и считает эмбеддинги локально, без внешнего API.

from fastembed import TextEmbedding
from qdrant_client import QdrantClient, models

model = TextEmbedding(model_name="sentence-transformers/all-MiniLM-L6-v2")
client = QdrantClient(url="http://localhost:6333")

client.create_collection(
    "docs",
    vectors_config=models.VectorParams(size=384, distance=models.Distance.COSINE),
)
texts = ["настройка резервного копирования", "обновление ядра Linux"]
vectors = list(model.embed(texts))
client.upsert("docs", points=[
    models.PointStruct(id=i, vector=v.tolist(), payload={"lang": "ru"})
    for i, v in enumerate(vectors, start=1)
])

Модель all-MiniLM-L6-v2 даёт 384 измерения; для русских текстов берите многоязычную модель и меняйте размерность коллекции.

Поиск и фильтры

Универсальный эндпоинт /points/query принимает вектор, лимит и фильтр. Фильтры применяются до или во время обхода индекса, поэтому важно, чтобы поля были проиндексированы.

curl -X POST http://localhost:6333/collections/demo/points/query \
  -H 'Content-Type: application/json' \
  -d '{"query": [0.12, 0.18, 0.31, 0.39], "limit": 5, "with_payload": true,
       "filter": {"must": [{"key": "lang", "match": {"value": "ru"}}]}}'

В ответе приходят точки с score: для Cosine чем выше, тем ближе по смыслу. Не сравнивайте score разных моделей, а порог score_threshold подбирайте на размеченной выборке.

Payload-индексы и гибридный поиск

Без индекса фильтр по payload всё равно работает, но перебирает точки. Индекс создаётся для конкретного поля и типа: keyword, integer, float, datetime, bool.

curl -X PUT http://localhost:6333/collections/demo/index \
  -H 'Content-Type: application/json' \
  -d '{"field_name": "lang", "field_schema": "keyword"}'

Для гибридного поиска добавляют разреженный вектор и объединяют результаты dense- и sparse-запросов через RRF: это даёт и смысловую близость, и точное совпадение терминов.

Память и квантование

Основной ресурс — оперативная память. Векторы можно держать на диске через on_disk, а скалярное квантование int8 уменьшает объём примерно вчетверо при небольшой потере качества.

curl -X PATCH http://localhost:6333/collections/demo \
  -H 'Content-Type: application/json' \
  -d '{"quantization_config": {"scalar": {"type": "int8", "always_ram": true}},
       "optimizers_config": {"indexing_threshold": 20000}}'

Параметр indexing_threshold задаёт, сколько точек накопить до построения индекса: низкое значение замедляет загрузку, высокое ухудшает поиск на старте.

Резервные копии

Снапшот коллекции — согласованный снимок на диске сервера; его можно скачать и восстановить на другом сервере.

curl -X POST http://localhost:6333/collections/demo/snapshots
curl http://localhost:6333/collections/demo/snapshots
curl -X PUT http://localhost:6333/collections/demo/snapshots/recover \
  -H 'Content-Type: application/json' \
  -d '{"location": "file:///qdrant/snapshots/demo/demo-latest.snapshot"}'

Для регулярного бэкапа снапшоты складывают в S3-совместимое хранилище и проверяют восстановление на тестовом стенде.

Диагностика

Метрики отдаются в формате Prometheus, логи — в stdout контейнера. Для медленных запросов смотрите hnsw_ef: увеличение повышает полноту, но замедляет поиск.

curl -s localhost:6333/metrics | grep qdrant_collections
curl -s localhost:6333/collections/demo | python -m json.tool
docker logs --tail 100 qdrant
docker stats qdrant --no-stream
  • Высокая задержка — проверьте payload-индексы и долю точек на диске.
  • Рост памяти — уменьшите число реплик на узле или включите квантование.
  • Ошибки размерности — сверьте модель и схему коллекции.
  • Долгая загрузка — отправляйте пакеты по 100–500 точек и не ставьте wait=true на каждый запрос.

Чек-лист

  • Размерность и метрика зафиксированы до загрузки данных.
  • Поля фильтрации проиндексированы.
  • Порог score подобран на реальных запросах.
  • Снапшоты вынесены за пределы узла и проверены восстановлением.
  • Память и диск покрывают рост коллекции на полгода.
  • Метрики и алерты на задержку и ошибки настроены.

Итог

Qdrant закрывает задачу поиска по смыслу без сборки собственного индекса: коллекция, HNSW, фильтры и снапшоты доступны через понятный REST API. Ключевые решения принимаются до загрузки — модель эмбеддингов, размерность и метрика, а эксплуатация сводится к памяти, индексам payload и резервным копиям. Начните с одной коллекции и тысячи документов, измерьте качество на реальных запросах, и только потом включайте квантование и распределённый режим.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте