Векторные БД для корпоративного поиска: pgvector против выделенных решений
Строим RAG-систему и упираемся в вопрос хранения эмбеддингов. Сравниваем pgvector, Chroma и Weaviate - и почему для старта выбрали расширение PostgreSQL.
Векторные базы данных (Chroma, Weaviate, pgvector) становятся инфраструктурным компонентом LLM-систем
После того как мы собрали первый RAG-прототип с LangChain и Chroma для поиска по корпоративным регламентам, заказчик задал логичный вопрос: «А это будет работать на нормальном объёме?». Chroma в режиме embedded - это удобно для прототипа, но не для продакшна с несколькими источниками данных и возможным ростом корпуса. Пришлось садиться и разбираться с тем, что вообще есть на рынке векторных хранилищ и как это соотносится с нашей инфраструктурой.
Спойлер: выбрали pgvector. Не потому что он лучший по всем параметрам, а потому что оказался достаточным - и это важное различие.
Зачем вообще нужна отдельная БД для векторов
Чтобы не тратить время читателя: эмбеддинги - это числовые векторы в нескольких сотнях или тысячах измерений, которые кодируют смысл текста. При поиске мы ищем не по ключевым словам, а по близости векторов в многомерном пространстве. Обычная реляционная база для этого не приспособлена: посчитать косинусное расстояние между двумя массивами из 1536 float-чисел для тысяч записей в честном SQL - это либо медленно, либо не работает без специального индекса.
Отсюда и появились специализированные векторные БД.
Что смотрели
На момент нашего разбора основные кандидаты выглядели так:
Chroma - то, с чего мы начали. Embedded-режим разворачивается в Python-процессе без сервера, персистит на диск. Есть и клиент-серверный режим. Активно развивается, API ещё не устоялся, документация за кодом не успевает. Для прототипа - отлично, для продакшна нужно внимательно смотреть.
Weaviate - выделенный сервис с GraphQL API, поддержкой мультимодальных данных и встроенными модулями для генерации эмбеддингов прямо внутри БД. Серьёзный продукт с богатыми возможностями. Разворачивается через Docker, есть Kubernetes Helm-чарт. Если нужен полноценный векторный магазин с гибкими схемами данных - это сильный вариант. Цена: отдельный сервис в инфраструктуре, который надо администрировать, мониторить и бэкапить.
Qdrant - ещё один выделенный сервис, написан на Rust, позиционируется как высокопроизводительный. Более свежий проект, чем Weaviate, но API понятный, документация приличная. Тоже требует отдельного деплоя.
pgvector - расширение для PostgreSQL. Добавляет тип vector(N) и индекс IVFFlat для приближённого поиска. Запросы пишутся на обычном SQL через операторы близости (<-> для L2, <=> для косинусного расстояния). Актуальная версия на момент написания - 0.4.0 (январь 2023).
Почему остановились на pgvector
Контекст проекта: у клиента уже стоит PostgreSQL 15 в продакшне, настроен нормально, есть резервирование, мониторинг, бэкапы. Команда умеет с ним работать.
Добавление pgvector - это CREATE EXTENSION vector; и несколько минут. Новый компонент в инфраструктуре не появляется. Бэкап векторов едет вместе с остальными данными в pg_dump или в потоковую репликацию. Это чертовски важно, если команда не большая и администрировать «ещё одну базу данных» особо некому.
Что проверяли практически:
- Индекс IVFFlat строится с параметром
lists- количество списков кластеров. Для корпуса в несколько десятков тысяч документов с размерностью 1536 (ada-002) время ответа получается приемлемым. Не мгновенным, но приемлемым - поиск top-5 в пределах секунды. - Точность поиска в сравнении с Chroma на том же корпусе оказалась практически одинаковой на нашей тестовой выборке вопросов. ANN-алгоритмы обоих решений дают похожие результаты при умеренных размерах базы.
- SQL как язык запросов - это неожиданный плюс. Можно фильтровать по метаданным прямо в одном запросе: поиск ближайших векторов среди документов определённого отдела, с датой не раньше заданной - это обычный WHERE, без изучения чужого API.
Где pgvector проигрывает: при очень большом корпусе (миллионы векторов) и требованиях к латентности в десятки миллисекунд выделенные решения начинают выигрывать. У Weaviate и Qdrant HNSW-индексы встроены глубже, тюнинг под производительность богаче. Но это не наш случай прямо сейчас.
Практика хранения: что важно
Независимо от выбора векторной БД, мы зафиксировали несколько вещей, которые горели на прототипе:
- Метаданные хранить рядом с вектором, обязательно. Источник документа, дата, раздел - без этого ответ системы не верифицируем. В pgvector это просто колонки той же таблицы.
- Document ID и переиндексация. При обновлении документа нужно удалить старые векторы и добавить новые. Инкрементальное обновление требует стабильных идентификаторов чанков - если генерировать их на лету без привязки к исходному контенту, при переиндексации получим дубли.
- Размер чанка влияет на качество поиска сильнее, чем выбор БД. Это неочевидный вывод, но он честный: правильная стратегия разбивки документов даёт больший прирост качества ответов, чем переход с одного хранилища на другое.
Где сейчас
Архитектуру с pgvector развернули на тестовом стенде клиента. Postgres-инстанс тот же, расширение установлено, таблица document_chunks с колонкой embedding vector(1536) и метаданными работает. LangChain подключается через PGVector-интегратор - он есть в составе langchain, без дополнительных зависимостей.
Следующий шаг - нагрузочный тест на реальном объёме документов и решение про инкрементальное обновление индекса. Если корпус вырастет кратно - будем смотреть на Weaviate отдельно. Пока оснований переезжать нет.
Задача по аналитической инфраструктуре и работе с данными часто упирается именно в такие выборы: не «какой инструмент лучший», а «какой инструмент достаточен при нашем операционном контексте».