Як працюють сучасні AI-боти: розбір продакшн RAG-пайплайну
Мене звати Артем, я працюю як Head of AI Transformation і займаюся впровадженням AI у великому ентерпрайзі, що об’єднує понад 12 компаній. Мій головний виклик — змінювати процеси так, щоб AI реально працював для більш ніж 500 людей: інженерів, менеджерів, продакт-оунерів, директорів та інших фахівців.
У цій статті я хочу показати повний продакшн-потік RAG-системи: від ingestion до observability.
Без магії, без припущень. Тільки практичні речі, які дійсно працюють у реальних системах.

Коли я тільки почав будувати RAG-системи, все здавалося простим. Пишеш в Jupyter Notebook, робиш кілька викликів до ретривера, передаєш контекст у LLM і ніби все працює.
А потім ти пробуєш запустити це в продакшн... і все змінюється.
Реальний світ одразу приносить виклики, яких ти навіть не бачив у своєму Notebook: несистематизовані документи, непередбачувані запитання, затримки, дивні результати ретривалу... одним словом, уся «класика» продакшен-інженерії.
Щоб зробити ці системи більш зрозумілими, я створив інтерактивну візуалізацію Production RAG Pipeline.
Подивитися її можна тут:
👉 www.artem-antonenko.com/...-visualization#production
Нижче я пройдуся по головних етапах максимально простою мовою.
Моя мета — допомогти вам краще зрозуміти повний життєвий цикл RAG-системи та впевнено пояснювати його на співбесідах чи технічних обговореннях архітектури.
1. Ingestion Layer. Підготовка даних для векторної бази
Перш ніж говорити про чанкінг, важливо зрозуміти, що саме робить векторна база та навіщо вона потрібна.
Більшість із нас починали з реляційних баз: таблиці, рядки, колонки, SQL. I вони чудові, коли точно знаєш, що шукаєш:
SELECT * FROM orders WHERE user_id = 123 AND status = 'pending';
Це добре працює для структурованих даних та чітких фільтрів.
Але зовсім не підходить для таких запитів, як:
«Show me documents that explain how our billing system handles failed payments.»
Тут важко виразити «схожий зміст» мовою SQL.
Векторні бази працюють інакше.
Замість того, щоб зберігати лише рядки й колонки, вони зберігають вектори — списки чисел, які представляють значення тексту.
Приблизно це виглядає так:
- Ви берете фрагмент тексту (абзац, секцію тощо).
- Надсилаєте його в embedding-модель (OpenAI, Cohere тощо).
- Модель повертає вектор на кшталт [0.12, —0.88, 0.03, ...] з сотнями або тисячами вимірів.
- Тексти зі схожим змістом мають вектори, розташовані близько один до одного.
Коли користувач ставить запитання:
- Ви теж перетворюєте його в embedding-вектор.
- «Запитуєте» у векторної бази: «Which stored vectors are closest to this one?»
- База порівнює їх, наприклад, за cosine similarity.
- Повертає ті фрагменти, чий зміст найбільш схожий на питання.
Це головна відмінність від реляційної бази: ви шукаєте не збіги слів, а збіги змісту.

