Houdrik
Ai
· 3 травня 2026 р.· 11 min read

Як випустити RAG-систему, що витримує продакшн-трафік

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

Cover · rag-system-production-traffic

Робоча RAG-демонстрація — одна з найпростіших речей, яку можна зібрати у 2026 році. На це вистачить вечора: розбити документи на чанки, порахувати ембедінги, скласти їх у сховище, дістати top-k, передати мовній моделі та підключити чат-інтерфейс. Виглядати буде ефектно.

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

1. Складіть набір для оцінювання на третій день

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

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

Цей набір крутиться в CI. Будь-яка зміна пошуку, підказок, вибору моделі чи навіть токенізатора запускає переоцінку. Тренди важать більше за абсолютні числа, але ми публікуємо і те, і те. Достовірність (чи послалася відповідь на правильний документ?) і точність (чи був процитований документ справді релевантним?) — ось дві метрики, які ми відстежуємо завжди.

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

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

Наш базовий підхід такий: щільний пошук (косинус на ембедінгах) + розріджений (BM25 поверх tsvector) + об'єднання через reciprocal rank fusion. Далі — cross-encoder, який переранжує top-50. Переранжування — найдорожчий крок конвеєра; його результати ми агресивно кешуємо.

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

3. Поділ на чанки — це продуктовий дизайн, а не препроцесинг

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

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

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

4. Цитуй або відмовляйся

RAG-систему переслідують два режими невдачі: галюцинації та впевнено неправильні відповіді. Це не одне й те саме. Галюцинацію — коли модель вигадує те, чого в результатах пошуку немає, — виявити простіше. Впевнено неправильна відповідь гірша: модель переказує те, що є у пошуку, але воно не підходить до запитання, і користувач не має жодного сигналу їй не довіряти.

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

Частота відмов — це KPI, а не баг. Нульова частота відмов на наборі з 250 запитань — тривожний сигнал: модель упевнено вигадує там, де не знає відповіді.

5. Межі вартості — інженерне питання, а не фінансове

Рахунки від провайдерів LLM легко випустити з-під контролю. Жорсткі межі ми ставимо з першого дня й проєктуємо архітектуру так, щоб вона їх дотримувалася:

  • Агресивне кешування ембедінгів. Для заданого чанка ембедінг незмінний; кешуйте його назавжди.
  • Кешування відповідей із семантичною дедуплікацією. Хеш підказки — дешевий варіант; збіг за нормалізованим ембедінгом — трохи витонченіший. У будь-якому разі двадцять-сорок відсотків трафіку RAG, орієнтованого переважно на читання, потраплятиме в кеш.
  • Запасний рівень моделі. Коли місячні витрати перетинають вісімдесят відсотків бюджету, запити автоматично йдуть на дешевшу модель. Кешовані "флагманські" відповіді для цього клієнта позначаємо, щоб згодом не платити двічі.
  • Межі на одного орендаря. У SaaS RAG їх треба підтримувати на рівні бази даних, а не у прикладному коді.

Цими чотирма ходами ми щоразу скорочували успадковані рахунки RAG на шістдесят-вісімдесят відсотків уже на першому проєкті. На це просто ніхто не дивився.

6. Затримку дає пошук, а не генерація

Поширене припущення: "вузьке місце — це виклик LLM". Часто це не так. Завдяки стрімінгу користувач бачить перші токени за пів секунди навіть на флагманських моделях. Чого він не бачить — але відчуває — це 1,8 секунди до того, поки відпрацьовує пошук.

Ми профілюємо наскрізну затримку трасами OpenTelemetry і знаходимо повільний крок. Зазвичай це cross-encoder на холодному кеші або запит до Postgres, що промахується повз індекс, бо хтось додав фільтр і забув про індекс. Іноді — крок ембедінгу вхідних запитів: ембедінговий API провайдера не завжди швидкий.

Оптимізуйте холодний шлях. Кешуйте гарячий.

7. Ізоляція орендарів у двох місцях

Витік даних між орендарями — найгірший можливий режим невдачі для B2B RAG. Ми вимагаємо ізоляції у двох незалежних місцях:

  • На рівні SQL. Кожен запит пошуку несе ідентифікатор орендаря в WHERE. Не опційно. Не фільтр у Python. База даних відмовляє сама.
  • На рівні підказки. Складач підказок явно перевіряє, що всі знайдені чанки мають один ідентифікатор орендаря. Невідповідність — виняток — користувачу повертається загальне повідомлення про помилку.

Ремінь і шлейки. Якщо один шар обійшли — інший ловить.

8. Влаштуйте систему собі сам, перш ніж це зробить клієнт

Раз на місяць на повноцінній копії продакшну ми проводимо навмисні навчальні атаки:

  • Чи вдасться витягнути чанк з іншого орендаря?
  • Чи вдасться змусити модель виконати інструкцію prompt-injection, заховану в знайденому документі?
  • Чи вдасться вичерпати обмежувач швидкості й погіршити сервіс?
  • Чи вдасться запустити дорогий шлях коду дешевими вхідними даними?

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

Суть

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

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

Маєте додаток, який має жити?

Виведіть його з прототипу в продакшн.

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

Запустити проєкт