Перейти к содержанию
Публикация AiManual

Self-service LLM-шлюз на Amazon Bedrock и LiteLLM: архитектура, изоляция квот и контроль расходов

Разбираем, как собрать self-service LLM-шлюз на Amazon Bedrock и LiteLLM: поток запроса через Route 53, CloudFront и ALB, изоляция квот через Application Infere

Коротко

Что будет в материале

  1. 01

    Что такое self-service LLM-шлюз и кому он нужен

  2. 02

    Архитектура и поток запроса: от клиента до Bedrock

  3. 03

    Изоляция квот и IAM: Application Inference Profiles и CDK

  4. 04

    Контроль расходов: почему бюджеты не срабатывают и как это исправить

Self-service LLM-шлюз на Amazon Bedrock и LiteLLM собирается из предсказуемых частей: прокси на AWS Fargate, управляемая база для ключей и расходов, кэш, менеджер секретов и Application Inference Profiles в Bedrock. Разработчик получает один base URL и персональный API-ключ. Платформа получает контроль доступа и учёт затрат по пользователям. Состав компонентов и поток запроса AWS описывает в материале про self-service LLM gateway на Bedrock и LiteLLM.

Ломается такая схема чаще всего в двух местах. Первое: IAM-политика, где есть только ARN inference-профиля, но нет ресурсов базовой модели, и вызовы падают с AccessDenied. Второе: незаполненные cost-поля в конфигурации модели, из-за чего LiteLLM не может корректно посчитать расход, а бюджеты не срабатывают. Оба сюжета разобраны ниже, с чек-листами проверки.

Что такое self-service LLM-шлюз и кому он нужен

Паттерн простыми словами. LiteLLM работает на задаче AWS Fargate и выставляет foundation-модели Amazon Bedrock за OpenAI-совместимым API. Разработчик направляет свой инструмент на один эндпоинт и аутентифицируется персональным ключом, с самим Bedrock он не работает. Платформа видит расход по каждому пользователю и поведение моделей под нагрузкой.

Какие боли закрываются. Разработчику нужен привычный OpenAI-совместимый клиент и ключ, а не тикет в платформенную команду на каждый новый сервис. Платформе нужны контроль доступа, раздельные квоты по моделям и учёт затрат, причём по людям, а не по общему счёту аккаунта. Шлюз закрывает обе задачи одним сервисом.

В примере из исходного материала фигурируют Claude Sonnet и Claude Haiku. Это иллюстрация, а не рекомендация: набор моделей определяется задачами и доступностью в вашем регионе.

Границы применимости стоит обозначить сразу. Для команды из трёх человек с одним потребителем моделей схема избыточна: CloudFront, ALB, RDS, ElastiCache и Secrets Manager - это постоянная операционная нагрузка и отдельный счёт. Шлюз оправдан, когда потребителей много, у них разные бюджеты, а доступ к Bedrock напрямую выдавать не хочется.

Разработчик и платформа: две стороны одной схемы

Граница ответственности проходит по порталу LiteLLM. Это лёгкое веб-приложение для администраторов и разработчиков, которое может интегрироваться с корпоративным SSO. Операционные инженеры через портал задают политику расходов. Разработчики в этих рамках сами создают API-ключи, без обращения в платформенную команду.

Что остаётся на стороне платформы: состав доступных моделей, лимиты на ключ, IAM-периметр задачи Fargate, алерты по RPM и TPM. Что уходит разработчику: выбор модели в пределах разрешённых, свой ключ, свой бюджет и метрики по своим вызовам. Такое разделение снимает типовой конфликт, когда за скорость выдачи доступа отвечает один человек и он же становится узким местом.

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

Архитектура и поток запроса: от клиента до Bedrock

Запрос разработчика идёт по фиксированному маршруту. OpenAI-совместимый клиент обращается к шлюзу через Amazon Route 53, Amazon CloudFront и Application Load Balancer. На ALB запрос попадает в LiteLLM на Fargate, где аутентифицируется персональным API-ключом. Дальше LiteLLM обращается к Amazon RDS (PostgreSQL) за состоянием виртуальных ключей и расходов, к Amazon ElastiCache for Valkey как к кэшу и к AWS Secrets Manager за секретами. Вызов модели идёт в Amazon Bedrock Application Inference Profiles через scoped AWS IAM task role, а Amazon CloudWatch фиксирует метрики запросов в минуту (RPM) и токенов в минуту (TPM) и квотный алерт. Так этот поток описан в первоисточнике.

