Продакшн шлюзов LLM: архитектура, компромиссы и суровые уроки — Каниш Мануджа, Twilio

AAI Engineer
Computing/SoftwareManagementInternet Technology

Transcript

00:00:00Я Ганеш Мануджа, ведущий инженер в Twilio. Давайте начнем с небольшого опроса поднятием рук.
00:00:20Кто здесь видел сообщение «Что-то пошло не так, пожалуйста, попробуйте снова»?
00:00:27Что ж, у нас есть несколько счастливчиков и те, кто плотно пообедал.
00:00:33Итак, за этим простым сообщением на самом деле скрывается очень сложная система, которая выдает его вам, несмотря на сбои у провайдеров моделей.
00:00:45И именно об этом мы сегодня будем говорить в контексте запуска в продакшн.
00:00:50Итак, что такое шлюз LLM?
00:00:52Шлюз LLM — это точка входа или промежуточный слой между вашими приложениями и стоящими за ними провайдерами моделей.
00:00:58Он выполняет множество задач: маршрутизацию, аутентификацию, резервное переключение, ограничение скорости и всевозможные правила управления.
00:01:08И в самом сердце этого шлюза лежит борьба между четырьмя факторами.
00:01:13Это доступность, задержка, защитные барьеры и стоимость.
00:01:18В случае ухудшения условий максимизировать все четыре параметра не получится.
00:01:22Вам нужно выбрать то, что важнее.
00:01:25Поэтому в этом докладе я хочу помочь вам сделать этот выбор для вашего сценария использования, если вы применяете шлюз LLM.
00:01:35А если вы проектируете шлюз, я хочу, чтобы вы заложили эти рычаги управления для своих пользователей и клиентов, чтобы они оставались довольны.
00:01:46Начнем с доступности.
00:01:50Если у вас один-единственный провайдер моделей, его потолок — это ваш потолок.
00:01:56Их сбой — это ваш сбой.
00:02:03Поэтому в традиционной разработке ПО ненадежные зависимости принято обрабатывать с помощью повторных попыток.
00:02:11Повторные попытки с экспоненциальной задержкой и случайными отклонениями.
00:02:15А когда все это не работает, срабатывает автомат защиты, который отключается после достаточного числа сбоев, и вы перестаете дергать эту чертову систему.
00:02:24Для LLM этого недостаточно.
00:02:26Модели LLM сильно отличаются от быстрых и дешевых API, к которым мы привыкли применять повторные попытки.
00:02:32Повторный запрос к LLM очень быстро исчерпывает ваш лимит времени отклика.
00:02:38К тому же, задействовать автомат защиты, когда у вас есть другой прекрасно работающий провайдер моделей для маршрутизации, не имеет смысла.
00:02:47Вам следует использовать второго провайдера.
00:02:48И в-третьих, как я уже говорил, вызовы работают медленно и стоят дорого.
00:02:53Поэтому слепые повторные попытки лишь умножают ваши расходы и хвостовые задержки.
00:02:58Что же здесь будет лучшим решением?
00:03:02Фактически это резервное переключение для каждого отдельного запроса.
00:03:05Это значит, что вы можете сначала попробовать провайдер моделей A, а затем последовательно переключиться на провайдер B, если запрос к провайдеру A завершился неудачей.
00:03:14Еще один вариант — отправлять запросы обоим провайдерам параллельно, но это только если вы помешаны на задержках, поскольку это просто удвоит ваши затраты.
00:03:26Некоторые похожие паттерны схемотехники с защитой от сбоев применимы и здесь, к LLM.
00:03:32Если вы знаете, что ваш основной провайдер какое-то время сбоит, пробовать его снова бессмысленно.
00:03:40Вы убираете его из балансировщика нагрузки или пути запроса, отправляете на охлаждение, а через несколько минут пробуете вернуть обратно.
00:03:51Интересный выбор, который вам предстоит сделать, — где именно хранить счетчики сбоев.
00:03:59Вы можете хранить счетчики сбоев в памяти на тех экземплярах, которые обслуживают трафик, либо использовать общую информацию, доступную для всего пула.
00:04:12Здесь есть свои плюсы и минусы.
00:04:14Если вам нужны быстрые переключения, помогает статистика по всему пулу.
00:04:19А при использовании локальных счетчиков состояния экземпляра проблема в том, что при каждом изменении размера развертывания меняются ваши конфигурации и ожидания.
00:04:30Так что об этом стоит подумать.
00:04:34То чистая схема на самом деле не показала некоторые другие подводные камни, о которых я расскажу.
00:04:39Резервные копии и фоллбеки не так прозрачны.
00:04:41Хотя индустрия и сходится на едином формате, совместимом с OpenAI API, я бы сказал, что нюансы все еще остаются.
00:04:49Поэтому вам нужно тщательно тестировать резервные механизмы.
00:04:52У них могут быть отличия в схемах вызова инструментов, лимитах токенов, причинах остановки генерации и многом другом.
00:04:58Поэтому со шлюзами LLM можно использовать уровень нормализации, который гарантирует корректное переключение между разными провайдерами.
00:05:08Еще один момент — потоковая передача (стриминг).
00:05:15По сути, никто не хочет ждать 30 секунд, пока перед ним вырастет стена текста.
00:05:22Поэтому существуют сценарии, где стриминг абсолютно необходим.
00:05:26Но он обходится дорого.
00:05:27Вы жертвуете своими рычагами управления.
00:05:29Вы не можете — как только вы решили работать с Провайдером А, вы обязаны продолжать работу с ним.
00:05:36Нельзя сменить провайдера посреди потока.
00:05:39Все, что отправлено клиенту, уже отправлено.
00:05:42И именно поэтому появляется сообщение «Что-то пошло не так».
00:05:46Вы видите именно его.
00:05:48И дело тут вовсе не в лени.
00:05:49Такое поведение заложено архитектурно.
00:05:52И это один из компромиссов.
00:05:54Я хотел бы отметить еще одну вещь, на которой команды спотыкаются снова и снова.
00:06:00Они отлично настраивают и тестируют своих основных провайдеров.
00:06:05Но второй провайдер, резервный, зачастую получает куда меньше внимания.
00:06:11Хотя я бы утверждал, что показатели пропускной способности, емкости и запас прочности для резервного провайдера должны быть еще выше.
00:06:21Потому что это ваш последний рубеж обороны.
00:06:23Если упадет он, упадет и все ваше приложение.
00:06:29Давайте обсудим задержки.
00:06:31Проблемы с доступностью бьют прямо в лоб.
00:06:34Все падает.
00:06:36Срабатывают алармы.
00:06:37Инженерам высылают пейджеры.
00:06:38Но высокие задержки могут действовать скрытно и тихо.
00:06:42И им нужно уделять больше внимания, чем простой настройке служб на одну лишь доступность.
00:06:49Стоит упомянуть об одной детали.
00:06:54Шлюз может обрабатывать смешанные рабочие нагрузки.
00:06:58У вас могут быть запросы на эмбеддинги, которые выполняются меньше секунды.
00:07:04Могут быть запросы классификации, занимающие менее секунды.
00:07:07Есть чат-запросы, которые длятся три секунды.
00:07:10И запросы на рассуждения (reasoning), занимающие много времени.
00:07:13Поднимите руки.
00:07:15Поднимите руки те, кто измеряет общую совокупную задержку для всего сервиса целиком.
00:07:20Что ж, это был вопрос с подвохом.
00:07:23Простите.
00:07:24Вам не следует этого делать.
00:07:25Это не имеет смысла.
00:07:26Это искажение фактов.
00:07:27Вы должны отслеживать показатель P99 для каждой конкретной модели и каждого маршрута, а не усредненное по всему шлюзу число.
00:07:32Общая цифра по шлюзу бесполезна, особенно если вы обрабатываете разнородные рабочие нагрузки.
00:07:36И я надеюсь, что те, кто поднял руки, так на самом деле не делают.
00:07:40Еще одна вещь, важность которой невозможно переоценить — это настройка тайм-аутов для каждого класса моделей и каждого маршрута.
00:07:49Именно в этом кроется первопричина ваших скрытых сбоев.
00:07:54Если у вас нет тайм-аута, шлюз считает, что запрос успешно обрабатывается, хотя на самом деле это не так.
00:08:00И в завершение темы задержек я оставлю вам такую мысль.
00:08:05Нормальное время отклика модели рассуждений — это фактически сбой для обычного чат-бота.
00:08:09Поэтому вам определенно нужно отслеживать задержку для каждого маршрута.
00:08:13Хорошо, это самый болезненный слайд, доставивший мне больше всего хлопот: модели рассуждений и модели-маршрутизаторы.
00:08:24Именно здесь задержка становится по-настоящему непредсказуемой.
00:08:29Модели рассуждений обладают высокой недетерминированностью, гораздо большей, чем привычные модели.
00:08:40Во многих случаях вы не можете установить температуру на ноль.
00:08:43И один и тот же промпт может обрабатываться от двух до 60 секунд.
00:08:48Мы видели в продакшене, как показатель P99 внезапно подскакивал до 60 секунд без видимых на то причин.
00:08:53Хотя волшебного решения тут нет, я рекомендую начать хотя бы с фиксации уровня рассуждений для каждого маршрута.
00:09:03Что касается моделей-маршрутизаторов, они скрывают эту абстракцию от вас.
00:09:08Например, они сами выбирают, какие модели запустить.
00:09:11И я настоятельно рекомендую сделать запросы настолько детерминированными, насколько это возможно в рамках недетерминированной системы.
00:09:23Еще одна идея — страховка «хвоста» распределения.
00:09:26Вы можете отправить параллельный запрос, если ваш основной запрос уже исчерпал, скажем, 90 процентов вашего бюджета задержки.
00:09:37Это действительно поможет сгладить хвостовые задержки P99 для ваших сервисов.
00:09:44Хорошо, это одна из моих любимых тем.
00:09:47Чтобы защитить ваши модели, необходимы защитные экраны (guardrails).
00:09:53Защитные фильтры нужны для предотвращения атак инъекций промптов, фильтрации персональных данных (PII), подавления токсичности и пресечения попыток языковых моделей грубить вашим клиентам.
00:10:08И все в таком духе.
00:10:11Но, как и у провайдера моделей, здесь тоже есть свои компромиссы.
00:10:15Защитные фильтры — это просто еще один сервис.
00:10:18Который может упасть.
00:10:19Который может оказаться ненадежным.
00:10:21И тут вам приходится делать выбор.
00:10:24Вы пропускаете запрос при сбое (fail open) или блокируете его (fail close)?
00:10:27Когда я говорю «пропускать при сбое», вы все равно можете обслужить запрос, даже если защитные фильтры недоступны.
00:10:32При блокировке вы отклоняете запрос и говорите: извините, сервис недоступен.
00:10:36В определенной степени это компромисс между доступностью и безопасностью.
00:10:41Универсального ответа нет, всё зависит от вашего сценария использования.
00:10:45Вы можете решить, например, что если фильтр токсичности не работает, запрос всё равно можно обработать.
00:10:53Поэтому выбором по умолчанию должен быть худший сценарий, с которым вы можете смириться.
00:11:02Есть несколько способов улучшить поведение систем при сбоях защитных экранов и справиться с их ненадежностью.
00:11:17Первое — это временной бюджет.
00:11:20Время выполнения вашего запроса никогда не должно зависеть от работы защитных экранов.
00:11:25Фактором, определяющим скорость, всегда должна быть языковая модель.
00:11:29Поэтому убедитесь, что у вас настроены тайм-ауты, а защитные экраны работают в рамках определенного временного бюджета.
00:11:38Еще одна важная вещь — резервные варианты.
00:11:40Вы наверняка слышали, и я об этом говорил, мы всегда обсуждаем резервные варианты применительно к провайдерам моделей.
00:11:47Но защитные экраны — это тоже критически важные сервисы, для которых можно предусмотреть резервные каналы, запасных провайдеров, дополнительные проверки и кэширование решений, чтобы сохранить доступность сервиса при сбое защитного экрана.
00:12:03Еще один интересный вопрос, возникающий в отношении защитных экранов, — это их размещение.
00:12:10Обычно защитный экран можно разместить тремя способами.
00:12:15Вы можете использовать предварительный перехватчик, который проверяет входные данные.
00:12:19Это, пожалуй, самый безопасный вариант, но он добавляет последовательную задержку вашим запросам.
00:12:26Второй вариант — параллельная работа.
00:12:29Это один из моих любимых способов, но стоит учесть, что потоковая передача здесь работать не будет.
00:12:35Поэтому, если вы генерируете структурированный вывод, пожалуйста, не используйте потоковую передачу.
00:12:40Старайтесь экономить время и запускать эти защитные экраны параллельно для структурированных данных.
00:12:46Еще один вариант — последующие перехватчики.
00:12:48Они лучше всего подходят для мониторинга результатов, аудита вывода и тому подобного.
00:12:58Итак, до сих пор мы обсуждали все возможные проблемы, связанные с нашими зависимостями.
00:13:06Мы еще не упомянули, что фактически добавляем еще одну зависимость в сам путь запроса — центральный шлюз языковых моделей.
00:13:15С ним связано несколько проблем, на которых мы обожглись, и я хочу поделиться этими уроками, если вы работаете над таким шлюзом или используете его.
00:13:25Первое — это общие лимиты.
00:13:28Убедитесь, что ваши ключи API разделены для каждого маршрута и каждого сценария использования настолько детально, насколько это вообще возможно.
00:13:40Наличие «шумного» соседа может стать одной из самых больших проблем.
00:13:47Второе — сброс нагрузки.
00:13:50Убедитесь в рамках ваших инструкций и учений, что используемый вами шлюз поддерживает сброс нагрузки.
00:13:59Потому что при шквале повторных запросов масштабировать систему становится невероятно трудно.
00:14:03Нельзя просто так взять и масштабировать сервисы, находящиеся под волной повторных запросов.
00:14:07У всех этих веб-серверов есть внутренняя очередь, и ее можно настраивать.
00:14:13Убедитесь, что размер очереди ограничен и они не принимают запросы бесконечно.
00:14:19Если вам нужна особая логика, вы даже можете настроить приоритезацию трафика, чтобы гарантировать качественное обслуживание самых важных сценариев при высокой нагрузке.
00:14:29Последнее, что я хочу обсудить, — это саму концепцию центрального шлюза.
00:14:38Это единая точка отказа.
00:14:40Поэтому, если вы планируете создать единый шлюз для всей компании под пару языковых моделей, я бы порекомендовал пересмотреть это решение и взвесить причины.
00:14:52Я заметил, что в большинстве случаев людям нужен вовсе не центральный шлюз.
00:14:57Им требуется централизованное управление.
00:15:00Существует подход, позволяющий децентрализовать шлюз и при этом сохранить централизованное управление.
00:15:07Поэтому не пытайтесь централизовать весь трафик через один узел, лучше используйте плагины и кастомный код для централизации управления.
00:15:17Управление может выражаться в отслеживании затрат, управлении лимитами и других возможных решениях.
00:15:24Изучите эти варианты, прежде чем внедрять единственный центральный шлюз для всей компании.
00:15:30Им может управлять одна команда, но я бы не рекомендовал развертывать его как единое целое для всей компании, даже если он распределенный.
00:15:43В завершение я хочу сказать несколько слов личного характера.
00:15:47Сегодня день рождения моего сына, а я стою здесь и рассказываю незнакомцам об ограничении сбоев.
00:15:54Так что всё, что вы можете для меня сделать, — пожалуйста, предотвратите хотя бы один инцидент ради меня и ваших клиентов.
00:16:02Спасибо.
00:16:03Если у вас есть вопросы, да.
00:16:06.