Як підготувати документи
Щоб усе це працювало, документи мають бути підготовлені у вигляді «пошукових» чанків із embedding-векторами.
Стратегії чанкінгу бувають різні:
Character-based splitting. Просто ріжемо кожні N символів. Легко, але може випадково перервати речення.
Character splitting with overlap. Те саме, але з перекриттям між чанками. Дає більше контексту.
Splitting by headers/titles. Для структурованих документів (мануали, техдоки) — розбивка за заголовками.
Semantic splitting. Групуємо речення за змістом. Кожен чанк представляє цілісну думку. Це підвищує якість, але LLM для кожного документа — дорого. Тому з’явився latent chunking.
Latent chunking — це спосіб створювати «змістові» чанки без участі LLM. Замість того, щоб питати модель, де робити розриви, ми аналізуємо сам текст.
Спрощено процес виглядає так:
- Текст проходять невеликим sliding window, наприклад, по кілька речень або по фрагментах у
150–200 токенів. - Для кожного вікна створюється embedding-вектор.
- Система порівнює вектори сусідніх фрагментів і дивиться, наскільки їхній зміст схожий.
- Якщо семантична схожість різко падає, то це ознака, що починається нова тема. У цьому місці й створюється новий чанк.
- У результаті текст автоматично розбивається на логічні блоки, які добре тримають цілісну думку.
Це дозволяє отримати семантично цілісні чанки без дорогих викликів LLM. Якість зазвичай вища, ніж у простого character-based splitting, але вартість значно нижча, ніж у
Offline Indexing Pipeline
Коли чанки готові:
- Ви генеруєте для кожного з них embedding. У деяких базах робите це самі (виклики OpenAI / Meta / Cohere). У Weaviate база може робити embedding автоматично.
- Зберігаєте: текст чанку + метадані та embedding-вектор в індекс (часто HNSW).
Тепер RAG може запитувати: «дай чанки зі змістом, найближчим до цього запиту».
Це фундамент усього пайплайну.
2. Query Input & Processing. Розуміння того, що користувач справді питає
Тепер ми переходимо до «онлайнової» частини пайплайну — тобто до реального запиту користувача.
А реальні користувачі рідко формулюють свої питання чітко й технічно грамотно. Вони можуть ставити:
- розмиті запити;
- дуже довгі запити;
- майже нерелевантні запити;
- запити з внутрішнім сленгом або скороченнями.
Тому на цьому етапі зазвичай виконуються такі кроки.
Query rewriting
Користувачі часто пишуть дуже короткі або нечіткі питання.
Наприклад, хтось може ввести: «fix login?»
Система переформульовує це у щось зрозуміліше, наприклад:
«How do I troubleshoot login failures in our authentication service?»
Це значно підвищує шанси ретривера знайти правильні документи.
Lightweight expansion (наприклад, HyDE)
Замість переформулювання питання система може створити коротку «гіпотетичну відповідь», яка допомагає скерувати пошук.
Це не фінальна відповідь, радше щось на кшталт:
«Login failures often occur due to invalid tokens or OAuth configuration issues...»
Такий розширений текст дає ретриверу більше контексту.
Normalizing the question before retrieval
Користувачі часто використовують внутрішній жаргон або скорочення.
Якщо хтось питає: «issue in billing v2?»
Система перетворює це на повну назву:
«Billing Service v2» щоб база могла знайти відповідні документи.
3. Query Routing. Визначаємо, наскільки складний запит
Не кожне питання потребує повного RAG-пайплайну.
Зазвичай маршрутизатор обирає:
- Simple path: на просте запитання відповідає маленька модель.
- Cache lookup: якщо відповідь уже є в кеші.
- Full RAG path: складний випадок → запускається весь пайплайн.
Це економить гроші, знижує затримку і зменшує навантаження.

