Як я випадково побудував систему оркестрації LLM у браузері

Архітектурний погляд на те, як Litseller використовував GPT API, React, промпти й оркестрацію на боці браузера, щоб генерувати структурований контент книжкового каталогу.

Як я випадково побудував систему оркестрації LLM у браузері
Оркестрація LLM у браузері, зібрана з React, промптів і практичних інженерних рішень

Два роки тому я зробив Litseller.

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

Озираючись зараз, я розумію, що це по суті була повноцінна система оркестрації LLM. Просто реалізована не на бекенді, а прямо в браузері.

Чим це було насправді

Важливо правильно це окреслити.

Litseller не був LLM-сервісом.

Це був класичний вебзастосунок із каталогом, поверх якого я вбудував LLM як інструмент генерації даних в адмінці.

Уся оркестрація відбувалася в простому потоці: інтерфейс редактора, GPT API, JSON, валідація, збереження на бекенді.

  • Жодних черг.
  • Жодних воркерів.
  • Жодної серверної оркестрації.
  • Жодної складної інфраструктури.

Усе трималося на React, промптах і ланцюжках запитів.

Архітектура

Систему було поділено на три шари.

  • Фронтенд: адмінка на Next.js, редактор і вся логіка LLM.
  • Бекенд: API на .NET, валідація і збереження.
  • Сховище: SQL Server і S3.
Логіка LLM узагалі не жила на бекенді. Виклики йшли просто з браузера.

Конвеєр генерації

Замість одного великого запиту я побудував конвеєр.

  1. Перевірити, чи модель знає книжку.
  2. За потреби уточнити назву.
  3. Обрати категорію.
  4. Згенерувати основну інформацію.
  5. Обрати блоки контенту: короткий зміст, цитати, теми та інші розділи.
  6. Згенерувати кожен блок окремо.
  7. Зібрати JSON.
  8. Перекласти контент іншими мовами.
  9. Перевірити результат.
  10. Зберегти фінальні дані.

Це вже була справжня оркестрація, просто без окремого сервісу оркестрації.

Чому блоки спрацювали добре

Генерація контенту блоками стала одним із найсильніших рішень.

Я не просив модель згенерувати всю сторінку книжки за один раз.

  • Короткий зміст генерувався окремо.
  • Персонажі генерувалися окремо.
  • Цитати генерувалися окремо.
  • Теми генерувалися окремо.

Це дало кращий контроль якості, можливість перегенерувати окремі частини, стабільніший JSON і менше помилок.

На практиці це перетворилося на ручний шар контролю версій над виводом LLM.

Промпт-інжиніринг

Промпти були прості, але структуровані.

  • Суворий формат JSON.
  • Чіткі інструкції.
  • Мінімум магії.

Контекст передавався явно: назва, автор, категорії, мова і попередні блоки.

  • Без пам'яті розмови.
  • Без складного стану.
  • Без tool calling.

Найсуперечливіше рішення

Ключ API зберігався в localStorage.

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

Це було свідоме рішення. Ризики були зрозумілі й прийняті.

До того ж доступ був обмежений і захищений вручну через Cloudflare.

Слабкі місця

Якщо чесно подивитися сьогодні, слабкі місця очевидні.

  • Ключ API на клієнті.
  • Немає централізованого обмеження частоти запитів.
  • Немає централізованого контролю викликів.
  • Немає механізму повторів чи backoff.
  • Немає нормальної валідації схеми.
  • Слабка обробка помилок.
  • Немає спостережуваності.
  • Забагато складної логіки в браузері.
Це був сильний MVP, але не готова до продакшену LLM-платформа.

Сильні місця

Водночас система працювала і давала реальний результат.

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

Що я зробив би інакше сьогодні

Якби будував це сьогодні, я змінив би архітектуру.

  • Перенести виклики LLM у бекенд-шлюз.
  • Прибрати ключ API з клієнта.
  • Додати черги і повтори.
  • Запровадити сувору валідацію JSON-схеми.
  • Додати логування і трасування.
  • Реалізувати обмеження частоти запитів.
  • Відокремити оркестрацію від інтерфейсу.

Головний висновок

Найцікавіше для мене те, що я не проєктував LLM-систему.

Я просто розв'язував задачу.

Лише згодом я усвідомив, що побудував архітектуру, яку сьогодні називають оркестрацією LLM.

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

Що відбувається всередині моделі

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

Як навчається мовна модель: інтерактивна схема

Читати далі