Маршрутизация запросов к провайдерам

Как Meridian маршрутизирует запросы между LLM-провайдерами через governance-правила и адаптивную балансировку.

Обзор

Meridian предлагает два мощных механизма распределения запросов между провайдерами, каждый под свои сценарии:

  1. Governance-routing — явные пользовательские правила маршрутизации, заданные через виртуальные ключи.
  2. Адаптивная балансировка — автоматическая маршрутизация по производительности, основанная на метриках в реальном времени (Enterprise).

Когда оба механизма доступны, приоритет у governance — пользователь явно задал свои предпочтения через provider configurations виртуальных ключей.

Что и когда выбирать:

  • Governance — нужен явный контроль, требования compliance или конкретная стратегия оптимизации стоимости.
  • Adaptive Load Balancing — автоматическая оптимизация производительности с минимальной настройкой.

Каталог моделей (Model Catalog)

Каталог моделей — это центральный реестр Meridian, отслеживающий, какие модели доступны у каких провайдеров. Он питает и governance-routing, и адаптивную балансировку, поддерживая в актуальном состоянии маппинг «модель → провайдер».

Источники данных

Каталог объединяет два источника:

  1. Pricing-данные (основной источник).

    • Скачиваются с удалённого URL (настраиваемый, по умолчанию https://meridian.neria.cloud/datasheet).
    • Содержат имена моделей, тарифы и сопоставление с провайдерами.
    • Синхронизируются в БД при старте и периодически обновляются (по умолчанию раз в 24 часа).
    • Используются для расчёта стоимости и первичного маппинга.
    • Хранение: in-memory map pricingData[model|provider|mode] с O(1)-доступом.
  2. List Models API провайдеров (вторичный источник).

    • Запрос к эндпоинту /v1/models каждого провайдера при старте.
    • Обогащает каталог provider-specific моделями и алиасами.
    • Перезапрашивается при добавлении/обновлении провайдера через API или панель управления.
    • Добавляет модели, которых ещё нет в pricing-данных (например, только что выпущенные).
    • Хранение: in-memory map modelPool[provider][]models.

Зачем два источника? Pricing-данные дают полное покрытие моделей с информацией о стоимости, а List Models API позволяет использовать новые модели, не дожидаясь обновления pricing-данных.

Как определяется доступность модели

Поведение синхронизации

Поведение allowed_models с примерами

Поле allowed_models в provider-конфигах определяет, какие модели можно использовать с данным провайдером. Понимание его поведения критично для governance-routing.

Конфигурация:

{
  "provider_configs": [
    {
      "provider": "openai",
      "allowed_models": ["*"],
      "weight": 1.0
    }
  ]
}

Поведение:

  • Meridian вызывает GetModelsForProvider("openai").
  • Возвращает все модели из modelPool["openai"].
  • Запрос валидируется по каталогу.

Примеры:

# ✅ Разрешено (есть в каталоге)
curl -H "x-bf-vk: vk-123" -d '{"model": "gpt-4o"}'

# ✅ Разрешено
curl -H "x-bf-vk: vk-123" -d '{"model": "gpt-3.5-turbo"}'

# ❌ Отклонено (нет в каталоге OpenAI)
curl -H "x-bf-vk: vk-123" -d '{"model": "claude-3-5-sonnet"}'

Сценарии:

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

"allowed_models": [] (пустой массив) означает deny all — ни один запрос не пройдёт. Используйте ["*"], чтобы разрешить все модели через каталог.

Конфигурация:

{
  "provider_configs": [
    {
      "provider": "openai",
      "allowed_models": ["gpt-4o", "gpt-4o-mini"],
      "weight": 1.0
    },
    {
      "provider": "anthropic",
      "allowed_models": ["claude-3-5-sonnet-20241022"],
      "weight": 1.0
    }
  ]
}

Поведение:

  • Meridian валидирует модель по явному списку;
  • каталог в этом случае игнорируется;
  • поддерживаются прямые совпадения и записи с префиксом провайдера;
  • сравнение чувствительно к регистру.

Примеры:

# ✅ Разрешено
curl -H "x-bf-vk: vk-123" -d '{"model": "gpt-4o"}'

# ❌ Отклонено (нет в списке)
curl -H "x-bf-vk: vk-123" -d '{"model": "gpt-4-turbo"}'
# даже если gpt-4-turbo есть в каталоге OpenAI

# ✅ Разрешено
curl -H "x-bf-vk: vk-123" -d '{"model": "claude-3-5-sonnet-20241022"}'

# ❌ Отклонено (несовпадение версии)
curl -H "x-bf-vk: vk-123" -d '{"model": "claude-3-5-sonnet-20240620"}'

Записи с префиксом провайдера. Можно использовать имена моделей с префиксом — Meridian снимет префикс и сравнит с запрошенной моделью:

{
  "provider_configs": [
    {
      "provider": "openrouter",
      "allowed_models": ["openai/gpt-4o", "anthropic/claude-3-5-sonnet"],
      "weight": 1.0
    }
  ]
}

Как это работает:

# Запрос без префикса
curl -H "x-bf-vk: vk-123" -d '{"model": "gpt-4o"}'

# 1. Проверяется: "openai/gpt-4o" в allowed_models
# 2. Префикс снимается: "openai/gpt-4o" → "gpt-4o"
# 3. Сравнение: "gpt-4o" == "gpt-4o" ✅
# 4. Результат: разрешено и маршрутизируется в OpenRouter

Особенно полезно для proxy-провайдеров (OpenRouter, Vertex), где нужно явно ограничить, какие upstream-модели доступны.

Сценарии:

  • compliance (только утверждённые модели);
  • контроль расходов (только дешёвые модели);
  • pin версии (защита от автообновления);
  • тестирование конкретных версий;
  • явный cross-provider routing (например, разрешить только OpenAI-модели через OpenRouter).

Ключевое: deployments — это key-specific маппинги, позволяющие подменять провайдеро-специфические идентификаторы deployment'ов на удобные имена моделей.

Как работает:

  • задаётся на уровне ключа, не виртуального ключа;
  • структура: deployments: {"alias": "deployment-id"};
  • alias (слева) — имя модели в запросе пользователя;
  • deployment ID (справа) — провайдеро-специфический идентификатор, уходящий в API.

Azure OpenAI:

{
  "providers": {
    "azure": {
      "keys": [
        {
          "name": "azure-prod-key",
          "value": "your-api-key",
          "models": [],
          "azure_key_config": {
            "endpoint": "https://your-resource.openai.azure.com",
            "deployments": {
              "gpt-4o": "my-prod-gpt4o-deployment",
              "gpt-4o-mini": "my-mini-deployment"
            }
          }
        }
      ]
    }
  }
}

Что происходит:

  1. Allowed models выводится из алиасов: ["gpt-4o", "gpt-4o-mini"].
  2. Запрос с алиасом: {"model": "gpt-4o"}.
  3. Валидация: gpt-4o есть в выведенных allowed models ✅.
  4. Маппинг: gpt-4omy-prod-gpt4o-deployment.
  5. В Azure: используется my-prod-gpt4o-deployment.
  6. Pricing: если для deployment'а pricing нет, fallback на алиас gpt-4o.

Bedrock с inference-профилями:

{
  "providers": {
    "bedrock": {
      "keys": [
        {
          "name": "bedrock-key",
          "models": [],
          "bedrock_key_config": {
            "access_key": "your-access-key",
            "secret_key": "your-secret-key",
            "region": "us-east-1",
            "deployments": {
              "claude-sonnet": "us.anthropic.claude-3-5-sonnet-20241022-v2:0",
              "claude-opus": "us.anthropic.claude-3-opus-20240229-v1:0"
            }
          }
        }
      ]
    }
  }
}

Приоритет ограничений модели. При вычислении allowed models для ключа:

1. Если key.models НЕ пуст → используется key.models
2. Иначе если есть deployments → используются алиасы deployments
3. Иначе → разрешены все модели (через Model Catalog)

Пример с обоими:

{
  "keys": [
    {
      "models": ["gpt-4o", "gpt-3.5-turbo"],
      "azure_key_config": {
        "deployments": {
          "gpt-4o": "my-deployment",
          "gpt-4-turbo": "another-deployment"
        }
      }
    }
  ]
}

Результат: разрешены только ["gpt-4o", "gpt-3.5-turbo"]models имеет приоритет.

Vertex (тот же паттерн):

{
  "keys": [
    {
      "vertex_key_config": {
        "project_id": "my-project",
        "region": "us-central1",
        "deployments": {
          "claude-3-5-sonnet": "anthropic/claude-3-5-sonnet@20241022",
          "gemini-pro": "google/gemini-1.5-pro"
        }
      }
    }
  ]
}

Сценарии:

  • Azure — маппинг общих имён моделей на конкретные имена deployment'ов в Azure-ресурсе;
  • Bedrock — короткие алиасы для длинных ARN inference-профилей;
  • Vertex — конкретные версии моделей или региональные эндпоинты;
  • Окружения — разные deployment'ы в разных ключах (dev/staging/prod).

Ключевая идея:

Запрос: {"model": "gpt-4o"}

Валидация: "gpt-4o" в allowed models (выведенных из deployments)

Маппинг: deployments["gpt-4o"] → "my-prod-gpt4o-deployment"

API-вызов: используется "my-prod-gpt4o-deployment"

Pricing: fallback на "gpt-4o", если deployment'а нет в pricing-данных

Конфигурация:

{
  "provider_configs": [
    {
      "provider": "openai",
      "allowed_models": ["gpt-4o"],
      "weight": 0.5
    },
    {
      "provider": "azure",
      "allowed_models": ["gpt-4o"],
      "weight": 0.5
    }
  ]
}

Запрос:

curl -H "x-bf-vk: vk-123" \
     -d '{"model": "gpt-4o"}'

Поведение:

  1. Валидация: оба провайдера содержат gpt-4o в allowed_models ✅.
  2. Взвешенный выбор: 50/50.
  3. Выбран провайдер: допустим, Azure.
  4. Преобразование модели: gpt-4oazure/gpt-4o.
  5. Fallbacks: ["openai/gpt-4o"] (оставшиеся провайдеры).

Cross-provider сценарии:

OpenRouter как универсальный proxy.

{
  "provider_configs": [
    {
      "provider": "openrouter",
      "allowed_models": ["*"]
    }
  ]
}

Запрос claude-3-5-sonnet:

  • Meridian проверяет GetModelsForProvider("openrouter").
  • Находит anthropic/claude-3-5-sonnet в каталоге OpenRouter.
  • ✅ Разрешено, маршрутизируется в OpenRouter.

Взвешенный routing через proxy-провайдера. Сценарий: 99 % OpenAI-трафика отправлять через OpenRouter ради экономии, 1 % напрямую — для подстраховки.

{
  "provider_configs": [
    {
      "provider": "openai",
      "allowed_models": ["gpt-4o"],
      "weight": 0.01
    },
    {
      "provider": "openrouter",
      "allowed_models": ["openai/gpt-4o"],
      "weight": 0.99
    }
  ]
}

Запрос gpt-4o:

  • OpenAI: "gpt-4o" в ["gpt-4o"] → ✅.
  • OpenRouter: снимает префикс с "openai/gpt-4o" → совпадает с "gpt-4o" → ✅.
  • Взвешенный выбор: 99 % → OpenRouter.
  • Финальная модель: openrouter/gpt-4o.
  • Fallbacks: ["openai/gpt-4o"].

Vertex как мульти-провайдерный шлюз.

{
  "provider_configs": [
    {
      "provider": "vertex",
      "allowed_models": ["claude-3-5-sonnet", "gemini-1.5-pro"]
    }
  ]
}

Запрос claude-3-5-sonnet:

  • GetProvidersForModel("claude-3-5-sonnet") находит ["anthropic", "vertex", "bedrock"].
  • Валидация по allowed_models ✅.
  • В Vertex уходит как anthropic/claude-3-5-sonnet.

Совместимость Groq с OpenAI.

{
  "provider_configs": [
    {
      "provider": "groq",
      "allowed_models": ["gpt-3.5-turbo"]
    }
  ]
}

Запрос gpt-3.5-turbo:

  • Специальная обработка: проверка каталога Groq на openai/gpt-3.5-turbo.
  • ✅ Найдено, валидация прошла.
  • В Groq уходит как openai/gpt-3.5-turbo.

Как каталог используется в маршрутизации

Когда у виртуального ключа есть provider_configs, governance использует каталог для валидации.

Wildcard allowed_models:

{
  "provider_configs": [
    {
      "provider": "openai",
      "allowed_models": ["*"],
      "weight": 0.5
    }
  ]
}

Поток:

curl -H "x-bf-vk: vk-123" -d '{"model": "gpt-4o"}'

# 1. Governance: есть ли "gpt-4o" в GetModelsForProvider("openai")?
# 2. Каталог: modelPool["openai"] содержит "gpt-4o" ✅
# 3. Валидация прошла, провайдер выбран
# 4. Модель становится: "openai/gpt-4o"

Отказ:

curl -H "x-bf-vk: vk-123" -d '{"model": "claude-3-5-sonnet"}'

# 1. Governance: есть ли "claude-3-5-sonnet" в GetModelsForProvider("openai")?
# 2. Каталог: modelPool["openai"] НЕ содержит "claude-3-5-sonnet" ❌
# 3. Валидация не прошла, запрос отклонён
# 4. Ошибка: "model not allowed for any configured provider"

Балансировщик опрашивает каталог для поиска кандидатов.

Поток:

curl -X POST http://localhost:8080/v1/chat/completions \
  -d '{"model": "gpt-4o", "messages": [...]}'

# 1. LB: GetProvidersForModel("gpt-4o")
# 2. Каталог: ["openai", "azure", "groq"]
# 3. Фильтр по настроенным провайдерам: ["openai", "azure"] (groq не настроен)
# 4. Скоринг: openai=0.95, azure=0.87
# 5. Выбран: openai
# 6. Модель: "openai/gpt-4o"
# 7. Fallbacks: ["azure/gpt-4o"]

Cross-provider обнаружение:

curl -d '{"model": "claude-3-5-sonnet"}'

# 1. LB: GetProvidersForModel("claude-3-5-sonnet")
# 2. Проверки каталога:
#    - Прямой: ["anthropic"] ✅
#    - OpenRouter: содержит "anthropic/claude-3-5-sonnet" ✅
#    - Vertex: содержит "anthropic/claude-3-5-sonnet" ✅
#    - Bedrock: содержит "anthropic.claude-3-5-sonnet-..." ✅
# 3. Каталог: ["anthropic", "openrouter", "vertex", "bedrock"]
# 4. Скоринг по всем четырём
# 5. Выбран лучший по производительности

Так Meridian реализует умный cross-provider routing без ручной настройки.

Каталог моделей критичен для cross-provider routing. Без него Meridian не знал бы, что gpt-4o доступен у OpenAI, Azure и Groq, и что claude-3-5-sonnet маршрутизируется через Anthropic, Vertex, Bedrock и OpenRouter. Эти знания питают и governance-валидацию, и обнаружение провайдеров балансировщиком.


Governance-routing

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

Как это работает

Когда у виртуального ключа определены provider_configs:

  1. Запрос приходит с виртуальным ключом (например, x-bf-vk: vk-prod-main).
  2. Валидация модели: Meridian проверяет, разрешена ли модель хотя бы у одного настроенного провайдера.
  3. Фильтрация провайдеров по:
    • наличию модели в allowed_models;
    • бюджетам (текущее использование vs максимум);
    • rate limit'ам (токены/запросы за окно).
  4. Взвешенный выбор: провайдер выбирается weighted random.
  5. Префикс провайдера: строка модели становится provider/model (например, openai/gpt-4o).
  6. Fallbacks: оставшиеся провайдеры сортируются по убыванию веса и добавляются как fallbacks.

Пример конфигурации

{
  "provider_configs": [
    {
      "provider": "openai",
      "allowed_models": ["gpt-4o", "gpt-4o-mini"],
      "weight": 0.3,
      "budget": {
        "max_limit": 100.0,
        "current_usage": 45.0
      }
    },
    {
      "provider": "azure",
      "allowed_models": ["gpt-4o"],
      "weight": 0.7,
      "rate_limit": {
        "token_max_limit": 100000,
        "token_reset_duration": "1m"
      }
    }
  ]
}

Поток запроса

Запрос с виртуальным ключом:

curl -X POST http://localhost:8080/v1/chat/completions \
  -H "x-bf-vk: vk-prod-main" \
  -d '{"model": "gpt-4o", "messages": [...]}'

Оценка governance:

  • OpenAI: ✅ gpt-4o в allowed_models, бюджет в порядке, вес 0.3.
  • Azure: ✅ gpt-4o в allowed_models, rate limit в порядке, вес 0.7.

Взвешенный выбор:

  • 70 % → Azure;
  • 30 % → OpenAI.

Преобразование запроса:

{
  "model": "azure/gpt-4o",
  "messages": [...],
  "fallbacks": ["openai/gpt-4o"]
}

Возможности

ВозможностьОписание
Явный контрольТочный список провайдеров и моделей
БюджетАвтоматическое исключение провайдеров с превышением бюджета
Rate limitПропуск провайдеров, упирающихся в лимиты
ВесаРаспределение трафика custom-весами
Авто-fallbacksПри сбое провайдера автоматически идёт следующий по весу

Лучшие практики

allowed_models: ["*"] — разрешает все модели провайдера, валидация через каталог моделей (см. секцию выше).

allowed_models: [] (пустой массив) — отказывает всем моделям; ни один запрос для этой конфигурации не пройдёт.

Пустой provider_configsвсе провайдеры заблокированы (deny-by-default). Чтобы пропустить трафик через виртуальный ключ, добавьте конфигурации провайдеров явно.


Адаптивная балансировка

Возможность Enterprise. Adaptive Load Balancing доступен в Enterprise-редакции Meridian. Подробнее: neria.cloud.

Адаптивная балансировка автоматически оптимизирует маршрутизацию по метрикам производительности в реальном времени. Работает на двух уровнях: macro-уровне (выбор провайдера) и micro-уровне (выбор ключа).

Двухуровневая архитектура

Разделение выбора провайдера (направления) и выбора ключа (маршрута) даёт:

  • Оптимизацию на уровне провайдера — лучший провайдер для модели по агрегированной производительности;
  • Оптимизацию на уровне ключа — внутри провайдера выбирается лучший API-ключ;
  • Устойчивость — даже когда провайдер задан явно (governance или пользователем), оптимизация ключа продолжает работать.

Уровень 2: маршрут (выбор ключа)

Уровень 1: направление (выбор провайдера)

Запрос: gpt-4o

Поиск в Model Catalog

Кандидаты:
openai, azure, groq

Фильтр по allowed_models
и доступности ключей

Скоринг по производительности:
error rate, latency, utilization

Выбор: openai

Ключи OpenAI:
key-1, key-2, key-3

Скоринг ключей:
error rate, latency, TPM hits

Выбор: key-2

Выполнение:
openai/gpt-4o + key-2

Уровень 1: направление (выбор провайдера)

Когда выполняется: только когда строка модели не содержит префикс провайдера (например, gpt-4o).

Как:

  1. Поиск в каталоге — все настроенные провайдеры, поддерживающие модель.
  2. Фильтрация по:
    • allowed_models из конфигурации ключей;
    • доступности ключей у провайдера.
  3. Скоринг производительности:
    • error rates (вес 50 %);
    • latency (вес 20 %, алгоритм MV-TACOS);
    • utilization (вес 5 %);
    • momentum bias (ускорение восстановления).
  4. Smart selection: weighted random с jitter и exploration.
  5. Fallbacks: оставшиеся провайдеры сортируются по убыванию score.

Уровень 2: маршрут (выбор ключа)

Когда выполняется: всегда, даже если провайдер уже выбран (governance, пользователем или уровнем 1).

Как:

  1. Сбор ключей провайдера.
  2. Фильтрация по конфигурации (ограничения по моделям).
  3. Скоринг каждого ключа:
    • error rates (недавние сбои);
    • latency;
    • TPM hits (нарушения rate limit);
    • текущее состояние (Healthy, Degraded, Failed, Recovering).
  4. Weighted random с exploration (25 % шанс «зондирования» recovering-ключа).
  5. Circuit breaker: ключи с весом 0 пропускаются (TPM hits, повторные сбои).

Алгоритм скоринга

Балансировщик считает score для каждой пары provider-model:

Score = (P_error × 0.5) + (P_latency × 0.2) + (P_util × 0.05) − M_momentum

Меньшие штрафы → бо́льшие веса → больше трафика. Система самовосстанавливается: быстро штрафует проблемные маршруты и быстро возвращает их в работу после восстановления.

Поток запроса

Запрос без префикса провайдера:

curl -X POST http://localhost:8080/v1/chat/completions \
  -d '{"model": "gpt-4o", "messages": [...]}'

Поиск в каталоге. Поддерживают gpt-4o: [openai, azure, groq].

Оценка производительности:

  • OpenAI: 0.92 (низкая latency, 99 % успехов);
  • Azure: 0.85 (средняя latency, 98 % успехов);
  • Groq: 0.65 (высокая latency недавно).

Выбор провайдера. OpenAI (наивысший score в jitter-диапазоне).

Преобразование запроса:

{
  "model": "openai/gpt-4o",
  "messages": [...],
  "fallbacks": ["azure/gpt-4o", "groq/gpt-4o"]
}

Возможности

ВозможностьОписание
Авто-оптимизацияБез ручного подбора весов
Real-time адаптацияВеса пересчитываются каждые 5 секунд
Circuit breakersПадающие маршруты автоматически выводятся из ротации
Быстрое восстановлениеСнижение штрафа на 90 % за 30 секунд после устранения
Health statesHealthy, Degraded, Failed, Recovering
Smart exploration25 % шанс «зондирования» восстановившихся маршрутов

Видимость в дашборде

Мониторинг балансировки в реальном времени:

Дашборд адаптивной балансировки

В дашборде отображается:

  • распределение весов по маршрутам provider-model-key;
  • метрики производительности (error rate, latency, success rate);
  • переходы состояний (Healthy → Degraded → Failed → Recovering);
  • фактическое vs ожидаемое распределение трафика.

Как governance и балансировка взаимодействуют

Когда оба механизма доступны, они работают согласованно по двум уровням.

Ключевая идея. У балансировки два уровня:

  • Уровень 1 (направление / провайдер) — пропускается, если провайдер уже задан;
  • Уровень 2 (маршрут / ключ)всегда выполняется, даже если провайдер задан.

То есть оптимизация ключа работает независимо от того, кто выбрал провайдера.

Поток выполнения

Балансировка, уровень 2

Балансировка, уровень 1

Governance Plugin (HTTPTransportIntercept)

Да

Нет

Да, пропустить уровень 1

Нет

Запрос: gpt-4o

Есть VK с
provider_configs?

Выбор провайдера:
weighted random

Префикс:
azure/gpt-4o

Уже есть префикс
провайдера?

Выбор провайдера:
по производительности

Префикс:
openai/gpt-4o

Получить ключи
выбранного провайдера

Скоринг ключей
по метрикам

Выбор лучшего ключа

Выполнить запрос
с выбранным провайдером и ключом

Порядок выполнения

  1. HTTPTransportIntercept (governance, уровень провайдера) — выполняется первым; если у виртуального ключа есть provider_configs, добавляет префикс провайдера. Результат: провайдер выбран governance.
  2. Middleware (балансировка, уровень провайдера / направление) — после governance; если в строке модели уже есть /, пропускает выбор провайдера; иначе делает performance-based выбор. Результат: префикс добавлен, если его не было.
  3. KeySelector (балансировка, уровень ключа / маршрут) — всегда выполняется во время исполнения запроса в ядре Meridian: получает ключи провайдера, фильтрует по ограничениям модели, скорит и выбирает лучший. Результат: оптимальный ключ внутри выбранного провайдера.

Даже когда governance задал azure/gpt-4o, балансировка всё равно подбирает оптимальный ключ Azure по метрикам. В этом сила двухуровневой архитектуры.

Сценарии

Конфигурация:

  • у виртуального ключа есть provider_configs;
  • адаптивная балансировка выключена.

Запрос:

curl -X POST http://localhost:8080/v1/chat/completions \
  -H "x-bf-vk: vk-prod-main" \
  -d '{"model": "gpt-4o", "messages": [...]}'

Поведение:

  1. Governance делает weighted-провайдера → выбирает Azure (вес 0.7).
  2. Модель: azure/gpt-4o.
  3. Стандартный выбор ключа (не адаптивный) — по статическим весам.
  4. Запрос уходит в Azure с выбранным ключом.

Конфигурация:

  • виртуальный ключ не передан (нет x-bf-vk) — это и есть LB-only сценарий;
  • виртуальный ключ с пустым/отсутствующим provider_configs блокирует все провайдеры (deny-by-default), и это не LB-only;
  • адаптивная балансировка включена.

Запрос:

curl -X POST http://localhost:8080/v1/chat/completions \
  -d '{"model": "gpt-4o", "messages": [...]}'

Поведение:

  1. LB уровня 1 делает performance-based выбор провайдера → OpenAI.
  2. Модель: openai/gpt-4o.
  3. LB уровня 2 выбирает лучший ключ OpenAI по метрикам.
  4. Запрос уходит в OpenAI с оптимальным ключом.

Конфигурация:

  • у виртуального ключа есть provider_configs;
  • адаптивная балансировка включена;
  • у Azure три ключа: azure-key-1, azure-key-2, azure-key-3.

Запрос:

curl -X POST http://localhost:8080/v1/chat/completions \
  -H "x-bf-vk: vk-prod-main" \
  -d '{"model": "gpt-4o", "messages": [...]}'

Поведение:

  1. Governance идёт первым (уважает явную настройку) → выбирает Azure.
  2. Модель: azure/gpt-4o.
  3. LB уровня 1 видит / и пропускает выбор провайдера.
  4. LB уровня 2 продолжает работать! Скорит ключи Azure:
    • azure-key-1: 99 % успехов, 150 мс latency → 0.95;
    • azure-key-2: 85 % успехов, 200 мс latency → 0.60 (degraded);
    • azure-key-3: TPM лимит → 0.0 (circuit broken);
    • выбран azure-key-1.
  5. Запрос уходит в Azure с azure-key-1.

Governance отвечает за провайдера (явное намерение пользователя), балансировка — за ключ (автоматическая оптимизация).

Конфигурация:

  • governance и балансировка включены;
  • у OpenAI два доступных ключа.

Запрос:

curl -X POST http://localhost:8080/v1/chat/completions \
  -d '{"model": "openai/gpt-4o", "messages": [...]}'

Поведение:

  1. Governance видит / и пропускает.
  2. LB уровня 1 видит / и пропускает выбор провайдера.
  3. LB уровня 2 работает: выбирает лучший ключ OpenAI по текущим метрикам.
  4. Запрос уходит в OpenAI с оптимальным ключом.

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

Правила выбора провайдера и ключа

СценарийВыбор провайдераВыбор ключа
VK с provider_configsGovernance (weighted random)Стандартный или адаптивный (если включён)
VK без provider_configs + LBЗаблокировано (пусто = провайдеров нет)
Без VK + LBLB уровня 1 (производительность)LB уровня 2 (производительность)
Модель с префиксом провайдера + LBПропускается (уже задан)LB уровня 2
LB не включёнGovernance, пользователь или каталогСтандартный (статические веса)

Ключевой момент.

  • Выбор провайдера уважает иерархию: правила маршрутизации → governance → LB уровня 1 → ручное указание.
  • Выбор ключа работает независимо и получает выгоду от балансировки даже когда провайдер предопределён.

Это разделение и делает двухуровневую архитектуру мощной.


Правила маршрутизации (динамические выражения)

Положение в pipeline. Правила маршрутизации выполняются до выбора провайдера в governance и могут переопределить его. Они оцениваются раньше адаптивной балансировки и позволяют динамически переопределять провайдера/модель по runtime-условиям: заголовкам, параметрам, метрикам потребления и организационной иерархии.

Кратко

Правила маршрутизации дают expression-based контроль через CEL. В отличие от governance (статические веса), правила оцениваются в момент запроса.

Когда выполняются

3. Слой балансировки

2. Governance (если правил не было)

1. Слой правил маршрутизации (первый)

Да

Нет

Запрос: model + provider

CEL-выражение
совпало?

Override:
новый provider/model/fallbacks

Нет совпадения:
идём в governance

Валидация VK

Routing провайдера
(weighted random)

Уровень 1: выбор провайдера

Уровень 2: выбор ключа

Выполнение:
выбранный провайдер + ключ

Как это работает

  1. Правила оцениваются первыми в порядке scope (VirtualKey → Team → Customer → Global).
  2. Если правило совпало: provider/model/fallbacks переопределяются, governance provider_configs пропускается.
  3. Если совпадений нет: работает governance (weighted random).
  4. LB уровня 1 пропускается, если провайдер уже определён (есть /).
  5. LB уровня 2 (выбор ключа) работает всегда.

Доступные CEL-переменные

// Контекст запроса
model                      // Запрошенная модель
provider                   // Текущий провайдер

// Заголовки и параметры (без учёта регистра)
headers["x-tier"]          // Заголовок запроса
params["region"]           // Query-параметр

// Контекст организации
virtual_key_id             // ID виртуального ключа
team_name                  // Имя команды
customer_id                // ID клиента

// Метрики потребления (0–100 %)
budget_used                // Использование бюджета
tokens_used                // Использование токенового rate limit
request                    // Использование request rate limit

Примеры

По тарифу пользователя

headers["x-tier"] == "premium"   // → openai/gpt-4o

Fallback при высоком бюджете

budget_used > 85                 // → groq/llama-2 (дешевле)

По команде

team_name == "ml-research"       // → anthropic/claude-3-opus

Сложное многокритериальное

headers["x-environment"] == "production" &&
tokens_used < 75 &&
team_name == "ai-platform"       // → openai/gpt-4o

Иерархия scope

Правила оцениваются по precedence (first-match-wins):

1. VirtualKey scope (наивысший)
2. Team scope
3. Customer scope
4. Global scope (низший)

Внутри одного scope — по приоритету (по возрастанию: 0 раньше 10).

Возможности

ВозможностьОписание
CEL-выраженияКомпозиционный язык условий с богатым набором операторов
Иерархия scopeПравила на уровне VirtualKey/Team/Customer/Global с правильной precedence
Динамический overrideПереопределение провайдера и/или модели по runtime-условиям
Fallback-цепочкиНесколько резервных провайдеров для авто-failover
ПриоритетыМеньше — раньше внутри одного scope
Capacity awarenessДоступ к real-time использованию бюджета и rate limits

Подробная документация: Правила маршрутизации.


Как выбрать подход

  1. Используйте governance, когда:

    • ✅ требуется compliance (данные в определённом регионе/провайдере);
    • ✅ нужен явный контроль над оптимизацией стоимости;
    • ✅ нужны жёсткие лимиты бюджета на провайдера;
    • ✅ разделение окружений (разные команды/приложения, разные провайдеры);
    • ✅ управление rate limit'ами провайдеров.
  2. Используйте правила маршрутизации, когда:

    • ✅ нужна динамическая маршрутизация по runtime-контексту (заголовки, параметры);
    • ✅ нужен capacity-aware routing (fallback при росте бюджета или rate limit);
    • ✅ организационные правила (разные правила для команд/клиентов);
    • ✅ A/B-тесты;
    • ✅ сложные условия (тариф + capacity + команда).
  3. Используйте балансировку, когда:

    • ✅ нужна автоматическая оптимизация по производительности;
    • ✅ хочется минимума настройки;
    • ✅ нагрузки часто меняются;
    • ✅ нужен мгновенный авто-failover;
    • ✅ нужна устойчивость через multi-provider redundancy.
  4. Используйте всё вместе, когда:

    • ✅ нужно полное решение: governance задаёт базу, правила добавляют динамику, балансировка оптимизирует ключи;
    • ✅ разные виртуальные ключи используют разные стратегии;
    • ✅ enterprise-развёртывания со сложной структурой требований.

Содержание

ОбзорКаталог моделей (Model Catalog)Источники данныхКак определяется доступность моделиПоведение синхронизацииПоведение allowed_models с примерамиКак каталог используется в маршрутизацииGovernance-routingКак это работаетПример конфигурацииПоток запросаВозможностиЛучшие практикиАдаптивная балансировкаДвухуровневая архитектураУровень 1: направление (выбор провайдера)Уровень 2: маршрут (выбор ключа)Алгоритм скорингаПоток запросаВозможностиВидимость в дашбордеКак governance и балансировка взаимодействуютПоток выполненияПорядок выполненияСценарииПравила выбора провайдера и ключаПравила маршрутизации (динамические выражения)КраткоКогда выполняютсяКак это работаетДоступные CEL-переменныеПримерыПо тарифу пользователяFallback при высоком бюджетеПо командеСложное многокритериальноеИерархия scopeВозможностиКак выбрать подход