Розробка RAG з pgvector: векторний пошук у PostgreSQL

База знань зростає, а пошук по ній відповідає все повільніше й неточніше — знайома картина. Ми розробляємо RAG-пошук на pgvector під ключ: збираємо конвеєр обробки документів, налаштовуємо векторний індекс і видачу відповідей із посиланнями на джерело. Скорочуємо час пошуку потрібного фрагмента в рази й прибираємо вигадані відповіді.
Часті запитання

Векторний пошук на pgvector: де ламається RAG

На демонстраційному стенді RAG по внутрішній базі знань відповідає за частки секунди: поки корпус — це кілька тисяч фрагментів, pgvector порівнює вектор запиту з кожним і вкладається в прийнятний час відповіді. Межа з'являється непомітно.

Точний перебір векторів лінійний за розміром корпусу, і на сотнях тисяч фрагментів пошук починає конкурувати за процесор з іншими операціями бази — спочатку зростає хвіст затримок, потім загальний фон.

Другий симптом — витрати пам'яті на проміжні обчислення. Щоб віддати верхні збіги, база тримає в сортуванні всі відстані, а не лише потрібну верхівку; за паралельних запитів це витісняє кеш сторінок, і час відповіді перестає бути передбачуваним.

Варто додати фільтр за атрибутами — права доступу, дата документа, джерело — і план може піти в обхід індексу, навіть якщо той побудований.

Деградація пошуку при зростанні корпусу рідко проявляється як явна помилка. Частіше це тихе розходження: сьогодні в контекст моделі потрапили одні фрагменти, завтра — інші, і відповіді плавають від запиту до запиту.

Індексація векторів знімає обчислювальну стелю, але приносить свої налаштування — скільки зв'язків тримати в графі, скільки кандидатів переглядати на запит, — і за неправильних значень повнота видачі тихо зрізається на рідкісних, але важливих формулюваннях.

Окрема пастка — змішування двох режимів в одному індексі: частина корпусу оновлюється частіше, ніж перебудовується граф, і свіжі документи випадають з пошуку, хоча лежать у таблиці.

Тому якість тут вимірюють не на одному гарному питанні, а на наборі з відомими правильними відповідями — до і після кожної зміни параметрів. Як це закривається на практиці і в якому порядку, розбираємо далі.

Що дає RAG-пошук: терміни, точність, відмови

Чотири ефекти ми фіксуємо на прийманні, і кожен перевіряється на вашому корпусі: час відповіді пошуку, частка релевантних видач, зниклий клас відмов і обсяг ручного розбору результатів. Нижче — як це виходить і що саме замірюється, а не декларується.

Швидкість відповіді при зростанні корпусу

Без векторного індексу час відповіді зростає разом із корпусом: кожен запит порівнюється з усіма фрагментами, тому за десятикратного зростання документів пошук сповільнюється в ті самі рази.

Індекс HNSW у pgvector будує граф зв'язків між векторами, і обхід іде по ньому, а не по всій таблиці — час відповіді залишається майже сталим, поки обсяг корпусу зростає в рази.

Точність регулюється параметром обходу: чим він ширший, тим ближча видача до повного перебору, і це усвідомлений вибір, а не прихована деградація.

Частка релевантних видач і ручна перевірка

Другий замір — частка запитів, де потрібний фрагмент потрапив у перші рядки видачі.

Вона залежить від того, як документи нарізані: великі фрагменти розмивають сенс, дрібні втрачають контекст, тому розмір і перекриття ми підбираємо за вашим корпусом і додаємо фільтри за метаданими — датою, типом документа, підрозділом.

Клас відмов «знайшлося не те» зникає передбачувано: поряд із векторним пошуком ставимо повнотекстовий і переранжування, щоб збіги за ключовими словами й за сенсом не конкурували.

Разом із цим падає обсяг ручного розбору: аналітик перестає перевіряти кожну видачу й дивиться лише ті, де спрацював поріг низької впевненості.