Key Takeaway

Создание надежного шлюза LLM требует балансировки между доступностью, задержками, стоимостью и защитными барьерами с помощью маршрутизации запросов, детального мониторинга P99 и продуманных резервных механизмов.

Highlights

  • Шлюз LLM выступает в роли промежуточного слоя между приложениями и провайдерами моделей, управляя маршрутизацией, доступностью, задержками, защитными барьерами и стоимостью.

  • Слепые повторные попытки запросов к языковым моделям умножают расходы и увеличивают хвостовые задержки, поэтому шлюзы используют резервное переключение для каждого отдельного запроса.

  • Для разнородных рабочих нагрузок отслеживание общей совокупной задержки шлюза не имеет смысла, вместо этого требуется мониторинг показателя P99 для каждого конкретного маршрута.

  • Модели рассуждений обладают высокой недетерминированностью, при которой один и тот же промпт обрабатывается от двух до 60 секунд, вызывая непредсказуемые пики задержки.

  • Размещение защитных экранов в параллельном режиме экономит время при обработке структурированных данных, но полностью исключает использование потоковой передачи.

  • Единый центральный шлюз для всей компании становится критической точкой отказа, поэтому на практике эффективнее децентрализовать маршрутизацию трафика при сохранении централизованного управления.

Timeline

