Статьи по тегу: architecture

В классической программной инженерии правит детерминизм. Функция сортировки либо возвращает отсортированный массив, либо нет. Мы пишем `assert result == expected`, запускаем CI/CD пайплайн и спокойно идем пить кофе. В разработке продуктов на базе Large Language Models (LLM) этот уютный подход рушится на первом же коммите.

Каждый стартап начинает интеграцию AI по одному сценарию. Вы устанавливаете пакет `openai`, пишете десять строк кода для вызова эндпоинта `/v1/chat/completions`, и в приложении появляется умный ассистент. Фича быстро улетает в продакшен, метрики растут. Через пару месяцев приходят продакт-менеджеры: «Говорят, Claude 3.5 Sonnet пишет код лучше, давай переключим генерацию компонентов на него. А для парсинга гигантских логов нам нужен Gemini 1.5 Pro с его окном в два миллиона токенов. Еще Llama 3…

В конце первого месяца активного роста продукта мы получили счет от провайдеров LLM. Цифра с четырьмя нулями заставила нас остановить разработку новых фич и открыть логи. Мы ожидали увидеть там сложные, уникальные запросы, отражающие невероятную креативность наших пользователей. Вместо этого мы увидели бесконечный день сурка.

Пользователь набирает сложный технический промпт на экране смартфона. Текст занимает три экрана, содержит куски кода и подробные бизнес-требования. Большой палец тянется к кнопке «Отправить». В этот момент поезд метро въезжает в туннель. Соединение обрывается. Приложение перезагружается, либо пользователь смахивает его в попытке «починить интернет». При следующем запуске экран пуст. Промпт исчез. Пользователь уходит и больше не возвращается.

На дашборде Datadog всё светится зеленым. График RPS ровный, P95 latency держится в рамках 1.5 секунд, HTTP 500 отсутствуют как класс, CPU контейнеров едва переваливает за 30%. Система работает идеально.

Вы подключаете LLM к своему проекту. Находите библиотеку, пишете код для работы с потоковым ответом (`stream: true`), отправляете запрос из фронтенда и ждете. И ждете. А через 15 секунд весь сгенерированный текст вываливается на экран одним куском.

Когда команда интегрирует искусственный интеллект в продукт, первой мыслью становится выбор флагмана. Если бюджет позволяет, разработчики инстинктивно тянутся к самой мощной модели на рынке, например, к GPT-4o. Логика кажется железной: раз нейросеть умеет писать микросервисы на Go и анализировать многостраничные юридические контракты, она играючи раскидает пользовательские отзывы по категориям или ответит на базовые вопросы в чате.

Первый релиз AI-продукта всегда проходит по одному и тому же сценарию. Вы тестируете функционал локально: запросы к OpenAI или Anthropic улетают мгновенно, стриминг плавно отрисовывает текст, логи чисты. Продукт отправляется в продакшен. Приходят реальные пользователи. И тут случается столкновение с реальностью: кто-то запускает массовый импорт данных, десяток пользователей одновременно нажимают кнопку «Сгенерировать», и графики мониторинга окрашиваются в тревожный красный цвет.

Индикатор загрузки вращается на экране. Проходит пять секунд. Десять. На двадцатой секунде пользователь начинает нервно водить курсором по странице. На тридцатой секунде он обновляет вкладку, принудительно обрывая соединение. На сороковой система возвращает ошибку 504 Gateway Timeout, но результат уже не имеет значения — клиент ушел, и с высокой долей вероятности ушел навсегда.

Веб-разработка долгие годы молилась на stateless-архитектуру. REST-паттерны диктуют суровое правило: сервер обязан забыть клиента в ту же миллисекунду, когда закрывается TCP-соединение. Балансировщики нагрузки тасуют запросы между десятками серверов, PHP-скрипты умирают после отдачи ответа, Node.js-воркеры перерабатывают контексты. Это прекрасно работало для CRUD-приложений, где всё состояние жестко зафиксировано в базе данных. Но для ИИ-разработки мир без состояний обернулся настоящим…