Qdrant - Vector Database
Первый поиск: Search API
Вы проиндексировали данные. Теперь самое интересное - поиск за 10-15ms по миллионам векторов. Но поиск без правильных параметров может давать нерелевантные результаты. Разберём как сделать его точным.
- **RAG (Retrieval-Augmented Generation):** поиск контекста для LLM - score_threshold 0.75 отсекает мусор
- **Семантический поиск:** аналог Elasticsearch, но по смыслу - limit: 10, score_threshold: 0.7
- **Рекомендации:** 'похожие статьи' - поиск по вектору существующего документа
Предварительные знания
Search API: базовый запрос
**Поиск в Qdrant** - найти K ближайших точек к query-вектору. Query-вектор - это embedding вашего запроса (текст, изображение, что угодно). Qdrant возвращает точки, отсортированные по убыванию score (сходства).
**Score** - мера сходства, зависит от выбранной метрики. Для Cosine: от -1 до 1, где 1 = идентичные векторы. На практике похожие документы: 0.85-0.99, несвязанные: < 0.5.
**Используйте ту же embedding-модель** для индексации и поиска. Если документы проиндексированы через `text-embedding-3-small`, запрос тоже должен идти через неё. Смешивать модели нельзя - пространства несовместимы.
Вы проиндексировали документы через OpenAI text-embedding-3-small (1536d). Для поиска хотите использовать более дешёвую модель ada-002 (тоже 1536d). Это сработает?
Метрики расстояния: Cosine, Dot, Euclid
**Метрика расстояния** определяет как вычисляется сходство двух векторов. Выбирается при создании коллекции и не меняется. Разные модели embeddings оптимизированы под разные метрики.
| Метрика | Формула | Диапазон score | Когда использовать |
|---|---|---|---|
| Cosine | cos(a,b) = a·b / (|a|·|b|) | [-1, 1], выше = лучше | Текстовые embeddings (OpenAI, Cohere, Sentence Transformers) |
| Dot Product | a·b = Σ(aᵢ × bᵢ) | [-∞, +∞], выше = лучше | Нормализованные векторы, ColBERT, SPLADE |
| Euclid | √Σ(aᵢ - bᵢ)² | [0, +∞], ниже = лучше | Координаты, физические измерения, специальные модели |
| Manhattan | Σ|aᵢ - bᵢ| | [0, +∞], ниже = лучше | Редко используется в NLP, спецзадачи |
**Cosine vs Dot Product:** если векторы нормализованы (|v|=1), они дают одинаковый ранкинг. OpenAI embeddings нормализованы, так что оба работают. Cosine - более интуитивный (угол), Dot Product - чуть быстрее вычисляется (нет деления на нормы).
Вы используете OpenAI text-embedding-3-small. Документация говорит что embeddings нормализованы (|v|=1). Какие метрики дадут одинаковый ранкинг?
Параметры поиска: limit, threshold, hnsw_ef
**Параметры поиска** позволяют балансировать между точностью, скоростью и количеством результатов.
**`score_threshold`** - важный параметр для production. Без него Qdrant вернёт top-K даже если они совсем не похожи на запрос. С threshold - только реально релевантные результаты.
**Как подобрать score_threshold:** сделайте 20-30 тестовых запросов, посмотрите на score у правильных и неправильных результатов. Граница между ними - ваш threshold. Обычно он стабилен для конкретной модели + домена данных.
В RAG-приложении поиск всегда возвращает 5 результатов, даже на нерелевантные запросы ('какой сегодня день?'). Как это исправить?
Результаты: payload, векторы и пагинация
**Управление результатами** - что возвращается в ответе, как делать пагинацию и искать по конкретным полям payload.
**`with_payload: {include: ['field1', 'field2']}`** - рекомендуется в production. Большой payload увеличивает размер ответа. Если нужен только `title` и `url` - не запрашивайте всё остальное.
Большой offset для пагинации: `offset: 1000, limit: 10`
Для пагинации в vector search нет эффективного решения с большим offset. Лучше возвращать больше результатов (top-50) и пагинировать на стороне клиента
При offset=1000 Qdrant вычисляет top-1010 и возвращает последние 10 - это O(offset × log N). Векторный поиск не предназначен для deep pagination. Альтернатива: scroll() для обхода коллекции, или ограничить пагинацию до 2-3 страниц
Вам нужно делать 10 семантических поисков параллельно в одном запросе. Какой метод использовать?
Ключевые идеи
- **search(collection, {vector, limit})** - базовый API. Query-вектор из той же модели, что и при индексации
- **Score** - мера сходства. Для Cosine: 1=идентично, >0.8=очень похоже, <0.5=нерелевантно
- **score_threshold** - обязателен в production. Без него поиск вернёт K результатов даже для нерелевантного запроса
- **Метрики:** Cosine/Dot для текстовых embeddings, Euclid для координат. Ориентир - документация модели
- **hnsw_ef** - точность vs скорость. Default (64) - хороший баланс. `exact: true` только для тестов
- Помните hook? 10-15ms при миллионах векторов - это HNSW + правильные параметры
Что дальше
Базовый поиск работает. Следующий уровень - понять как HNSW делает это за миллисекунды.
- HNSW - как работает индекс — Понять m, ef_construct, ef - настроить качество поиска
- Фильтрация по payload — Фильтры must/should/must_not - найти документы с нужными метаданными
- Hybrid Search — Сочетание семантического и keyword поиска для лучших результатов
Вопросы для размышления
- Как выбрать score_threshold для своего домена данных? Какой эксперимент провести?
- Если поиск возвращает 0 результатов с threshold 0.8, но 10 с threshold 0.6 - что это говорит о качестве embeddings или данных?
- Чем семантический поиск Qdrant лучше Elasticsearch full-text search? А хуже?