Qdrant - Vector Database
Группировка результатов
RAG без группировки - как поиск в Google, где все первые 10 результатов с одного сайта. searchGroups решает это элегантно: «лучшие 5 документов с лучшими 2 чанками каждый» - один запрос, правильный контекст.
- **RAG системы:** 4 документа × 3 чанка = 12 контекстных фрагментов без дублирования, с метаданными
- **Поиск по новостям:** по 1 статье от каждого издания в топе - diversity в результатах
- **Поиск кандидатов:** по 1-2 вакансии от каждой компании - не доминирует одна организация
Предварительные знания
Проблема: много чанков одного документа
**Стандартный RAG pipeline:** документ разбивается на чанки → каждый чанк индексируется как отдельная точка. При поиске топ-10 результатов может оказаться, что 7 из них - чанки одного документа. Это плохо для пользователя.
**Решения без groupBy:** 1. deduplication на уровне приложения - но тогда limit=100 и вы выбрасываете 90 результатов 2. хранить документы целиком - но теряете precision чанков. **searchGroups** решает это элегантно на уровне Qdrant.
| Подход | Плюсы | Минусы |
|---|---|---|
| Обычный search + dedup в коде | Простая реализация | Высокий limit → лишняя работа, непредсказуемый recall |
| Хранить только документы (не чанки) | Нет дублирования | Плохой recall на длинных документах |
| searchGroups | Встроенная дедупликация, N лучших чанков на документ | Нужен payload индекс для group_by поля |
RAG система возвращает релевантные результаты, но LLM видит один и тот же документ 5 раз из 10. Что это вызывает?
searchGroups API: group_by и group_size
**searchGroups** - специальный метод Qdrant, который группирует результаты по значению payload поля. Возвращает top-N документов, каждый с top-M чанков.
**Payload индекс обязателен!** Поле `group_by` должно быть проиндексировано. Создайте индекс: `qdrant.createPayloadIndex('chunks', { field_name: 'document_id', field_schema: 'keyword' })`. Без индекса - full scan = медленно.
searchGroups с group_by='document_id', group_size=2, limit=5. В коллекции 3 документа, у каждого 10 чанков. Максимум сколько точек вернётся?
lookup_from: обогащение из другой коллекции
**lookup_from** - параметр searchGroups, который автоматически подгружает payload для каждой группы из другой коллекции. Это паттерн «chunk collection + document collection».
**Зачем две коллекции?** Чанки содержат только текст чанка. Полные метаданные документа (title, URL, author, date, tags) хранятся в отдельной документной коллекции. При поиске Qdrant автоматически обогащает группы данными из документной коллекции.
**Коллекция 'documents' без реального вектора:** если documents нужна только для lookup (не для поиска), можно создать её с minimal vector (size: 1, distance: 'Cosine') и использовать только как хранилище метаданных. Это нормальный паттерн.
Зачем хранить метаданные документа (title, URL) в отдельной коллекции 'documents' вместо payload каждого чанка?
RAG паттерн: chunk search + document grouping
**Полный RAG pipeline с searchGroups** - объединяем всё: гибкие чанки, group by документ, lookup метаданных, формирование контекста для LLM.
**group_size рекомендации:** для RAG обычно group_size=2-3 (несколько чанков дают больше контекста чем один). Для UI «один результат на документ» используйте group_size=1. Для дедупликации с максимальным recall - group_size=1 + limit=20.
Использовать обычный search с limit=100 и дедупликацию в коде для экономии на searchGroups
searchGroups делает дедупликацию на уровне Qdrant - эффективнее и с лучшим recall
С limit=100 вы отправляете 100 точек по сети и фильтруете в коде. searchGroups возвращает только нужные группы - меньше трафика, правильный recall. И ещё: с limit=100 верхние позиции всё равно будут захвачены одним документом - вы теряете diversity
Для RAG: group_by='document_id', group_size=1, limit=10 vs group_size=3, limit=4. Что лучше при ограниченном контекстном окне LLM (4096 токенов)?
Ключевые идеи
- **searchGroups** решает проблему дублирования чанков одного документа в результатах
- **group_by** - поле payload для группировки, **group_size** - лучших чанков на группу, **limit** - количество групп
- **with_lookup** - автоматическое обогащение групп данными из другой коллекции
- **Двух-коллекционный паттерн:** chunks (поиск) + documents (метаданные) - нормализация и легкое обновление
- **Payload индекс** для group_by поля обязателен для производительности
- Помните hook? Один запрос, N документов, M чанков - это и есть production RAG
Что дальше
Группировка работает. Следующий шаг - поиск «похожего» без явного query вектора.
- Recommendations API — Поиск похожих документов по примерам
- Гибридный поиск — Добавить hybrid search к группировке
- Фильтрация — Комбинировать фильтры с group by
Вопросы для размышления
- Как определить оптимальный group_size для вашей RAG системы? Какие метрики отслеживать?
- Когда двух-коллекционный паттерн (chunks + documents) оправдан, а когда усложняет без нужды?
- Как searchGroups влияет на score_threshold - нужно ли его пересматривать?
Связанные уроки
- qd-13-filters — Group By часто комбинируется с фильтрами для агрегации
- qd-12-multi-vector — Multi-vector search + group by - частая прод-комбинация
- pg-08-aggregation — SQL GROUP BY - та же семантика агрегации по полю
- pg-11-window — Window functions - похожая идея ранжирования внутри групп
- db-05-sql-basics