Задача LiteLLM на Fargate масштабируется по target-tracking политике на загрузку CPU. Практический вывод: пики трафика на шлюзе приходят вместе с ростом числа одновременных стримов, поэтому политику стоит привязать к наблюдаемому поведению, а не к среднему за сутки.

КомпонентРоль в схеме
Amazon Route 53DNS-имя шлюза, на которое настроены клиенты
Amazon CloudFrontCDN-слой на входном контуре перед ALB
Application Load BalancerБалансировка запросов между задачами LiteLLM
LiteLLM на AWS FargateАутентификация по персональному ключу, OpenAI-совместимый API, политика расходов
Amazon RDS (PostgreSQL)Состояние виртуальных ключей и расходов
Amazon ElastiCache for ValkeyКэш
AWS Secrets ManagerСекреты шлюза
Amazon Bedrock Application Inference ProfilesВызов моделей с изоляцией квот
Amazon CloudWatchМетрики RPM и TPM, квотный алерт

Входной контур: Route 53, CloudFront и ALB

Три компонента на входе решают разные задачи. Route 53 даёт стабильное DNS-имя, которое прописывается в инструментах разработчиков и не меняется при пересборке инфраструктуры. CloudFront стоит перед ALB как CDN-слой. ALB распределяет запросы по задачам LiteLLM и выводит из балансировки нездоровые.

Убирать что-то из этой цепочки можно, но осознанно. Без CloudFront останется прямая связка Route 53 и ALB, что проще и дешевле для внутреннего контура. WAF, терминация TLS и правила кэширования на уровне CloudFront - это общие места архитектуры, а не то, что описано в исходном паттерне. Ответы LLM кэшировать на CDN обычно не стоит: контекст запроса уникален, а политика хранения таких ответов требует отдельного решения.

Состояние и секреты: RDS PostgreSQL, ElastiCache for Valkey, Secrets Manager

RDS PostgreSQL хранит состояние виртуальных ключей и расходов LiteLLM. Это источник правды для бюджетов: если запись о расходе не обновилась, никакой лимит не сработает. Отсюда практическое следствие, ошибки в cost-полях конфигурации модели бьют именно по бюджетам, а не по доступности сервиса. Сервис продолжит отвечать, просто бесплатно с точки зрения учёта.

ElastiCache for Valkey работает как кэш LiteLLM. Не путайте его с кэшированием префикса промпта на стороне модели: это разные механизмы с разной экономикой. Про второе есть отдельный разбор, prompt caching для AI-агентов на Amazon Bedrock.

Secrets Manager держит секреты шлюза: ключи доступа к провайдерам, строку подключения к базе, служебные токены. Схемы таблиц и версии LiteLLM в исходном материале не раскрываются, поэтому планируйте миграции базы как обычную задачу эксплуатации, с бэкапами и проверкой восстановления.

Вызов модели и наблюдаемость: Application Inference Profiles и CloudWatch

LiteLLM вызывает Amazon Bedrock Application Inference Profiles от имени scoped IAM task role. Профиль задаёт отдельную единицу квоты, поэтому лимиты по разным моделям не смешиваются в общий аккаунтный котёл.

CloudWatch собирает RPM и TPM и обслуживает квотный алерт. Эти две метрики закрывают базовый вопрос: упираемся ли мы в лимит модели или в собственные настройки шлюза. Телеметрия на уровне промптов, например через Langfuse, даёт другой разрез, качество и содержание запросов, но деталей интеграции в исходном материале AWS нет. Считайте это точкой расширения, которую нужно проектировать отдельно.

Изоляция квот и IAM: Application Inference Profiles и CDK

Application Inference Profiles нужны, когда у моделей разные лимиты, а потребителей много. Один общий счётчик на аккаунт означает, что тяжёлый эксперимент одного разработчика съедает квоту у всей команды. Профиль на модель (или на пару модель плюс команда) разводит эти потоки и делает алерты осмысленными.

CDK-конструкции в исходном материале иллюстративные. Из них собирается собственная версия паттерна, готового продакшен-развёртывания там нет.

Почему одного ARN профиля в IAM недостаточно

Ситуация типичная: в политике роли задачи указан ARN inference-профиля, запрос всё равно отклоняется с AccessDenied. Причина в том, что при вызове через профиль проверка прав идёт не только на ресурсе профиля, но и на ресурсах базовой модели, которую профиль маршрутизирует. Политика, покрывающая только ARN профиля, этот случай не закрывает.

