SurfCap-α — алгоритм, которым наш LLM-шлюз выбирает модель, когда клиент присылает model=auto. Простые запросы он отправляет на дешёвую модель за 0.7 мс в среднем, а запросы с персональными или конфиденциальными данными не выпускает за контур: за 19 часов работы текущего процесса шлюза так на резидентных моделях остались 176 из 230 самых рискованных запросов. Решение принимают детерминированные правила, а ML-слои могут только повысить класс модели, но не понизить его.

Если коротко — для бизнеса

  • Дешёвые вопросы — дешёвой модели. Больше половины запросов (56.9%) ушли на дешёвый класс вместо привычного «всё на самую сильную».
  • Данные не уходят наружу. Запросы с персональными или конфиденциальными данными остаются на моделях внутри контура, и никакая оптимизация стоимости это правило не отменяет.
  • Каждое решение объяснимо одной строкой. По заголовку ответа видно, почему выбрана именно эта модель.
  • Честная оговорка: сравнения качества и стоимости с режимами «всегда сильная» и «всегда дешёвая» пока нет — для него ещё не накоплена разметка.

О цикле «LLM-шлюз»

Две статьи о собственном LLM-шлюзе компании. «Мы» — это автор и AI-агенты, которым он делегировал часть исследований и правок. Компания обезличена, цифры настоящие.

  1. SurfCap-α: как семантический роутер выбирает модель ← вы здесь
  2. Guardrails в LLM-шлюзе: почему Shieldstral
Термины за минуту
  • LLM-шлюз — единая точка входа между клиентами (чатами, агентами, интеграциями) и языковыми моделями: маршрутизация, проверки, учёт, трассировка.
  • model=auto — клиент не выбирает модель сам, а поручает выбор шлюзу.
  • Класс модели и тир — класс описывает, какая модель нужна (дешёвая, кодовая, с длинным контекстом, «умная», самая сильная, мультимодальная, резидентная). Тир T0–T3 — уровень требований запроса, из которого выводится класс.
  • Резидентная модель — модель, развёрнутая внутри контура компании. Данные к ней не покидают периметр.
  • ПДн — персональные данные.
  • Routing и cascading — routing решает, какую модель вызвать, до генерации; cascading сначала спрашивает дешёвую модель и при неудачном ответе идёт к дорогой.
  • α-порог — порог на вероятность того, что сильная модель ответит лучше слабой. Выше α — дешевле и рискованнее.
  • Capability-головы — лёгкие классификаторы, оценивающие, нужны ли запросу рассуждение, код, отладка или инструменты.
  • Circuit breaker — предохранитель: сбойный провайдер временно выводится из ротации.
  • Fallback — переключение на запасной вариант при ошибке.
  • p50 — медиана, например задержки.

Задача и исходные условия

Кому особенно полезен раздел:Бизнес и продуктАрхитекторы

SurfCap-α — алгоритм автоматического выбора модели в собственном LLM-шлюзе: клиент присылает model=auto, а шлюз за доли миллисекунды решает, какой класс модели нужен этому запросу. Название — аббревиатура слоёв: Surface heuristics → Capability heads → α-preference, плюс исполнение с перескоками и каскад как страховка.

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

  • Пул из нескольких классов моделей: дешёвая быстрая, кодовая, с длинным контекстом, «умная», самая сильная, мультимодальная — плюс резидентные модели, развёрнутые внутри контура.
  • Часть запросов содержит персональные данные или конфиденциальную информацию. Такие запросы нельзя отправлять внешним провайдерам ни при каких оптимизациях.
  • До шлюза каждый клиент сам выбирал модель. Типичный сбой — «всё на самую сильную»: дорого и медленно на простых вопросах.
  • Первая версия маршрутизации жила на готовом прокси, и его автоматический fallback однажды увёл трафик на дорогой класс. Это стало аргументом за собственный шлюз с явной, объяснимой логикой выбора.

Требования к роутеру сформулировали так:

  • решение до первого HTTP-вызова — меньше 1 мс;
  • каждое решение объяснимо одной строкой;
  • дорогие ML-компоненты опциональны и могут только повышать класс, но не понижать.

Что говорит литература

Кому особенно полезен раздел:ML и исследователиАрхитекторы

Маршрутизация между LLM делится на два семейства: решение до генерации (routing) и решение после неё (cascading). Эта таксономия и каталог дешёвых признаков запроса хорошо изложены в двух обзорах [7] [8].

Routing до генерации. Hybrid LLM [2] обучает лёгкий энкодер предсказывать разрыв качества между малой и большой моделью. RouteLLM [1] сводит задачу к одной величине — вероятности того, что сильная модель ответит лучше слабой, — и порогу α над ней:

route(q)={strong,P(winstrong∣q)≥αweak,иначе\text{route}(q) = \begin{cases} \text{strong}, \quad & P(\text{win}_{\text{strong}} \mid q) \ge \alpha \\ \text{weak}, \quad & \text{иначе} \end{cases}

Ручка α напрямую задаёт компромисс: выше α — дешевле и рискованнее, ниже α — ближе к «всегда сильная». HyDRA [6] идёт дальше двух моделей: четыре сигмоидные «головы способностей» (reasoning, code, debug, tools) поверх ModernBERT сопоставляются с профилями моделей в гетерогенном пуле; медиана на CPU — 86 мс.

Cascading после генерации. FrugalGPT [3] спрашивает дешёвую модель, оценивает ответ и при неудаче идёт к дорогой. AutoMix [4] заменяет внешний оценщик самопроверкой модели. Унифицирующая работа [5] показывает, что каскад выигрывает только тогда, когда оценщик качества достаточно точен.

ML-исследователю

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

Из обзора вытекал и главный инженерный вопрос: все обучаемые роутеры требуют размеченных пар «запрос → какая модель ответила лучше» на своём трафике. На старте таких данных нет, а решать нужно с первого дня.

Устройство SurfCap-α

Кому особенно полезен раздел:АрхитекторыИнженеры

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

Конвейер SurfCap-α, 7 слоёв: слои могут только поднять тир, резидентность перекрывает всё. Пунктир — реализовано, в бою выключено; жирная рамка — собственный инвариант, которого нет в литературеКонвейер SurfCap-α, 7 слоёв: слои могут только поднять тир, резидентность перекрывает всё. Пунктир — реализовано, в бою выключено; жирная рамка — собственный инвариант, которого нет в литературеОграничение и исполнениеРезидентностьПДн и конф. данныене уходят наружуБюджетпонижает до T0сверх лимитаПровайдерперескоки400/429/5xxКаскадFrugalGPT /AutoMixВыбор тира: каждый слой только поднимает тир ↑Surfaceправила T3 → T0,первое совпадениеCapabilityголовы по мотивамHyDRAα-порогRouteLLM,граница T1 ↔ T2
Конвейер SurfCap-α, 7 слоёв: слои могут только поднять тир, резидентность перекрывает всё. Пунктир — реализовано, в бою выключено; жирная рамка — собственный инвариант, которого нет в литературе

Верхний ряд выбирает тир и может только поднимать его. Нижний ряд ограничивает и исполняет решение: провайдер выбирается внутри класса, сбойный уходит на паузу (circuit breaker, ~30 с), а каскад срабатывает как страховка уже после ответа.

Слой 1. Surface — поверхностные признаки

Кому особенно полезен раздел:ИнженерыАрхитекторы

Из текста и полей запроса извлекаются дешёвые признаки: длина в токенах, маркеры кода, математики, отладки, вызова инструментов, мультимодальности, «мешки намерений» и флаги риска (юридический, медицинский, персональные данные, безопасность, конфиденциальность). Регулярные выражения двуязычные и включают отраслевой словарь и ссылки на профильные законы. Из структуры запроса берутся признак «несколько намерений сразу» и глубина цепочки вызовов инструментов.

Правила проверяются сверху вниз, и побеждает первое совпадение (first match, highest tier). Скоры не суммируются: правило либо сработало, либо нет, поэтому решение всегда объяснимо.

Правила тиров: проверяются сверху вниз, побеждает первое совпадение
ТирУсловиеКласс модели
T3есть флаг рискасамая сильная; при ПДн или конфиденциальных данных — резидентная (с файлом — резидентная мультимодальная)
T2отладка, инструменты, несколько намерений или ≥ 1200 токенов«умная»; если сработала только длина — с длинным контекстом
T1маркеры кода или математикикодовая
T0всё остальноедешёвая

Мультимодальный вход на любом тире переключает класс на vision-модель, но сам по себе тир не поднимает.

Слой 2. Capability — головы способностей

Кому особенно полезен раздел:ML и исследователиИнженеры

Слой перенят из HyDRA [6]: если оценка головы превышает порог, тир поднимается до минимального тира профиля. Профили заданы в YAML: code ≥ 0.5 → T1; debug ≥ 0.5, tools ≥ 0.5, reasoning ≥ 0.7 → T2. Понижение запрещено на уровне кода, а не соглашения:

tier′=max⁡(tier, min_tier(h))для всех h: sh(q)≥θh\text{tier}' = \max\big(\text{tier},\ \text{min\_tier}(h)\big) \quad \text{для всех } h:\ s_h(q) \ge \theta_h

Слой 3. α-preference

Кому особенно полезен раздел:ML и исследователиАрхитекторы

Порог RouteLLM [1] применяется только на одной границе — между T1 и T2. Именно там эвристики неувереннее всего: код ли это на пару строк или архитектурный вопрос. T0 и T3 α не трогает: простые запросы дешевле не сделать, а рискованные нельзя удешевлять. По умолчанию α = 0.5.

Инвариант резидентности

Кому особенно полезен раздел:Бизнес и продуктАрхитекторы

Если в запросе есть персональные или конфиденциальные данные, ни capability, ни α, ни бюджет не могут увести его с резидентной модели. В причину решения дописывается residency:guard.

Архитектору

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

Слой 4. Мягкий бюджет

Кому особенно полезен раздел:Бизнес и продуктИнженеры

Каждый класс стоит условные единицы: дешёвая — 1, кодовая — 2, длинный контекст — 4, «умная» и vision — 5, самая сильная и резидентные — 6. При превышении мягкого лимита запрос понижается до дешёвого класса ещё до первого вызова.

Исполнение и каскад

Кому особенно полезен раздел:ИнженерыАрхитекторы

Класс модели и конкретный провайдер — разные слои. При ошибках 400, 429 и 5xx шлюз перескакивает на другого провайдера того же класса, а сбойный провайдер уходит на паузу примерно на 30 с. Так сбой не меняет ни качество, ни стоимость ответа. Каскад в духе FrugalGPT и AutoMix реализован, но выключен по умолчанию — именно по аргументу [5] о точности оценщика.

Каждое решение записывается одной строкой в заголовке ответа и в трассировке:

heuristic:T1;capability:debug→T2;preference:p=0.80>=α=0.50;budget:degrade_to=cheap;cascade

Реализация

Кому особенно полезен раздел:Инженеры

Шлюз написан с нуля на FastAPI, без готового прокси. Правила тиров, регулярные выражения, профили голов и α живут в YAML, поэтому политику маршрутизации можно менять без релиза. Движок правил знает четыре предиката (равенство, «не меньше», «не пусто», пересечение множеств) и их комбинации any_of / all_of.

Путь запроса:

  1. Детектор секретов проверяет вход. В режиме наблюдения он добавляет флаги риска, которые дальше влияют на тир.
  2. SurfCap-α выбирает класс: признаки → правила → capability → α → резидентность.
  3. Бюджет при необходимости понижает класс.
  4. Параллельно запускается проверка guardrails (о ней — вторая статья цикла); её результат нужен только перед вызовом провайдера.
  5. Выбор провайдера внутри класса, исполнение с перескоками, пассивный circuit breaker и активные health-пробы.
  6. Причина решения уходит в заголовок X-Route-Decision, в структурный лог и в дерево трассировки.

Бюджет задержки заложен на этапе проектирования: признаки и правила — меньше 1 мс до первого HTTP-вызова; capability-головы на CPU — 50–100 мс (ориентир — 86 мс у HyDRA), α-оценщик — ещё 10–30 мс. Именно поэтому ML-слои опциональны: они стоят на два порядка дороже эвристик и оправданы только там, где эвристики ошибаются.

Данные для калибровки. Здесь проходит граница между планом и боем. Интерфейсы capability и α реализованы и покрыты тестами с подставными оценщиками, но весов моделей в бою нет: для обучения нужна собственная разметка. Оценщик качества для каскада пока тоже заглушка. Источник разметки уже есть: каждое решение с причиной пишется в журнал запросов, а скрипт офлайн-оценки умеет сравнивать режимы «всегда дешёвая», «всегда сильная», «только эвристики», «+capability», «+α» и «+каскад».

Что сейчас работает в бою

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

Результаты и ограничения

Кому особенно полезен раздел:Бизнес и продуктML и исследователи

За 19 часов работы текущего процесса через model=auto прошло 826 запросов. Почти половина из них ушла не на дешёвую модель, а больше четверти — в T3 из-за флагов риска. Цифры — из Prometheus-счётчиков шлюза; задержка — среднее время всей стадии маршрута (детектор секретов, признаки, правила, бюджет) без подсчёта токенов и вызова модели.

Распределение запросов model=auto по тирам за 19 часов работы процесса, 826 запросов
ТирЗапросовДоляВыбор маршрута, мс (среднее)
T0 — дешёвая47056.9%0.7
T1 — кодовая20.2%7.7
T2 — «умная» / длинный контекст12415.0%34.2
T3 — самая сильная / резидентные23027.8%55.5

Из этого следуют три вывода.

  • Резидентность — самый заметный эффект. 176 из 230 запросов T3 ушли на резидентные модели. Без инварианта они бы ушли во внешнюю сильную модель.
  • Дешёвый путь укладывается в бюджет. Для T0 выбор маршрута занимает в среднем 0.7 мс при цели меньше 1 мс. У сложных классов задержка в десятки раз выше, и это ожидаемо: туда попадают длинные документы, а регулярные выражения проходят весь текст. Разложить эти десятки миллисекунд по стадиям ещё предстоит. На фоне секунд генерации они незаметны.
  • Граница T1↔T2 почти не встречается. В T1 попало всего 2 запроса: правила T2 стоят выше, и кодовые запросы с отладкой или длинным контекстом сразу уходят в T2. Значит, для нашего трафика α на этой границе даст мало. Перспективнее вернуть часть T2 в более дешёвый класс.

Ограничения

  • Офлайн-сравнения с режимами «всегда сильная» и «всегда дешёвая» по качеству и стоимости пока нет: нет размеченного набора.
  • Эвристики проверены только синтетическим набором из 30 кейсов на флаги риска (все 30 проходят) и смоук-тестом из 8 сценариев по тирам.
  • Регулярные выражения улавливают только то, что в них заложено, поэтому флаги риска скорее избыточны, чем недостаточны. Для резидентности это правильный перекос, для стоимости — нет.

Что здесь собственное, а что заимствовано

Кому особенно полезен раздел:ML и исследователиАрхитекторы

Отдельные техники взяты из литературы: α-порог, головы способностей, каскад с самопроверкой, каталог признаков. Собственный вклад — в том, как они скомпонованы:

  • детерминированные правила идут первыми, ML может только поднять тир, а каскад служит страховкой, а не первым шагом;
  • α применяется только на одной границе тиров;
  • класс модели отделён от провайдера, перескоки идут строго внутри класса;
  • мягкий бюджет считается в условных единицах;
  • инвариант резидентности перекрывает любую оптимизацию;
  • каждое решение объясняется одной строкой.

Дальше по циклу — guardrails: проверка входа, которая идёт параллельно с выбором маршрута.

Литература

Кому особенно полезен раздел:ML и исследователи

  1. Ong I. et al. RouteLLM: Learning to Route LLMs with Preference Data. arXiv:2406.18665.
  2. Ding D. et al. Hybrid LLM: Cost-Efficient and Quality-Aware Query Routing. arXiv:2404.14618.
  3. Chen L., Zaharia M., Zou J. FrugalGPT: How to Use Large Language Models While Reducing Cost and Improving Performance. arXiv:2305.05176.
  4. Aggarwal P. et al. AutoMix: Automatically Mixing Language Models. arXiv:2310.12963.
  5. Dekoninck J. et al. A Unified Approach to Routing and Cascading for LLMs. arXiv:2410.10347.
  6. Garg A. et al. HyDRA: Hybrid Dynamic Routing Architecture for Heterogeneous LLM Pools. arXiv:2605.17106.
  7. Varangot-Reille C. et al. Doing More with Less: A Survey on Routing Strategies for Resource Optimisation in Large Language Model-Based Systems. arXiv:2502.00409.
  8. Moslem Y. et al. Dynamic Model Routing and Cascading for Efficient LLM Inference: A Survey. arXiv:2603.04445.

Названия и первые авторы сверены с arXiv API 25 октября 2026.