4. Hybrid Retrieval. Пошук найбільш релевантних знань
Цей етап — справжнє ядро RAG. Навіть якщо у вас чудово підготовлені дані та якісний чанкінг, результат повністю залежить від того, наскільки добре система може дістати саме ті фрагменти знань, які відповідають на конкретне запитання. Сучасні підходи показали, що жоден окремий метод пошуку не працює однаково добре для всіх типів запитів, тому в продакшені зазвичай комбінують кілька технік, щоб отримати максимально точний результат.
Sparse search (BM25)
BM25 шукає буквальні збіги у тексті. Він особливо ефективний, коли користувач згадує щось конкретне: назву продукту, API endpoint, код помилки, номер версії. Якщо хтось запитає про Error 5032, BM25 з великою ймовірністю поверне документ, де ця фраза зустрічається дослівно. Він не розуміє змісту, зате забезпечує дуже високу точність у випадках, коли потрібен саме точний збіг.
Dense search (vector search)
Це повна протилежність sparse-пошуку. Він не звертає уваги на точні формулювання, а шукає фрагменти, семантично подібні до запиту, навіть якщо ключові слова не збігаються. Саме таким чином система може зрозуміти, що «payment declined» і «transaction failed» фактично описують одну й ту саму проблему. Це критично важливо тоді, коли користувач не знає правильних термінів.
Metadata filters
На цьому етапі враховується структура та політики доступу. Результати можна фільтрувати за типом документа, відділом, датою створення, мовою чи правами користувача. Наприклад, якщо інженер підтримки шукає внутрішній troubleshooting-гайд, можна автоматично відсіяти маркетингові матеріали або документи, до яких він не має доступу. Це забезпечує як релевантність, так і безпеку.
Fusion (поєднання sparse + dense пошуку)
Замість того щоб обирати один метод, більшість продакшн-систем комбінують обидва. Алгоритми на кшталт Reciprocal Rank Fusion (RRF) беруть рейтинги зі sparse та dense пошуку й об’єднують їх в один ранжований список. Таким чином система не пропускає документи, які знайшов BM25, але й не ігнорує семантично релевантні фрагменти, які знайшов vector search. Це проста, але дуже ефективна техніка, яка суттєво покращує recall.
Reranking (cross-encoder)
Після злиття у вас зазвичай залишається довгий список «пристойних» кандидатів, але потрібно вибрати найкращі K. На цьому етапі reranker (часто cross-encoder модель) порівнює кожен кандидат із запитом і обчислює справжню релевантність. Це набагато точніше, ніж порівняння embedding-векторів, оскільки reranker аналізує повний текст. Це фінальне «очищення», яке піднімає найрелевантніші чанки в самий топ.
Keeping the knowledge fresh (оновлення знань)
У RAG-системах часто недооцінюють, як швидко змінюються документи в продакшені. З’являються нові політики, оновлюється код, модифікуються API, старий контент втрачає актуальність. Коли це трапляється, систему потрібно оновлювати не повністю, а лише для тих частин, які змінилися: перерендерити чанки та повторно створити embedding-и. Більшість команд роблять це через інкрементальний pipeline або фоновий job, який відстежує зміни. Це дозволяє тримати векторну базу в актуальному стані без повного rebuild-у індексу.