Для моделей, которые вызываются через inference profile, а не напрямую по ARN foundation-модели, IAM-политика должна разрешать вызов и на ресурсе inference profile, и на ресурсе базовой модели. Если разрешён только один тип ресурса, запрос завершается с AccessDeniedException. Кроме разрешения на вызов модели, для работы с inference profile может потребоваться действие bedrock:GetInferenceProfile.

Отдельный случай - кросс-региональный вызов. AccessDeniedException возникает, когда у IAM-роли нет прав на вызов API в целевых регионах, куда профиль направляет запрос. Bedrock при cross-region inference автоматически выбирает регион в пределах выбранной географии, включая регионы, которые не были включены для ресурсов и IAM-данных. Поэтому в политике нужно учитывать ресурсы регионов, куда профиль может маршрутизировать запрос, а в организациях с SCP - расширять и сами Service Control Policies, чтобы cross-region inference не требовал отдельного управления каждым новым регионом.

Чек-лист проверки, когда вызов через профиль не проходит:

  • в policy перечислены ресурсы базовой модели, а не только ARN профиля;
  • добавлено действие bedrock:GetInferenceProfile, если оно требуется для вашего способа вызова;
  • регион и аккаунт в ARN совпадают с теми, где реально работает задача Fargate;
  • нет явного Deny в другой политике, прикреплённой к той же роли или к boundary-политике;
  • для кросс-регионального вызова (Global CRIS) в политике учтены ресурсы тех регионов, куда профиль может увести запрос, и при необходимости расширены SCP.

Точный набор action и resource зависит от модели и способа вызова, поэтому сверяйте политику с документацией по своей модели, а не копируйте JSON из чужой статьи. Про региональные и IAM-нюансы при работе с новыми моделями в Bedrock есть отдельный материал, Claude Fable 5.1 в AWS Bedrock.

Практика CDK: что параметризовать

Смысл CDK здесь в том, чтобы новая модель добавлялась параметром, а не правкой пяти стеков. Разумный минимум для параметризации:

  • список моделей, их профилей и cost-значений в одном месте;
  • IAM-ресурсы базовых моделей, выводимые из этого же списка;
  • переменные окружения LiteLLM, включая base URL и настройки кэша;
  • секреты в Secrets Manager, а не в переменных окружения задачи;
  • размер задачи Fargate и политики Auto Scaling;
  • алиасы моделей, которые видят разработчики, отдельно от внутренних ARN.

Разделение внешнего имени модели и внутреннего ARN позволяет менять профиль без перевыпуска ключей у потребителей.

Контроль расходов: почему бюджеты не срабатывают и как это исправить

Механика отказа простая. LiteLLM считает стоимость запроса из количества токенов и цены за токен, заданной в model_info. Для расчёта используются параметры input_cost_per_token и output_cost_per_token. Если модель подключена по inference-profile ARN и эти поля не заполнены, LiteLLM не получает цену за токен и не может корректно отнести стоимость вызова к spend. Дальше цепочка разворачивается сама: расход не растёт, бюджет не превышен, лимит не срабатывает, ответ 429 не приходит. Деньги тратятся, счётчик стоит.

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

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

Чек-лист по бюджетам:

  1. Для каждой модели и каждого профиля заполнены input_cost_per_token и output_cost_per_token.
  2. Цены соответствуют актуальному прайсу провайдера и модели, а не скопированы с прошлогодней конфигурации.
  3. Бюджеты привязаны к ключам, командам и пользователям, а не только к общему аккаунту.
  4. Тестовый запрос реально увеличивает spend в интерфейсе LiteLLM.
  5. При превышении бюджета приходит именно 429, а не молчаливый пропуск запроса.

Пятый пункт проверяется один раз и вручную: поставьте заведомо крошечный лимит, сделайте несколько вызовов и убедитесь, что шлюз начал отказывать. Без такой проверки вы не знаете, работает контроль или создаёт видимость.

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

Подводные камни эксплуатации: таймауты, fallback, CI/CD и память Fargate

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

Таймауты и extended thinking

Режим extended thinking заставляет модель рассуждать до выдачи ответа, поэтому ответ приходит позже, чем при обычном вызове. Дефолтные таймауты клиента, ALB и LiteLLM проектировались под обычные ответы и могут обрывать такие запросы. Точные значения задержек зависят от модели и длины рассуждения, поэтому ориентируйтесь на замеры на своей нагрузке.

