Векторный поиск на pgvector: где ломается RAG
На демонстрационном стенде RAG по внутренней базе знаний отвечает за доли секунды: пока корпус — это несколько тысяч фрагментов, pgvector сравнивает вектор запроса с каждым и укладывается в приемлемое время ответа. Предел появляется незаметно. Точный перебор векторов линеен по размеру корпуса, и на сотнях тысяч фрагментов поиск начинает конкурировать за процессор с остальными операциями базы — сначала растёт хвост задержек, потом общий фон.
Второй симптом — расход памяти на промежуточные вычисления. Чтобы отдать верхние совпадения, база держит в сортировке все расстояния, а не только нужную верхушку; при параллельных запросах это вытесняет кэш страниц, и время ответа перестаёт быть предсказуемым. Стоит добавить фильтр по атрибутам — права доступа, дата документа, источник — и план может пойти в обход индекса, даже если тот построен.
Деградация поиска при росте корпуса редко проявляется как явная ошибка. Чаще это тихое расхождение: сегодня в контекст модели попали одни фрагменты, завтра — другие, и ответы плавают от запроса к запросу. Индексация векторов снимает вычислительный потолок, но приносит свои настройки — сколько связей держать в графе, сколько кандидатов просматривать на запрос, — и при неверных значениях полнота выдачи тихо срезается на редких, но важных формулировках.
Отдельная ловушка — смешение двух режимов в одном индексе: часть корпуса обновляется чаще, чем перестраивается граф, и свежие документы выпадают из поиска, хотя лежат в таблице. Поэтому качество здесь измеряют не на одном красивом вопросе, а на наборе с известными правильными ответами — до и после каждой смены параметров. Как это закрывается на практике и в каком порядке, разбираем дальше.
Что даёт RAG-поиск: сроки, точность, отказы
Четыре эффекта мы фиксируем на приёмке, и каждый проверяется на вашем корпусе: время ответа поиска, доля релевантных выдач, ушедший класс отказов и объём ручного разбора результатов. Ниже — как это получается и что именно замеряется, а не декларируется.
Скорость ответа при росте корпуса
Без векторного индекса время ответа растёт вместе с корпусом: каждый запрос сравнивается со всеми фрагментами, поэтому при десятикратном росте документов поиск замедляется в те же разы. Индекс HNSW в pgvector строит граф связей между векторами, и обход идёт по нему, а не по всей таблице — время ответа остаётся почти постоянным, пока объём корпуса растёт в разы. Точность регулируется параметром обхода: чем он шире, тем ближе выдача к полному перебору, и это осознанный выбор, а не скрытая деградация.
Доля релевантных выдач и ручная проверка
Второй замер — доля запросов, где нужный фрагмент попал в первые строки выдачи. Она зависит от того, как документы нарезаны: крупные фрагменты размывают смысл, мелкие теряют контекст, поэтому размер и перекрытие мы подбираем по вашему корпусу и добавляем фильтры по метаданным — дате, типу документа, подразделению. Класс отказов «нашлось не то» уходит предсказуемо: рядом с векторным поиском ставим полнотекстовый и переранжирование, чтобы совпадения по ключевым словам и по смыслу не конкурировали.
Вместе с этим падает объём ручного разбора: аналитик перестаёт проверять каждую выдачу и смотрит только те, где сработал порог низкой уверенности.
Сравнение подходов к векторному поиску на pgvector
Механизм поиска определяет, как быстро растёт база знаний и когда потребуется переезд на другой стек. Ниже — четыре схемы, между которыми реально выбирают при разработке поиска на pgvector внутри PostgreSQL.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Точный перебор | До сотен тысяч векторов, нужна эталонная полнота выдачи | Время ответа растёт линейно: читается вся таблица |
| Приближённый индекс в базе | Миллионы векторов, потеря доли процентов полноты допустима | Нужна настройка параметров и периодическая пересборка индекса |
| Внешнее векторное хранилище | Десятки миллионов векторов, отдельный контур нагрузки | Второй источник данных, рассинхронизация, свой контур отказоустойчивости |
| Гибридная схема с текстовым поиском | Запросы со словами, артикулами, именами | Сложнее ранжирование: две выдачи сводятся в один список |
Приближённый индекс против точного перебора
Точный перебор гарантирует правильный топ выдачи, но на больших объёмах читает всю таблицу, и время ответа растёт линейно. Приближённый поиск по индексу отвечает кратно быстрее, зато может пропустить часть близких векторов. Полноту регулируют параметром поиска: её поднимают до уровня, на котором выдача совпадает с эталоном на тестовом наборе вопросов.
Когда внешнее векторное хранилище избыточно
Отдельное хранилище оправдано, когда объём и нагрузка не помещаются в один контур базы. Если документы и векторы лежат рядом и обновляются вместе, вынос добавляет сетевой обмен, вторую схему доступов и рассинхронизацию при переиндексации. Пока речь о миллионах строк, хватает индекса внутри базы, а гибридный поиск собирается обычным полнотекстовым запросом рядом с векторным.
Этапы внедрения RAG-поиска в продукт
Внедрение поиска по документам редко упирается в выбор базы — упирается в качество данных и предсказуемость обновлений. Маршрут идёт от разбора корпуса и запросов к проектированию хранения, и только потом — к настройке выдачи.
- Разбор корпуса и запросов. Форматы, объём, дубли, частота изменений и то, как сотрудники формулируют вопросы на самом деле.
- Схема хранения векторов. В pgvector проектируем таблицы фрагментов и метаданные для фильтрации по правам доступа и версиям документов.
- Стратегия поиска и разбиения. Размер и перекрытие фрагментов, взвешивание, гибридный режим с полнотекстовым поиском.
- Обновление индекса. Переиндексация при изменении документов, удаление устаревших версий, контроль роста хранилища.
- Проверка качества. Прогон набора контрольных запросов со сверкой с эталонными фрагментами.
- Передача в эксплуатацию. Регламенты, мониторинг, инструкции для команды поддержки.
Разбиение документов и подготовка векторов
Размер фрагмента подбираем под структуру текста: регламенты и таблицы требуют одного подхода, сплошной текст — другого. Перекрытие сохраняет смысл на границах, а метаданные — документ, раздел, версия, права — отсекают лишнее до самого поиска.
Проверка на наборе контрольных запросов
Собираем реальные вопросы с эталонными фрагментами и смотрим, попадает ли нужный источник в первые строки выдачи. Расхождения разбираем и правим разбиением, весами или стратегией поиска.
Что передаём на руки: конфигурация и регламенты
Пакет передачи — не папка с кодом, а комплект, по которому команда сопровождения воспроизводит поиск без нас. Ниже — что именно фиксируем в документации и конфигурации.
Схема хранения и параметры индекса
- Схема таблиц: разбиение на фрагменты, типы полей, связи с источником, политика удаления при отзыве документа.
- Конфигурация векторного индекса в pgvector: тип, размерность векторов, параметры построения и поиска, зафиксированные в файле миграции.
- Описание разбиения текста: размер фрагмента, перекрытие, правила обработки таблиц, заголовков и кода.
- Набор запросов-эталонов с ожидаемой выдачей — по ним проверяют, что после перестроения индекса качество поиска не просело.
Регламенты обновления и доступа
- Регламент обновления корпуса: периодичность и триггеры переиндексации, порядок загрузки новых документов, обработка изменённых и удалённых.
- Политики доступа к документам и фрагментам: фильтры на уровне строк и привязка прав к разделам — поиск не возвращает то, что сотруднику не положено.
- Порядок перестроения индекса: прогон на копии, сверка с эталонными запросами, точка отката.
- Инструкция для дежурной смены: типовые сбои, как отличить проблему загрузки от деградации поиска, кому передавать дальше.
Отдельно — документация по поиску: как формулировать запросы, как читать метрики релевантности и какие правки в корпусе требуют согласования с владельцем данных.
Кейс: поиск по базе знаний на pgvector
Задача пришла от команды поддержки: база знаний на несколько тысяч документов — регламенты, инструкции, разборы инцидентов — росла быстрее, чем её успевали описывать, а поиск по ключевым словам находил страницу только при точном совпадении формулировки.
Как было до внедрения
Специалисты искали ответ глазами по оглавлению: открыть раздел, пролистать смежные документы, вернуться назад. На реальных обращениях один вопрос занимал от двух до трёх минут, а нужный раздел находился примерно в четырёх случаях из десяти — остальное закрывалось вопросом коллеге в чате. Ручной поиск был основным способом работы, и новые сотрудники выходили на нормативную скорость только через несколько недель.
Что изменилось после настройки индекса
Поиск перенесли в PostgreSQL: pgvector хранит векторные представления рядом с текстом документа, поэтому фильтры по отделу, типу и дате применяются в том же запросе, что и сравнение по близости. Индекс настраивали в два прохода — сначала загрузили корпус и замерили базовую полноту выдачи, затем подобрали параметры списков и число сегментов под размер базы, чтобы точность не падала при её росте.
Ответ на вопрос теперь приходит за 2-4 секунды вместо минут, попадание в нужный раздел выросло с 40 до 85 % на том же наборе запросов, а обращений к ручному поиску стало вчетверо меньше. Корпус обновляется ночной переиндексацией: новый регламент попадает в выдачу на следующий день без ручных правок.
Чем pgvector отличается от отдельной векторной базы
pgvector — расширение PostgreSQL, при котором векторы лежат в той же таблице, что и обычные данные. Поиск ближайших соседей идёт одним запросом вместе с фильтром по метаданным — арендатор, права доступа, свежесть документа; вектор и запись первичного документа попадают в одну транзакцию, а резервная копия остаётся одна.
Отдельное векторное хранилище берут под другую задачу: когда поисковый индекс не помещается в память вместе с рабочими данными и нужен поиск по распределённому индексу. Оно даёт предсказуемую задержку на очень больших коллекциях, но переносит сложность в интеграцию — появляется второй источник правды.
Основная цена — синхронизация данных и векторов. Строку и её вектор связывают фоновой задачей или потоком изменений, и между записью и индексацией возникает окно, когда документ уже обновлён, а поиск отдаёт старый фрагмент. Отдельно решаются удаление — иначе копятся осиротевшие чанки — и полная переиндексация при смене модели эмбеддингов.
Дальше — точки отказа: к базе добавляются сервис, его доступы, его бэкап и его схема. Отказ базы и так остановил бы приложение, а теперь ломаться могут два независимых контура. Поэтому выбор делают по порогу объёма: пока корпус и индекс укладываются в память узла и поиск не конкурирует с рабочими запросами, одна база проще — один бэкап, одна транзакция, никакой синхронизации. Разделять стоит там, где индекс перестаёт помещаться или поиск начинает мешать основной работе с данными.
Технические сомнения: ограничения и деградация
Пока корпус — десятки тысяч фрагментов, поиск в pgvector отвечает быстро и предсказуемо. На сотнях тысяч те же запросы идут заметно дольше, а выдача на перефразированных вопросах расходится: один и тот же смысл, заданный другими словами, приводит к разным документам. Это следствие того, что индекс строился на другом объёме и структуре данных, чем лежит в таблице сейчас, — и отдельно стоит вопрос работы поверх уже развёрнутой системы.
Как понять, что индекс пора перестраивать?
Признаки: время отклика растёт при неизменном числе строк, доля релевантных ответов в верхушке выдачи падает, после массовых загрузок и удалений появляется мусорная периферия. Индекс, построенный на неполном корпусе, теряет точность по мере дозагрузки, поэтому пересобираем его, когда основная масса документов на месте, — в отдельной ветке, с проверкой на контрольных вопросах и последующим переключением.
Как изолировать данные разных заказчиков в одной базе?
Разделяем на уровне схемы и строк: отдельный раздел на заказчика плюс обязательный фильтр по его идентификатору, применяемый до этапа ранжирования. Добавляем политики доступа на уровне строк в PostgreSQL и контроль на стороне сервиса, который формирует запрос. Проверка — негативным тестом: запрос от имени одного заказчика не находит ни одного чужого фрагмента.
Почему выдача нестабильна на похожих формулировках?
Векторный поиск плохо различает близкие по смыслу запросы, когда в корпусе много однотипных документов. Помогает совмещение с полнотекстовым поиском, уточняющий этап ранжирования, порог отсечения по близости и нормализация запроса. Сохранённые результаты частых формулировок снимают часть нагрузки на базу.
Не просядет ли база при работе поверх готовой системы?
Поиск читает данные отдельно от основной нагрузки: запросы идут на реплику, тяжёлое перестроение — в окно обслуживания. Схему с векторами добавляем рядом, наполнение идёт фоном, поиск включаем постепенно. Откат — отключение слоя поиска: остальная система продолжает работать как раньше.
Обсудим задачу: что подготовить к разговору
Чтобы оценка была предметной, а не «за векторный поиск в целом», к разговору достаточно нескольких вводных. Часть уточним в переписке, остальное разберём на встрече.
- Постановка задачи поиска: какие решения человек принимает по выдаче — находит конкретный пункт регламента, сравнивает условия или просит синтез по нескольким документам сразу. От этого зависит, нужен ли только поиск по pgvector или слой генерации поверх него.
- Примеры пользовательских запросов — 10–20 реальных формулировок, включая короткие, с опечатками и на двух языках. По ним настраиваем разбиение на фрагменты и понимаем, где дополнительно нужен поиск по ключевым словам.
- Объём и типы документов: сколько файлов, какие форматы, как быстро растёт корпус. Это определяет выбор индекса в pgvector и порядок переиндексации без остановки сервиса.
- Требования к свежести данных: допустимое отставание между публикацией документа и появлением его в выдаче. Если счёт идёт на минуты, закладываем потоковую загрузку и очередь обновления векторов.
- Схема доступа к информации: кто какие документы видит. Права разграничиваем фильтрами на уровне строк в PostgreSQL, чтобы они применялись до отбора ближайших векторов, а не после.
- Текущая нагрузка и поведение при сбое: сколько запросов в секунду в пике, какое время ответа приемлемо и что сервис делает при недоступности базы — отдаёт кэш или честно сообщает об ошибке.
Опишите задачу в свободной форме — вернёмся с оценкой и планом работ по этапам.
| Подход | Основа | Когда уместен | Что даёт | Ограничения | Сроки внедрения |
|---|---|---|---|---|---|
| Полный перебор по векторам | Векторы лежат в обычных таблицах, приближённый поиск не используется | База знаний до нескольких десятков тысяч фрагментов, важна эталонная точность | Ни один фрагмент не теряется, выдача предсказуема | Время ответа растёт вместе с объёмом базы; на больших массивах скорость падает | 1–2 недели |
| Приближённый векторный индекс в PostgreSQL | Векторный индекс внутри pgvector, данные в одной базе с основным продуктом | Основной сценарий: поиск по документам и базе знаний там, где база уже работает | Точность близка к эталонной при кратном ускорении ответа; один контур данных, права и резервные копии как у обычных таблиц | Нужна настройка параметров индекса и повторная сборка при массовой загрузке | 2–4 недели |
| Гибрид: полнотекстовый поиск + векторный | Совместный поиск по словам и по смыслу, результаты объединяются | Смешанные запросы: точные термины, артикулы, названия и вопросы «своими словами» | Точные совпадения не теряются, смысловые находятся; меньше пустых ответов | Требуется настройка весов и порядка объединения результатов | 3–5 недель |
| Переранжирование найденного | Модель переранжирования поверх первичной выдачи | Нужна высокая точность верхних позиций ответа | Доля релевантных фрагментов в начале выдачи заметно выше | Дополнительный шаг обработки удлиняет ответ | 2–4 недели |
| Отдельное хранилище векторов | Специализированная векторная база рядом с основной | Объёмы в десятки миллионов фрагментов, под сервис выделена отдельная команда | Высокая скорость на больших объёмах, готовое масштабирование | Второй контур данных: синхронизация, резервные копии, доступы, отдельная эксплуатация | 6–10 недель |
| Готовый внешний сервис поиска | Внешняя платформа, данные уходят за пределы контура | Быстрый запуск, если нет требований к хранению данных внутри | Минимум своей эксплуатации, запуск в короткий срок | Зависимость от чужого контура, ограничения по чувствительным данным, сложнее менять логику поиска | 1–3 недели |
- Считают, что задачу решает сама загрузка документов: если файлы не разбиты на смысловые фрагменты, поиск возвращает обрывки в середине мысли и ответ получается бессвязным.
- Проверяют качество на двух-трёх вопросах, которые сами же и придумали. Нужен набор контрольных запросов с заранее известными верными ответами — иначе после любой правки непонятно, стало лучше или хуже.
- Сваливают в одно хранилище документы разного возраста и назначения: действующие регламенты, черновики, переписку, отменённые приказы. Выдача засоряется устаревшим, и пользователь перестаёт доверять системе.
- Не разделяют доступ: в общий набор попадают материалы разных отделов и заказчиков, и поиск начинает показывать чужие данные тому, кому они не предназначены.
- Меняют способ построения векторов, но не пересчитывают уже загруженные записи. Старые и новые документы оказываются в разных системах координат, и поиск по ним перестаёт работать.
- Ждут, что система сама поймёт короткий разговорный вопрос. Без обработки запроса пользователя — уточнения формулировки, расширения сокращений, отсечения лишних слов — выдача приходит пустой или не по теме.
- Оставляют только поиск и убирают проверку найденного. Без повторной сортировки и порога по теме в ответ попадают материалы с формальным совпадением слов, но противоположным смыслом.
- Загружают весь архив «на всякий случай» и ничего не удаляют. Объём растёт, а ответы начинают противоречить друг другу, потому что в выдачу попадают взаимоисключающие версии одного документа.







