Межі в архітектурі ШІ: імовірнісні моделі всередині детермінованих систем
LLM змінює проєктування систем: контекст стає компонентом, вивід моделі не заслуговує на довіру, відповідь 200 може виявитися збоєм, а вартість — обмеженням рантайму. Правила й повноваження тримайте в коді.
Візьмімо планувальник розкладки, зібраний із трьох частин із моделями всередині. LLM читає запит користувача, RAG підтягує застосовні стандарти, а графова мережа з увагою пропонує, куди поставити кожен елемент. Жодна з них не має останнього слова. Що потрапить у розкладку, вирішує детермінований рушій із правилами й перевірками розміщення, і він постійно відхиляє пропозиції моделі.
Такий поділ не був заздалегідь обраним принципом проєктування. Він з'явився після розкладки, яку модель повернула з цілковитою впевненістю: чотири компоненти, з'єднані між собою й накладені один на одного, — геометрія, неможлива на справжньому дитячому майданчику. Жодної помилки не виникло. Відповідь була коректно оформлена, міркування читалося нормально, а результат був фізично неможливим.
Почнімо зі звичного
Візьмімо звичайний бекенд інтернет-магазину:
User → API Gateway → Backend Services → Database
У продакшен-системах є й додаткові частини — кеші, черги, авторизація, спостережуваність, зовнішні API, — але загальна картина зберігається, і її головна риса — контроль. Застосунок сам обирає, які операції виконувати, яких даних торкатися, які бізнес-правила діють, як обробляти збої та що вважати успіхом. Коли замовлення оплачено, ніхто не просить сервіс тлумачити, чи це правда:
if order.status == "PAID":
ship_order()
Вирішує програма.
Тепер додаймо модель
Припустімо, до системи приходить ШІ-асистент підтримки. Першого дня архітектура така:
User → Backend → LLM API → Response
Його випускають. Невдовзі клієнт питає про статус замовлення, і моделі потрібні дані, яких немає в промпті:
User → Backend → Fetch order data → Build context → LLM → Response
Потім інший клієнт просить асистента скасувати замовлення. Тепер модель має щось зробити, а не просто щось сказати:
User → LLM → Decide → Call cancellation API → DB → LLM → Response
Далі надходить питання, для якого потрібні три окремі операції, і картинка взагалі перестає бути лінією.
Модель більше не просто залежність. Вона стала частиною потоку керування, і саме тут архітектура змінюється по-справжньому.
Справжня відмінність — імовірнісні обчислення
Традиційний софт влаштований так:
Input → Program → Output
ШІ-софт виглядає так:
Input → Context → Model → Probabilistic Output
Різницю добре показує функція, яку можна протестувати напряму:
def calculate_discount(price):
return price * 0.9
Виклик calculate_discount(100) дає 90, і жоден суддя не потрібен, щоб сказати, чи 90 приблизно правильно.
Візьмімо запит на кшталт переказу договору. Тут немає одного правильного рядка для порівняння. Перевірки стосуються поведінки. Чи охоплено всі важливі пункти? Чи не вигадано чогось? Чи не зникла умова розірвання? Чи не прочитано юридичний термін неправильно? Чи допомагає результат бодай комусь?
Тому архітектурі потрібне те, чого немає в більшості систем: оцінювання поведінки, а не лише виконання коду.
Відповідь 200 однаково може бути збоєм
На цьому потрапляє багато команд.
Класичний моніторинг починається з простого правила: 200 — усе добре, 500 — зламалося. ШІ-системи можуть повернути 200 із неправильною відповіддю. Або 200, побудований на застарілому контексті. Або 200, у якому витекло щось чутливе. Або 200, де агент крутився дев'ятнадцять разів і набив рахунок.
Кожен окремий виклик пройшов успішно, а застосунок однаково не впорався.
Самих статус-кодів недостатньо, щоб судити про здоров'я системи. Командам потрібні затримка, частка помилок, витрата токенів, вартість, якість пошуку, якість відповідей, виклики інструментів і відгуки користувачів, пов'язані однією трасою.
Контекст — окремий компонент
Традиційний застосунок питає, які дані потрібні програмі. ШІ-застосунок ставить ще одне питання: що має побачити модель?
Для питання про політику повернення можуть знадобитися саме питання, політика компанії, акаунт клієнта й останні чотири репліки розмови.
Додати векторну базу — проста частина. Складніше вирішити, що має потрапити у вікно, бо застосунку треба зважити, наскільки кожен елемент релевантний і свіжий, кому дозволено його бачити, скільки місця він займає, що каже історія, що робити, коли джерела суперечать одне одному, і як тримати орендарів окремо. Контекст перестає бути трубопроводом і стає архітектурою.
Бізнес-правила не належать моделі
Припустімо, користувач просить видалити його акаунт. Можна написати в промпті правило: видаляй, якщо просять. Але тоді межу проведено не там. Кращий потік:
User → Model → Intent: DELETE_ACCOUNT → Validation
→ Authorization → Business rules → Delete
Модель з'ясовує, чого хоче користувач. Детермінований код вирішує, чи це дозволено.
Нехай модель розбирається з неоднозначністю. Нехай софт розпоряджається повноваженнями.
Нова межа довіри
У класичному варіанті межа одна:
Untrusted user → Validation → Trusted application
Щойно в потоці з'являється модель, її вивід теж треба вважати недовіреним:
User → Application → Model → Model output → Validation → Real systems
На цей вивід може впливати багато чого: введення користувача, знайдені документи, зовнішні дані, відповіді інструментів, впроваджені інструкції або контекст, якого ніхто не планував.
Якщо перевіряти й авторизувати вивід моделі перед виконанням, виходить зовсім інша система, ніж коли виконувати його напряму.
Інструменти перетворюють один запит на розподілений процес
Репліка в чаті — це один виклик моделі. Агентний хід може бути ланцюжком: модель, пошук, модель, виклик API, модель, ще один пошук, модель, відповідь.
Користувач бачить один запит, а бекенд виконує десять операцій.
Виникає проблема спостережуваності, яку звичайні логи запитів не розв'язують. Потрібні наскрізні траси, і не лише для налагодження. Вони відповідають, чому запит коштував $3, чому агент п'ять разів смикнув той самий API, який знайдений фрагмент призвів до неправильної відповіді і куди насправді пішов час.
Вартість стає обмеженням рантайму
Бекенд-команди й так планують бюджет CPU, пам'яті, з'єднань і пропускної здатності. ШІ додає нові статті: токени, виклики моделей, розмір контексту, виклики інструментів і витрати на інференс.
Один прогін агента може розкладатися так:
Model $0.02
Search
Model $0.04
API call
Model $0.03
Search
Model $0.05
─────────────
Total $0.14
Цей прогін коштує чотирнадцять центів. Помножте на тисячі користувачів на день, і число перестає бути бухгалтерією та стає обмеженням дизайну. Тому в продакшен-системах з'являються маршрутизація моделей, кешування промптів, атрибуція витрат і бюджети на кожен запит.
Розподілені системи завжди так поводилися з обмеженнями процесора й пам'яті. Тут ресурси, які споживає кожен запит, — це долари, токени й секунди.
Повтори стають ризикованішими
Повторні спроби — стандартна практика. А тепер уявіть, що запит — надіслати клієнтові рахунок.
Модель викликає send_invoice(), і виклик проходить. Мережа губить відповідь, тож виклик повторюється й знову проходить. Клієнт отримує два рахунки.
Поняття тут старі: ідемпотентність, тайм-аути, межі транзакцій, часткові збої та компенсація. ШІ їх не вигадав. Він повернув їх у центр застосунків, які примудрялися їх ігнорувати.
Тестування теж змінюється
Звичайний пайплайн іде від коду до юніт-тестів, інтеграційних тестів і деплою. Для ШІ потрібен ще один шар поруч, де юніт-тести, оцінювання й перевірки безпеки проганяють ШІ-процес до релізу.
Поведінка може зсунутися від зміни будь-чого з цього: коду, промпту, моделі, контексту, пошуку чи визначення інструмента. Можна не випустити жодного рядка коду застосунку, провайдер оновить модель, і до понеділка продукт уже інший.
Як виглядає архітектура
Уявіть потік, що йде від входу через логіку застосунку, збирання контексту й шар моделі, далі через валідацію та політики й нарешті до інструментів. Під ним проходить наскрізний шар: оцінювання, спостережуваність, вартість, безпека, зворотний зв'язок і управління.
Найважливіше в цій картині просте: LLM — це не архітектура. Це один компонент усередині неї.
Чи справді архітектура ШІ інша?
Частково. Усе старе так само потрібне: API, бази даних, авторизація, кешування, черги, балансування навантаження, повтори, транзакції, моніторинг і безпека. Ніщо з цього не зникло.
Змінився тип одного компонента:
Traditional: deterministic → deterministic → deterministic
AI: deterministic → PROBABILISTIC → deterministic
Ця єдина заміна змінює все довкола.
Принцип
Увесь аргумент зводиться до одного рядка:
Розміщуйте ймовірнісний інтелект усередині детермінованих меж.
Модель може дійти висновку, що користувач, імовірно, хоче скасувати замовлення. Застосунок вирішує, чи дозволено цьому користувачеві його скасувати.
Модель може заявити, що повернення становитиме $42. Сама сума має надходити з детермінованого коду, наприклад refund = order.amount - fees.
Модель може вирішити, що треба викликати API повернення. Детермінований софт вирішує, чи має запит право його викликати.
Оце і є межа.
Якщо подивитися ширше
Десятиліттями інженери вчилися робити системи передбачуваними, тестованими, спостережуваними, безпечними, відновлюваними й масштабованими. ШІ цих зусиль не скасовує. Він додає частину, якій цих властивостей за природою бракує, і решті системи доводиться їх забезпечувати.
Питання, яких демо ніколи не ставить
Найлегша помилка — думати, що досить викликати LLM. Для демо досить, для продукту ні.
Щойно ШІ опиняється в реальній системі, на кілька питань, на які детермінована версія відповідала безкоштовно, тепер потрібні відповіді:
- Що має знати модель?
- Чи можна довіряти її виводу?
- Що буде, якщо вона помилиться?
- Хто має право виконувати її рішення?
- Як тестувати поведінку, якщо вона змінюється у вас під ногами?
- Як виявити збій, якщо виклик повернув 200?
- Скільки може витратити один запит?
- Що буде, якщо провайдер ляже?
- Як відновити процес, що обірвався на півдорозі?
Відповідь не в тому, щоб додати більше логіки в промпт. Відповідь — у продуманіших межах довкола моделі.
Нехай ШІ має справу з неоднозначністю, а софт — із визначеністю. Межу між ними варто будувати ретельно.
Модель зрозуміла, чого хотів користувач, але не зрозуміла, чи може такий результат існувати. Її робота — лише перше.