5. Context Assembly. Побудова промпту для LLM
Коли етап ретривалу завершено, система об’єднує всі частини в один структурований промпт, з яким модель може працювати. Це включає початкове запитання користувача, знайдені релевантні чанки, інструкції, правила форматування, приклади та інколи навіть вихідну схему. Це один із тих етапів, які здаються непомітними, але при правильній реалізації дають значно більший приріст якості, ніж перехід на потужнішу LLM.
Нижче наведено кілька прикладів того, як виглядає якісно сформований промпт.
Приклад 1: Допомагаємо LLM залишатися в межах отриманого контексту
Без структури:
«Here is some context and a user question. Answer the question.»
Зазвичай це призводить до розмитих або навіть частково вигаданих відповідей.
З правильною структурою:
|
You are answering a question based ONLY on the context provided below. User question: Relevant context: Instructions: |
Така форма допомагає моделі залишатися в межах контексту, формувати лаконічні відповіді та чесно вказувати, коли інформації бракує.
Приклад 2: Чіткі правила форматування, щоб уникнути «неструктурованого» виводу
Якщо формат не визначити явно, модель може повертати довгі абзаци, змішувати стилі або додавати зайві коментарі.
З правильною структурою форматування:
|
Answer the question using ONLY the context. Output format: — Short title — 3 bullet points with the key information — 1 final recommendation line Do not include anything outside this structure. |
Результат — чіткі, впорядковані відповіді, які легко читати та обробляти.
Приклад 3: Примусове дотримання схеми для downstream-систем
Це важливо, коли відповідь від LLM має бути спожита іншим сервісом чи клієнтською аплікацією.
З вимогою суворо дотримуватися JSON-схеми:
|
Return the answer as valid JSON with the following fields: { Only return JSON. Do not include explanations or comments. |
Це запобігає типовій проблемі продакшен-систем: коли модель повертає «майже JSON», але з коментарями чи текстом, що ламає downstream-процеси.
Приклад 4: Few-shot приклади для стабілізації поведінки моделі
Іноді LLM поводиться нестабільно, доки їй не показати приклад правильної відповіді.
|
Here are examples of how to answer similar questions: Example 1: Now answer the user’s question using the same style. |
Такі приклади значно покращують стабільність тону, структури та точність відповідей.
Приклад 5: Запобіжники проти «вигадування» поза контекстом
Іноді потрібно уникнути того, щоб LLM вигадувала або додавала інформацію, якої немає в отриманому контексті.
|
If the answer requires information not present in the context: Be strict about this rule. |
Такі обмеження суттєво зменшують кількість галюцинацій і роблять поведінку моделі більш передбачуваною.
6. Safety & Guardrails. Перевірка перед поверненням відповіді
Після того, як LLM сформувала відповідь, система не повертає її користувачеві одразу. Спочатку виконується кілька перевірок, щоб упевнитися, що відповідь точна, безпечна та відповідає заданим обмеженням.
Citation check. Система перевіряє, чи кожен згаданий факт дійсно походить із одного з отриманих чанків. Якщо модель посилається на щось, чого в контексті не було, це ознака того, що вона могла «вигадати» інформацію або використати знання поза межами дозволеного контексту.
Faithfulness check. На цьому етапі відповідь порівнюють із отриманими документами, щоб переконатися, що модель не перебільшила, не додала вигаданих кроків і не поєднала інформацію некоректно. Навіть дуже сильні моделі інколи «дрифтують», тому це важлива перевірка на відповідність контексту.
Policy filtering. Нарешті результат сканується на наявність чутливої чи забороненої інформації: PII, конфіденційних даних, небезпечних інструкцій, токсичної лексики тощо. Це захищає і користувача, і компанію, особливо в середовищах із високими вимогами до безпеки або регуляцій.
Ці запобіжні механізми гарантують, що до користувача повертаються лише якісні, коректні та безпечні відповіді, навіть якщо модель або ретривал часом можуть помилятися.

7. Observability Layer. Що робити, коли щось пішло не так
Якщо вам коли-небудь доводилося дебажити RAG-систему в продакшені, ви знаєте, наскільки цінним є цей шар.
У таких системах може дуже багато речей піти не так, і часто симптоми виникають далеко від справжньої причини. Observability дає змогу «зазирнути всередину» пайплайну, замість того щоб гадати.
Хороша система спостереження відстежує:
- затримку кожного компонента;
- релевантність результатів ретривалу;
- поведінку кешу;
- використання токенів;
- патерни генерації;
- результати A/B-тестів;
- журнали помилок;
- зворотний зв’язок від користувачів.
Це допомагає відповісти на питання:
- Чому сьогодні виросла затримка?
- Чому ретривер повернув нерелевантні чанки?
- Яка частина пайплайну дала збій?
- Чи не видалив якийсь фільтр важливий документ?
- Чи не помилився reranker?
Приклад 1: Зникаюча затримка
Уявіть, що ваша LLM раптом починає відповідати вдвічі повільніше. Без observability це виглядає як: «сьогодні модель повільна». Але детальні трейсинги можуть показати, що насправді ретривер тайм-аутиться на 10% запитів, а не модель. Рівнем нижче з’ясовується, що індекс HNSW перебудовувався після великого завантаження даних, що й спричинило тимчасову затримку.
Приклад 2: Нерелевантні відповіді після, здавалося б, невинної зміни
Уявіть, що користувачі починають скаржитися на те, що відповіді стали «не по темі». Переглянувши кілька трейсів, ви помічаєте, що reranker раптом почав віддавати перевагу занадто загальним документам. Ще глибше у логах стає зрозуміло: під час ingestion хтось змінив стратегію чанкінгу, і чанки стали надто великими — через це вони втратили специфічність.
Висновки
Я створив візуалізацію, тому що з часом зрозумів: «RAG у ноутбуці» та «RAG у продакшені» — це два різні світи. Коли розумієш повний цикл: ingestion, routing, retrieval, prompting, safety, observability, то архітектура стає набагато зрозумілішою й менш загадковою.
Сподобалась стаття? Підписуйтесь на автора, щоб отримувати сповіщення про нові публікації на пошту.

32 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів