Keystone убрал имя модели из кода: open-source LLM-шлюз добавляет 0,64 мс

Команда Keystone открыла код LLM-шлюза The Model Switchboard (KTMS) на Go под лицензией Apache 2.0. Микросервис больше не знает имя модели: он передаёт только уровень сложности LOW, MEDIUM или HIGH и произвольные атрибуты, а правила маршрутизации лежат в Postgres. Шлюз сам идёт по цепочке фолбэков, размыкает circuit breaker после 5 неудач подряд и заранее резервирует в Redis худший случай стоимости вызова. Добавленная задержка на стенде — около 0,64 мс на p50 и 1,1 мс на p99. • Режим model: "auto" отдаёт оценку сложности модели-судье с дедлайном 250 мс • Batch через API вендоров со скидкой 50% • Лимиты в нано-долларах на ключ, пользователя и команду

AI-обработка оригинала Habr AI; редакция Hamidun News
Keystone убрал имя модели из кода: open-source LLM-шлюз добавляет 0,64 мс
Источник: Habr AI. Коллаж: Hamidun News.
Слушать статью
Паспорт материала и цифры
ПараметрДанные
ПервоисточникHabr AI ↗
Главный редактор
Режим model"auto" отдаёт оценку сложности модели-судье с дедлайном 250 мс
Ключевой факт #2Batch через API вендоров со скидкой 50%
Ключевой факт #3Лимиты в нано-долларах на ключ, пользователя и команду

Самая дорогая строчка в микросервисе с LLM — model="gpt-4o". За ней тянутся свой ключ вендора, свои ретраи и счёт, который некому разобрать. Команда Keystone выпустила open-source шлюз The Model Switchboard (KTMS) на Go под Apache 2.0, который убирает эту строчку целиком. Сервис говорит шлюзу, насколько трудна задача, а какая модель ответит, решают правила. Накладные расходы на стенде: около 0,64 мс на p50 и 1,1 мс на p99 при 200 RPS.

Сорок сервисов и одно имя модели

Типичная история такова: первый сервис получает ключ OpenAI, через полгода таких сервисов сорок, и у каждого свой секрет, своя обёртка ретраев и свой подсчёт токенов. Дальше всё ломается предсказуемо. Вышла модель лучше или дешевле: это сорок pull request'ов и сорок деплоев, причём половина команд обновится через квартал. Вендор лёг: падают все сорок сервисов, потому что фолбэк либо не написан, либо написан по-разному. Пришёл счёт: ключ общий, метаданных у вызовов нет, понять, кто потратил деньги, нельзя.

В Keystone сервис передаёт effort (LOW, MEDIUM или HIGH) и атрибуты: язык, тип задачи, пользователя. Для шлюза атрибуты — просто ключи и значения, которые сравнивают правила. Клиентам на Python не нужен даже gRPC: OpenAI-совместимый HTTP понимает те же правила. Effort кодируется в имени модели (auto:high), metadata превращается в атрибуты, а поле user идёт в лимиты.

Правила, канарейки и судья на 250 мс

Правила хранятся в Postgres и заменяются целиком одним атомарным вызовом. Побеждает подходящее правило с наибольшим приоритетом, а поле use задаёт цепочку фолбэков, по соглашению от дешёвой модели к дорогой. Валидация не принимает набор, в котором для какого-то effort нет правила без атрибутов, так что запрос не может проскочить мимо всех правил. Реплики подхватывают изменения в течение 60 секунд. Команда ktms route explain показывает маршрут заранее, без вызова модели.

Правилам можно задать веса, например [90, 10]. При наличии user_id или canary_key распределение «липкое»: один пользователь всегда попадает на одну модель, поэтому новую модель выкатывают на 10% аудитории без правок в сервисах. Есть и экспериментальная shadow-маршрутизация: доля трафика асинхронно дублируется на кандидата, а судья сравнивает ответы и пишет результат в метрики.

Если сервис не знает сложность, включается model: "auto". Шлюз спрашивает модель-судью, которая возвращает не текст, а типизированный ответ с вероятностями. Поддерживаются Jev от TypeSafe и OpenAI Decisions. Дедлайн жёсткий, 250 мс; при сбое или медленном судье запрос уходит с MEDIUM. Закреплённая сложность всегда приоритетнее судьи, а выбор виден в заголовках X-KTMS-Route-Mode, X-KTMS-Judge-Score и X-KTMS-Judge-Model. Выбор судьёй в ветке 1.x помечен как экспериментальный, стабилен только auto:high и его аналоги.

Отказы вендоров и деньги под контролем

Встроенные ретраи SDK вендоров отключены: одна попытка роутера равна одному вызову вендора. Роутер повторяет 429, 5xx или обрыв один раз на той же модели, затем идёт по цепочке; для стриминга это работает до первого чанка. Механизмы защиты:

  • Circuit breaker на пару (вендор, модель): после 5 подряд неудач цепь размыкается, через 30 с один пробный запрос проверяет модель.
  • Retry-After, retry-after-ms и google.rpc.RetryInfo у Gemini: ожидание до 1 с выдерживается на месте, более долгое ставит модель на паузу для конкретного ключа (максимум 5 минут по умолчанию).
  • Если на паузе вся цепочка, клиент получает UNAVAILABLE vendors_unavailable с честным Retry-After.

Лимиты расходов задаются в долларах на ключ и пользователя за UTC-сутки или месяц, плюс месячный бюджет команды. Перед каждым вызовом шлюз резервирует худший случай (вход × цена входа + максимум токенов × цена выхода) одним Lua-скриптом в Redis, после ответа пересчитывает в фактическую стоимость. Суммы хранятся в нано-долларах. Если вызов пересёк бы лимит, он отклоняется, но более дешёвая модель дальше по цепочке ещё может ответить. Без max_output_tokens резерв считается по 4096 токенам, поэтому параметр стоит задавать всегда.

Остальные возможности:

  • Организации и команды со списками разрешённых моделей и месячными бюджетами; org_id и team_id проставляет шлюз по ключу.
  • Batch через API вендоров со скидкой 50%, идемпотентная отправка по client_batch_id, промпты в базе не хранятся.
  • Лимит запросов GCRA с настраиваемым burst и суточные токен-бюджеты с допуском 5%.
  • Событие на каждый вызов в light-tokenmeter или любой gRPC-сервис учёта.

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

Провайдеры: OpenAI, Anthropic, Gemini, DeepSeek, Groq, Mistral, Azure OpenAI, Bedrock, любые OpenAI-совместимые серверы и TypeSafe для решений. Экзотического вендора может не оказаться. По задержке Keystone уступает agentgateway, Apache APISIX и Kong. Стенд: Google Cloud c3-standard-4 с 4 vCPU и mock-апстримом, шлюз держал не меньше 4000 RPS. Без Redis лимиты не работают, а при его отказе лимиты запросов и токенов пропускают трафик. Для денежных лимитов можно включить строгий режим SPEND_LIMITS_FAIL_MODE=closed.

Практический вывод для команд с десятками LLM-сервисов: начните с трёх правил (LOW, MEDIUM, HIGH без атрибутов) и цепочек от дешёвой модели к дорогой, потом добавляйте атрибутные правила. Сразу задайте max_output_tokens и долларовые лимиты на ключи, чтобы счёт стал разбираемым. Схему можно повторить и в другом шлюзе, главное — вынести имя модели из кода сервисов в конфигурацию.

Сюжет

Вопросы и ответы по материалу

Что известно по теме: сорок сервисов и одно имя модели?

Типичная история такова: первый сервис получает ключ OpenAI, через полгода таких сервисов сорок, и у каждого свой секрет, своя обёртка ретраев и свой подсчёт токенов. Дальше всё ломается предсказуемо. Вышла модель лучше или дешевле: это сорок pull request'ов и сорок деплоев, причём половина команд обновится через квартал. Вендор лёг: падают все сорок сервисов, потому что фолбэк либо не написан, либо написан по-разному. Пришёл счёт: ключ общий, метаданных у вызовов нет, понять, кто потратил деньги, нельзя. В Keystone сервис передаёт effort (LOW, MEDIUM или HIGH) и атрибуты:

Что известно по теме: правила, канарейки и судья на 250 мс?

Правила хранятся в Postgres и заменяются целиком одним атомарным вызовом. Побеждает подходящее правило с наибольшим приоритетом, а поле use задаёт цепочку фолбэков, по соглашению от дешёвой модели к дорогой. Валидация не принимает набор, в котором для какого-то effort нет правила без атрибутов, так что запрос не может проскочить мимо всех правил. Реплики подхватывают изменения в течение 60 секунд. Команда ktms route explain показывает маршрут заранее, без вызова модели. Правилам можно задать веса, например [90, 10]. При наличии user_id или canary_key распределение «липкое»:

Что известно по теме: отказы вендоров и деньги под контролем?

Встроенные ретраи SDK вендоров отключены: одна попытка роутера равна одному вызову вендора. Роутер повторяет 429, 5xx или обрыв один раз на той же модели, затем идёт по цепочке; для стриминга это работает до первого чанка. Механизмы защиты: Circuit breaker на пару (вендор, модель): после 5 подряд неудач цепь размыкается, через 30 с один пробный запрос проверяет модель. Retry-After, retry-after-ms и google.rpc.RetryInfo у Gemini: ожидание до 1 с выдерживается на месте, более долгое ставит модель на паузу для конкретного ключа (максимум 5 минут по умолчанию). Если на паузе вс

ЖХ
Жемал Хамидун — CPO Alpina Digital, продакт-менеджер AlpinaGPT. Делает AI-продукты с 2019 года. Hamidun News — pet project, который вырос в production-агрегатор с 6000+ читателей.
✦ Единое AI-пространство · Nomad

Протестируйте новые LLM и мультимодальные модели в одном окне

Платформа Nomad AI (nomadnet.ai) даёт прямой доступ к Claude Opus 5.5, GPT-6, Gemini 3.1, DeepSeek и автономным агентам без VPN, зарубежных карт и зоопарка подписок.

Открыть Nomad AI →Автономный Nomad Agent
Что вы думаете?
Загружаем комментарии…