Qdrant - Vector Database

Qdrant + NestJS

У вас есть RAG pipeline. Теперь нужно обернуть его в production NestJS сервис: принимать документы через REST API, асинхронно индексировать через BullMQ, отвечать на вопросы. Это финальная точка курса - соберём всё вместе.

  • **Document management system:** POST /documents → BullMQ → chunking + embeddings + Qdrant; GET /search → semantic search → LLM answer
  • **Knowledge base API:** мультитенантный поиск через JWT tenant_id extraction → TenantScopedRepository → QdrantService
  • **AI assistant backend:** NestJS + Qdrant + OpenAI + BullMQ — полный production stack для RAG приложения

Предварительные знания

  • Production RAG Pipeline
  • Multitenancy in Qdrant

QdrantModule и QdrantService: NestJS паттерн

**NestJS** - идеальная среда для production Qdrant сервисов: DI, ConfigModule, модульность. Паттерн: кастомный `QdrantModule` с `forRoot()` (глобальная конфигурация) + `QdrantService` (обёртка над клиентом). Это то же самое что TypeORM, Redis и другие NestJS интеграции.

**`@Global()` + `forRoot()`** - паттерн из NestJS экосистемы (TypeOrmModule, BullModule). `@Global()` означает что QdrantService доступен во всём приложении без повторного импорта QdrantModule в каждый feature module. Один инстанс клиента = один пул соединений.

Вы добавили QdrantModule.forRoot() в AppModule. Теперь нужен QdrantService в SearchModule. Что нужно сделать?

REST API: endpoint для векторного поиска

**Полный пример:** принимаем документ через HTTP → возвращаем семантически похожие. Включает: DTO с валидацией, embeddings через OpenAI, поиск в Qdrant с фильтрацией, форматирование ответа.

**`score_threshold: 0.7`** - важный параметр. Без него Qdrant вернёт топ-N результатов даже если они нерелевантны. Для текстового поиска с text-embedding-3-small: 0.65-0.75 - хороший диапазон. Замерьте на своих данных: слишком высокий порог = пропущенные релевантные результаты.

SearchService делает OpenAI API вызов на каждый запрос. Как оптимизировать для частых повторяющихся запросов?

BullMQ: асинхронная индексация документов

**Синхронная индексация в HTTP запросе** - антипаттерн: пользователь ждёт генерации embeddings + записи в Qdrant (1-10 секунд). Решение: BullMQ очередь для асинхронной индексации. Эндпоинт принимает документ и сразу отвечает 202 Accepted. Processor обрабатывает в фоне.

«BullMQ только для тяжёлых задач - для индексации 1 документа достаточно синхронного await»

Индексация документа = 1-10 секунд (OpenAI API + Qdrant write). HTTP таймаут, плохой UX, невозможность retry. BullMQ - правильный выбор даже для «одного документа».

OpenAI embeddings API: p50 ~200ms, p99 ~2s. При 50 чанках × 200ms = 10 секунд minimum. За это время: HTTP может таймаутнуть, пользователь уйдёт, retry при ошибке сложен. BullMQ: immediate 202 response + retry logic + visibility в Bull Board dashboard.

IndexingProcessor падает с ошибкой OpenAI API (rate limit) на шаге генерации embeddings. Job настроен с attempts: 3. Что произойдёт?

Ключевые идеи

  • **QdrantModule.forRoot()** — глобальный модуль с DI, @Global() + ConfigModule интеграция
  • **QdrantService** — обёртка над @qdrant/js-client-rest: getClient() для нативного доступа, helper методы для частых операций
  • **SearchService** — embedding генерация + поиск с фильтрацией, score_threshold для отсечения нерелевантных результатов
  • **IndexingProcessor extends WorkerHost** — BullMQ processor: chunking → batch embeddings → upsert; attempts + backoff для надёжности
  • **Полный flow:** POST /documents → 202 Accepted → BullMQ job → IndexingProcessor → Qdrant; POST /search → embedding → search → результаты

Что дальше

Вы прошли полный курс по Qdrant: от первого поиска до production NestJS интеграции с BullMQ, мультитенантностью и RAG pipeline.

  • Production RAG Pipeline — Детали pipeline который лежит в основе IndexingProcessor и SearchService
  • Мультитенантность — Добавить tenant isolation в QdrantService используя custom sharding
  • Qdrant + LangChain — Альтернатива нативному QdrantService - использовать QdrantVectorStore из LangChain в NestJS

Вопросы для размышления

  • IndexingProcessor использует OpenAI для embeddings. Как написать unit-тест для handleIndexDocument без реальных API вызовов? Как замокать QdrantService и OpenAI?
  • В production: пользователь загрузил документ (202 Accepted), но хочет знать когда индексация завершилась. Как реализовать нотификацию? (WebSocket, SSE, webhook, polling)
  • QdrantModule использует один @Global() сервис. Как адаптировать для мультитенантности — чтобы каждый запрос автоматически использовал правильный tenant context без явной передачи tenantId в каждый метод?

Связанные уроки

  • ml-01-intro
Qdrant + NestJS

0

1

Войти