Порівняння підходів до векторного пошуку на pgvector

Механізм пошуку визначає, як швидко зростає база знань і коли знадобиться переїзд на інший стек. Нижче — чотири схеми, між якими реально обирають під час розробки пошуку на pgvector усередині PostgreSQL.

Варіант Коли підходить Обмеження
Точний перебір До сотень тисяч векторів, потрібна еталонна повнота видачі Час відповіді зростає лінійно: читається вся таблиця
Наближений індекс у базі Мільйони векторів, втрата частки відсотків повноти допустима Потрібне налаштування параметрів і періодичне перезбирання індексу
Зовнішнє векторне сховище Десятки мільйонів векторів, окремий контур навантаження Друге джерело даних, розсинхронізація, власний контур відмовостійкості
Гібридна схема з текстовим пошуком Запити зі словами, артикулами, іменами Складніше ранжування: дві видачі зводяться в один список

Наближений індекс проти точного перебору

Точний перебір гарантує правильний топ видачі, але на великих обсягах читає всю таблицю, і час відповіді зростає лінійно. Наближений пошук за індексом відповідає кратно швидше, зате може пропустити частину близьких векторів.

Повноту регулюють параметром пошуку: її піднімають до рівня, на якому видача збігається з еталоном на тестовому наборі питань.

Коли зовнішнє векторне сховище надмірне

Окреме сховище виправдане, коли обсяг і навантаження не вміщаються в один контур бази. Якщо документи й вектори лежать поруч і оновлюються разом, винесення додає мережевий обмін, другу схему доступів і розсинхронізацію під час переіндексації.

Поки йдеться про мільйони рядків, достатньо індексу всередині бази, а гібридний пошук збирається звичайним повнотекстовим запитом поруч із векторним.

Етапи впровадження RAG-пошуку в продукт

Впровадження пошуку за документами рідко впирається у вибір бази — впирається в якість даних і передбачуваність оновлень. Маршрут іде від розбору корпусу й запитів до проєктування зберігання, і лише потім — до налаштування видачі.

  1. Розбір корпусу й запитів. Формати, обсяг, дублі, частота змін і те, як співробітники формулюють питання насправді.
  2. Схема зберігання векторів. У pgvector проєктуємо таблиці фрагментів і метадані для фільтрації за правами доступу та версіями документів.
  3. Стратегія пошуку й розбиття. Розмір і перекриття фрагментів, зважування, гібридний режим із повнотекстовим пошуком.
  4. Оновлення індексу. Переіндексація при зміні документів, видалення застарілих версій, контроль зростання сховища.
  5. Перевірка якості. Прогін набору контрольних запитів зі звіркою з еталонними фрагментами.
  6. Передача в експлуатацію. Регламенти, моніторинг, інструкції для команди підтримки.

Розбиття документів і підготовка векторів

Розмір фрагмента підбираємо під структуру тексту: регламенти й таблиці потребують одного підходу, суцільний текст — іншого. Перекриття зберігає сенс на межах, а метадані — документ, розділ, версія, права — відсікають зайве до самого пошуку.

Перевірка на наборі контрольних запитів

Збираємо реальні питання з еталонними фрагментами й дивимося, чи потрапляє потрібне джерело в перші рядки видачі. Розходження розбираємо й правимо розбиттям, вагами або стратегією пошуку.

Що передаємо на руки: конфігурація й регламенти

Пакет передачі — не тека з кодом, а комплект, за яким команда супроводу відтворює пошук без нас. Нижче — що саме фіксуємо в документації та конфігурації.

Схема зберігання й параметри індексу

  • Схема таблиць: розбиття на фрагменти, типи полів, зв'язки з джерелом, політика видалення при відкликанні документа.
  • Конфігурація векторного індексу в pgvector: тип, розмірність векторів, параметри побудови й пошуку, зафіксовані у файлі міграції.
  • Опис розбиття тексту: розмір фрагмента, перекриття, правила обробки таблиць, заголовків і коду.
  • Набір запитів-еталонів з очікуваною видачею — за ними перевіряють, що після перебудування індексу якість пошуку не знизилася.

Регламенти оновлення й доступу

  • Регламент оновлення корпусу: періодичність і тригери переіндексації, порядок завантаження нових документів, обробка змінених і видалених.
  • Політики доступу до документів і фрагментів: фільтри на рівні рядків і прив'язка прав до розділів — пошук не повертає те, що співробітнику не належить.
  • Порядок перебудування індексу: прогін на копії, звірка з еталонними запитами, точка відкату.
  • Інструкція для чергової зміни: типові збої, як відрізнити проблему завантаження від деградації пошуку, кому передавати далі.

Окремо — документація з пошуку: як формулювати запити, як читати метрики релевантності та які правки в корпусі потребують погодження з власником даних.

Кейс: пошук за базою знань на pgvector

Задача надійшла від команди підтримки: база знань на кілька тисяч документів — регламенти, інструкції, розбори інцидентів — зростала швидше, ніж її встигали описувати, а пошук за ключовими словами знаходив сторінку лише за точного збігу формулювання.

Як було до впровадження

Фахівці шукали відповідь очима за змістом: відкрити розділ, переглянути суміжні документи, повернутися назад.

На реальних зверненнях одне питання займало від двох до трьох хвилин, а потрібний розділ знаходився приблизно в чотирьох випадках із десяти — решту закривали питанням колезі в чаті.

Ручний пошук був основним способом роботи, і нові співробітники виходили на нормативну швидкість лише через кілька тижнів.

Що змінилося після налаштування індексу

Пошук перенесли в PostgreSQL: pgvector зберігає векторні представлення поруч із текстом документа, тому фільтри за відділом, типом і датою застосовуються в тому самому запиті, що й порівняння за близькістю.

Індекс налаштовували у два проходи — спочатку завантажили корпус і заміряли базову повноту видачі, потім підібрали параметри списків і кількість сегментів під розмір бази, щоб точність не падала при її зростанні.

Відповідь на питання тепер надходить за 2-4 секунди замість хвилин, влучання в потрібний розділ зросло з 40 до 85 % на тому самому наборі запитів, а звернень до ручного пошуку стало вчетверо менше.

Корпус оновлюється нічною переіндексацією: новий регламент потрапляє у видачу наступного дня без ручних правок.

Чим pgvector відрізняється від окремої векторної бази

pgvector — розширення PostgreSQL, за якого вектори лежать у тій самій таблиці, що й звичайні дані.

Пошук найближчих сусідів іде одним запитом разом із фільтром за метаданими — орендар, права доступу, свіжість документа; вектор і запис первинного документа потрапляють в одну транзакцію, а резервна копія залишається одна.

Окреме векторне сховище беруть під іншу задачу: коли пошуковий індекс не вміщається в пам'ять разом із робочими даними й потрібен пошук за розподіленим індексом.

Воно дає передбачувану затримку на дуже великих колекціях, але переносить складність в інтеграцію — з'являється друге джерело правди.

Основна ціна — синхронізація даних і векторів. Рядок і його вектор пов'язують фоновою задачею або потоком змін, і між записом та індексацією виникає вікно, коли документ уже оновлено, а пошук віддає старий фрагмент.

Окремо вирішуються видалення — інакше накопичуються осиротілі чанки — і повна переіндексація при зміні моделі ембедингів.

Далі — точки відмови: до бази додаються сервіс, його доступи, його бекап і його схема. Відмова бази й так зупинила б застосунок, а тепер ламатися можуть два незалежні контури.

Тому вибір роблять за порогом обсягу: поки корпус та індекс вкладаються в пам'ять вузла й пошук не конкурує з робочими запитами, одна база простіша — один бекап, одна транзакція, жодної синхронізації.

Розділяти варто там, де індекс перестає вміщатися або пошук починає заважати основній роботі з даними.

Технічні сумніви: обмеження й деградація

Поки корпус — десятки тисяч фрагментів, пошук у pgvector відповідає швидко й передбачувано. На сотнях тисяч ті самі запити йдуть помітно довше, а видача на перефразованих питаннях розходиться: один і той самий сенс, заданий іншими словами, приводить до різних документів.

Це наслідок того, що індекс будувався на іншому обсязі та структурі даних, ніж лежить у таблиці зараз, — і окремо стоїть питання роботи поверх уже розгорнутої системи.

Як зрозуміти, що індекс пора перебудовувати?

Ознаки: час відгуку зростає за незмінної кількості рядків, частка релевантних відповідей у верхівці видачі падає, після масових завантажень і видалень з'являється сміттєва периферія.

Індекс, побудований на неповному корпусі, втрачає точність у міру довантаження, тому перезбираємо його, коли основна маса документів на місці, — в окремій гілці, з перевіркою на контрольних питаннях і подальшим перемиканням.

Як ізолювати дані різних замовників в одній базі?

Розділяємо на рівні схеми та рядків: окремий розділ на замовника плюс обов'язковий фільтр за його ідентифікатором, застосовуваний до етапу ранжування. Додаємо політики доступу на рівні рядків у PostgreSQL і контроль на боці сервісу, який формує запит.

Перевірка — негативним тестом: запит від імені одного замовника не знаходить жодного чужого фрагмента.

Чому видача нестабільна на схожих формулюваннях?

Векторний пошук погано розрізняє близькі за сенсом запити, коли в корпусі багато однотипних документів. Допомагає поєднання з повнотекстовим пошуком, уточнювальний етап ранжування, поріг відсікання за близькістю й нормалізація запиту. Збережені результати частих формулювань знімають частину навантаження на базу.

Чи не просяде база під час роботи поверх готової системи?

Пошук читає дані окремо від основного навантаження: запити йдуть на репліку, важке перебудування — у вікно обслуговування. Схему з векторами додаємо поруч, наповнення йде фоном, пошук вмикаємо поступово. Відкат — вимкнення шару пошуку: решта системи продовжує працювати як раніше.

Обговоримо задачу: що підготувати до розмови

Щоб оцінка була предметною, а не «за векторний пошук загалом», до розмови достатньо кількох вступних. Частину уточнимо в листуванні, решту розберемо на зустрічі.

  • Постановка задачі пошуку: які рішення людина ухвалює за видачею — знаходить конкретний пункт регламенту, порівнює умови чи просить синтез за кількома документами одразу. Від цього залежить, чи потрібен лише пошук за pgvector або шар генерації поверх нього.
  • Приклади користувацьких запитів — 10–20 реальних формулювань, включно з короткими, з одруками й двома мовами. За ними налаштовуємо розбиття на фрагменти й розуміємо, де додатково потрібен пошук за ключовими словами.
  • Обсяг і типи документів: скільки файлів, які формати, як швидко зростає корпус. Це визначає вибір індексу в pgvector і порядок переіндексації без зупинки сервісу.
  • Вимоги до свіжості даних: допустиме відставання між публікацією документа та його появою у видачі. Якщо рахунок іде на хвилини, закладаємо потокове завантаження й чергу оновлення векторів.
  • Схема доступу до інформації: хто які документи бачить. Права розмежовуємо фільтрами на рівні рядків у PostgreSQL, щоб вони застосовувалися до відбору найближчих векторів, а не після.
  • Поточне навантаження й поведінка при збої: скільки запитів за секунду в піку, який час відповіді прийнятний і що сервіс робить при недоступності бази — віддає кеш чи чесно повідомляє про помилку.

Опишіть задачу у вільній формі — повернемося з оцінкою та планом робіт за етапами.

Напрямки AI-розробки

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка логотипу компанії B2B Advance
    Розробка логотипу компанії B2B Advance
    815
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1140