Архитектура и базовые факторы шлюза LLM

  • Шлюз LLM работает как промежуточный слой между приложениями и провайдерами моделей.
  • Основная задача шлюза заключается в балансировке четырех факторов: доступности, задержки, защитных барьеров и стоимости.

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

Обеспечение доступности и механизмы резервного переключения

  • Традиционные слепые повторные попытки быстро исчерпывают лимиты времени и увеличивают расходы на LLM.
  • Оптимальным решением является резервное переключение для каждого отдельного запроса на резервный провайдер.

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

Контроль задержек и специфика моделей рассуждений

  • Измерение общей задержки для всего сервиса целиком искажает реальную картину при смешанных рабочих нагрузках.
  • Модели рассуждений демонстрируют высокую недетерминированность с разбросом времени ответа от двух до 60 секунд.

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

Интеграция защитных экранов и компромиссы безопасности

  • Защитные экраны предотвращают инъекции промптов, фильтруют персональные данные и подавляют токсичность.
  • Размещение защитных экранов в параллельном режиме ускоряет обработку структурированных данных ценой отказа от стриминга.

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

Уроки продакшена, лимиты и децентрализация шлюзов

  • Детальное разделение ключей API по маршрутам защищает систему от проблем с шумными соседями.
  • Развертывание единого центрального шлюза для всей компании создает опасную единую точку отказа.

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

Community Posts

View all posts