В LiteLLM таймаут LLM-запроса настраивается в секундах через litellm.request_timeout: по его истечении возникает Timeout error. Правило согласования: самый длинный таймаут ставится на самом медленном участке, иначе верхний уровень рвёт соединение раньше, чем нижний успевает ответить. Проверьте три места: настройку HTTP-клиента в инструменте разработчика, idle timeout балансировщика и таймаут запроса в LiteLLM. Для extended thinking ориентируйтесь на фактическое распределение длительности ответов, а не на среднее значение.

Повторные попытки вместо fallback

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

Retry на той же модели предсказуемее: временная ошибка или троттлинг лечатся повтором, а качество ответа не меняется. Fallback оправдан, когда важнее доступность, чем идентичность результата, и когда переключение видно в логах и метриках. Если fallback всё же нужен, делайте его явным: отдельный флаг в конфигурации, запись в лог с указанием фактической модели, отдельный ключ для сценариев, где подмена допустима. Про то, как аккуратно строить деградацию и оценку качества, есть разбор пяти техник контроля качества LLM на Amazon Bedrock.

Отдельные профили для CI/CD

Автотесты генерируют трафик, который выглядит как пользовательский: те же модели, те же ретраи, иногда те же большие промпты. Если он идёт в общий профиль, тесты выедают квоты и бюджеты продакшена, а разбор инцидента превращается в археологию.

Решение: отдельный Application Inference Profile и отдельный виртуальный ключ для CI/CD. Вы получаете изолированные квоты, отдельный бюджет и чистую атрибуцию трафика: по метрикам сразу видно, чей запрос упёрся в лимит. Цена вопроса - одна дополнительная сущность в конфигурации.

Память Fargate и IAM-описания

Память задачи Fargate подбирается по факту, а не по привычке. Признаки нехватки: задача перезапускается по OOM, стриминговые ответы обрываются на середине, латентность растёт при росте числа одновременных запросов. Смотрите на потребление памяти процессом LiteLLM под нагрузкой, а не на холостом ходу, и закладывайте запас на пики. Заниженная память даёт деградацию, которую легко списать на провайдера модели.

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

Ограничения и что сделать перед продакшеном

Первое ограничение касается лицензирования LiteLLM. Часть функций разграничения доступа и самообслуживания относится к Enterprise-версии: организации как внешний уровень поверх команд и назначение team admin требуют Enterprise-лицензии, тогда как команды остаются верхнеуровневой границей в open source. Полный self-service сценарий, где разработчики сами выдают себе ключи через портал, нужно проверять по актуальной документации LiteLLM для вашей редакции: в бесплатной версии часть таких возможностей недоступна. Либо планируйте лицензию, либо оставляйте выдачу ключей через заявку. Лицензионные условия меняются, сверяйтесь с актуальной страницей проекта перед бюджетированием.

Второе: паттерн не покрывает Amazon Bedrock Guardrails, включая content filtering и grounding. В исходном материале AWS это указано прямо: для простоты изложения такие контроли не рассматриваются, хотя перед продакшеном их обычно включают. Для внутреннего инструмента допустим более мягкий режим. Для клиентского продукта фильтрация и заземление на источники входят в базовые требования.

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

Чек-лист запуска и итог

Список проверок перед тем, как открывать шлюз команде:

  • base URL и персональные ключи работают, клиент разработчика подключается без правок кода;
  • IAM-политика роли задачи включает ресурсы базовой модели и при необходимости bedrock:GetInferenceProfile, проверено вызовом через профиль;
  • для кросс-регионального вызова учтены регионы маршрутизации профиля и расширены SCP;
  • cost-поля заданы для каждой модели и профиля, значения сверены с прайсом;
  • тестовый запрос увеличивает spend, а при превышении бюджета приходит 429;
  • таймауты клиента, балансировщика и LiteLLM согласованы под extended thinking;
  • retry настроен осознанно, fallback либо выключен, либо логируется с фактической моделью;
  • CI/CD вынесен в отдельный профиль и отдельный ключ;
  • память задачи Fargate подобрана по нагрузке, а не по значению из примера;
  • Guardrails и политика хранения логов продуманы до запуска;
  • лицензия LiteLLM Enterprise учтена, если нужны организации, team admin и самостоятельная выдача ключей.

Паттерн даёт реальный контроль доступа и наблюдаемость, но держится на двух скучных вещах: аккуратной IAM-политике и заполненных cost-полях. Без них шлюз продолжит работать и отвечать 200, а контроль расходов останется декорацией. Начните с проверки бюджета на одном ключе: если лимит срабатывает, остальную схему можно масштабировать.

Подписаться на канал