Перейти к основному содержанию
Встроенные стратегии — cheapest, quality, balanced, adaptive — выбирают модель по цене и качеству. Routing DSL — это уровень ниже, для случаев, когда правильная модель зависит от того, чем на самом деле является запрос: длинным агентным ходом по коду, дешёвым вызовом классификации, запросом с изображением, повторной попыткой после проваленного теста. Вы пишете правила; шлюз вычисляет их для каждого запроса и маршрутизирует соответственно. Это стратегия dsl именованного маршрутизатора — поэтому ваше приложение продолжает вызывать orcarouter/{name}, а логика маршрутизации живёт в дашборде, версионируется и редактируется без передеплоя.

Когда выбирать DSL

Используйте встроенную стратегию, когда «самая дешёвая живая модель» или «наилучшее качество» отражают ваше намерение. Обращайтесь к DSL, когда маршрутизация зависит от содержания или контекста запроса:
  • Специализация под задачу — отправляйте код в модель для кода, изображения в модель для изображений, дешёвый чат в дешёвую модель.
  • Маршрутизация с учётом сложности — эскалируйте к дорогой модели только сложные запросы; держите простые дешёвыми.
  • Маршрутизация с учётом агента — маршрутизируйте по-разному в зависимости от состояния сессии (какие инструменты использовал агент, провалились ли только что тесты, на каком ходе он находится).
  • Правила по времени / тенанту / заголовку — разная маршрутизация по часу, группе пользователей или заголовку запроса.

Включение

В дашборде в разделе Routing откройте маршрутизатор и установите его Strategy в DSL. Это откроет редактор DSL для данного маршрутизатора. Всё остальное в маршрутизаторе по-прежнему действует — glob Allowed models, страховочная Default model и вызов orcarouter/{name}.

Редактор

Редактор устроен так, чтобы провести вас от намерения к рабочему набору правил быстро:
  • Templates заполнены реальными моделями вашего рабочего пространства (через одноразовый диалог сопоставления уровней), так что вы никогда не начинаете с пустого файла и не упираетесь в стену «неизвестной модели».
  • Insert — вставьте Model, Router (orcarouter/<name>) или Pool из автодополнения вместо ручного ввода идентификаторов.
  • Generate — опишите нужную маршрутизацию обычным языком и получите скомпилированный, прошедший линтинг DSL, опирающийся на ваши реальные модели.
  • Explain — пересказ на обычном языке того, что делает текущий набор правил.
  • Inline lint — каждая ошибка сообщает {line, column, message}, и у каждого кода линта есть пояснение по ?. Приоритет (побеждает первое совпадение) и распространённые паттерны CEL подсказываются прямо на месте.

Структура файла

Набор правил — это YAML с тремя ключами верхнего уровня:
version: 1              # required — currently must be 1
rules: [...]            # required — 1 to 30 rules, evaluated in order
default: {...}          # required — the effect when no rule matches
Правило — это условие when: и эффект use::
rules:
  - id: hard_code              # required: ^[a-z][a-z0-9_]{0,39}$, unique
    when: |                    # optional CEL boolean; absent ⇒ always matches
      task_class == "code" && difficulty > 0.6
    use:
      model: "anthropic/claude-sonnet-4-6"
default:
  delegate: balanced           # fall back to a built-in strategy
Правила вычисляются сверху вниз; побеждает первое правило, чьё when: истинно. Если ни одно не совпало, применяется default:. Располагайте правила от наиболее специфичных к общим — широкое раннее правило затеняет всё, что ниже.

when: — условие

Условия пишутся на CEL (Common Expression Language): безопасном по своей сути — без циклов, без ввода-вывода, вычисление за микросекунды, только регулярные выражения RE2. Эти шесть паттернов покрывают подавляющее большинство реальных правил:
ПаттернПример
Доступ к полюtask_class == "agent"
Числовое сравнениеdifficulty > 0.6 && request.input_tokens < 50000
Булева логикаagent_state.has_edited && !agent_state.has_run_tests
Проверка вхождения в список"Edit" in agent_state.tools_used
Макрос регулярного выраженияsystem_prompt_matches("(?i)planning agent")
Макрос инструментовtool_calls_present_any(["Edit","Write","apply_patch"])

Переменные

Форма запроса
ПеременнаяТип
modelstring
request.input_tokensint
request.output_max_tokensint
request.streambool
request.visionbool
request.message_countint
request.has_system_promptbool
request.has_toolsbool
Классификация (вычисляется шлюзом для каждого запроса)
ПеременнаяТипЗначение
task_classstringchat / code / agent / vision / audio / rag / creative
difficultydouble0.01.0
code_keyword_densitydouble0.01.0
reasoning_cue_countintпризнаки рассуждения, обнаруженные в промпте
tool_countintразличные определения инструментов в запросе
Сессия агента (agent_state.*, сохраняется на протяжении разговора)
ПеременнаяТип
agent_state.turnint
agent_state.tools_usedlist<string>
agent_state.files_readlist<string>
agent_state.has_editedbool
agent_state.has_run_testsbool
agent_state.last_test_failedbool
agent_state.consecutive_errorsint
agent_state.elapsed_secondsint
agent_state.models_triedlist<string>
Контекст
ПеременнаяТип
headers["x-foo"]string
user.id / user.groupint / string
token.id / token.nameint / string
time.hour / time.weekdayint (UTC)
workspace.idint

Макросы

Зарегистрированные функции CEL для распространённых проверок «загляни внутрь запроса»:
МакросВозвращает
system_prompt_matches(regex)RE2 по объединённым системным сообщениям
user_message_matches(regex)RE2 по последнему сообщению пользователя
tool_definitions_include(name)инструмент объявлен в запросе
tool_calls_present_any(list)запрос несёт любой из этих вызовов инструментов
tool_results_from_any(list)в запросе есть сообщения с ролью инструмента от любого из них
header_matches(name, regex)RE2 по значению заголовка

use: — эффект

Блок use: называет назначение (ровно одно) и любое число необязательных настроек для конкретного вызова.

Назначение

use:
  model:    "anthropic/claude-sonnet-4-6"   # one upstream model
  models:   ["openai/gpt-4o-mini", "..."]   # load-balance across a list
  pool:     "@pool:<name>"                   # an admin-curated pool
  delegate: balanced                         # hand off to a built-in
                                             #   strategy: cheapest |
                                             #   quality | balanced |
                                             #   linucb | gated_adaptive
delegate: dsl отклоняется (это привело бы к рекурсии). Привязка к конкретным каналам (channels: / @channel:) сейчас недоступна и при линтинге помечается как неподдерживаемая — маршрутизируйте через model, models или pool.

Настройки для конкретного вызова

Комбинируйте с любым назначением, чтобы сформировать апстрим-вызов:
use:
  reasoning_effort:       low | medium | high     # OpenAI o-series, Gemini
  thinking_budget_tokens: 1024..64000             # Claude / Gemini thinking
  samples:                1..16                    # the n parameter
  temperature:            0.0..2.0
  param_override:         { ... }                  # merged into upstream params
  header_override:        { ... }                  # merged into upstream headers
  reason_tag:             "<[a-z0-9_]+>"           # shows up in logs/telemetry
  affinity_ttl:           "5m"                      # channel stickiness window
  model_rewrite:          "<upstream-model>"       # send under a different name
param_override и header_override соблюдают чёрный список — вы не можете переопределить model, messages, stream, tools, заголовки авторизации и т. п. (это подорвало бы биллинг, аудит или состояние агента).

Каскады уверенности и ансамбли (продвинутое)

Два продвинутых эффекта позволяют правилу реагировать на слабый первый ответ или развернуться веером по нескольким моделям. Они создаются так же, как любое правило. Cascade — повтор по сигналу низкой уверенности с более сильным эффектом:
rules:
  - id: code_with_repair
    when: task_class == "code"
    use:
      model: "openai/gpt-4o-mini"
    on_low_confidence:
      signals: [patch_invalid, self_doubt, next_turn_test_failed]
      use:
        model: "anthropic/claude-sonnet-4-6"   # repair attempt
Ensemble — запускает несколько ветвей параллельно и позволяет арбитру выбрать:
use:
  parallel:
    - { model: "anthropic/claude-sonnet-4-6" }
    - { model: "openai/gpt-4o-mini", samples: 2 }
  arbiter:
    strategy: best_of_n        # or majority | first | tests_pass
    model:    "anthropic/claude-sonnet-4-6"   # judge (best_of_n only)
  max_latency_ms: 120000
Среда выполнения ансамблей / каскадов закрыта флагом и по умолчанию выключена. Поскольку каждая параллельная ветвь и каждый каскадный повтор тарифицируются как отдельный вызов, среда выполнения веерного развёртывания находится за серверным флагом, пока проверяется потарифный учёт по ветвям. Когда она выключена, правило parallel: обслуживает только первую ветвь, а каскад записывает свой сигнал, но не перенаправляет повторно — набор правил всё равно проходит линтинг, сохраняется и маршрутизирует свой основной эффект как обычно. Свяжитесь с нами, чтобы включить среду выполнения ансамблей для вашего рабочего пространства.

Безопасный выкат

Новый набор правил не берёт на себя ваш трафик в момент сохранения:
  • Теневой режим — в течение окна после первого сохранения DSL вычисляется, но не используется: ваша прежняя стратегия по-прежнему обслуживает трафик, а шлюз записывает, что сделал бы DSL. Дашборд показывает отчёт о расхождениях — процент отличающихся маршрутов, прогнозируемую разницу в стоимости, число срабатываний по каждому правилу и как часто доходило до default:. Прочитайте его, прежде чем доверять правилам.
  • Канареечный запуск — наращивайте долю живого трафика, идущего через DSL (5 → 25 → 50 → 100), отслеживая метрики по каждому срезу, и мгновенно откатывайтесь, сдвинув процент к 0.
Вы также можете выполнить пробный прогон (dry-run) набора правил против синтетического запроса (класс задачи, сложность, состояние агента, форма запроса) прямо в редакторе и увидеть трассировку и совпавшее правило — без трафика, ничего не сохраняется.

Ограничения и валидация

Каждое сохранение запускает строгий линтинг; некорректные наборы правил отклоняются с {line, column, message, rule}:
  • Схема — обязательные ключи, корректные типы/enum, без неизвестных полей.
  • Размер — ≤ 30 правил, ≤ 16 КиБ YAML, ≤ 200 символов на when:.
  • CEL — парсится, проходит проверку типов относительно окружения переменных, без неизвестных идентификаторов, и when: должно вычисляться в bool.
  • Эффект — ровно одно назначение на блок use:; все ссылки model / models / @pool: должны разрешаться в вашем рабочем пространстве.
  • Диапазоны настроекthinking_budget_tokens ∈ [1024, 64000], temperature ∈ [0, 2], samples ∈ [1, 16].
  • Зарезервировано — id правил, начинающиеся с _, зарезервированы; default в качестве id правила отклоняется (используйте блок default: верхнего уровня).
Каждое сохранение и откат записывает строку аудита; конкурентные правки обнаруживаются, и второму сохранению предлагается повторить попытку относительно свежего состояния.

Полный пример

version: 1
rules:
  - id: vision
    when: request.vision
    use: { model: "openai/gpt-4o" }

  - id: cheap_chat
    when: task_class == "chat" && difficulty < 0.3
    use: { delegate: cheapest }

  - id: hard_code
    when: task_class == "code" && difficulty > 0.6
    use:
      model: "anthropic/claude-sonnet-4-6"
      thinking_budget_tokens: 8000
      reason_tag: hard_code

  - id: agent_after_failed_test
    when: agent_state.last_test_failed && agent_state.consecutive_errors >= 2
    use:
      model: "anthropic/claude-sonnet-4-6"
      reason_tag: repair

default:
  delegate: balanced
Чтобы подтвердить, в какую модель разрешился запрос, прочитайте заголовки ответа X-Orca-Router и X-Orca-Resolved-Model.

Справочник API

DSL управляется на уровне маршрутизатора; запись требует роли Developer+.
Метод и путьРольНазначение
GET /api/user/routers/:id/dslMemberИсходник + версия + состояние теневого/канареечного режима.
PUT /api/user/routers/:id/dslDeveloper+Линтинг + сохранение (новая версия, с аудитом).
POST /api/user/routers/:id/dsl/lintMemberЛинтинг черновика → {errors:[…]}.
POST /api/user/routers/dsl/lintMemberЛинтинг без сохранения состояния (без id маршрутизатора).
POST /api/user/routers/:id/dsl/dryrunMemberВычислить синтетический запрос → трассировка + совпавшее правило.
GET /api/user/routers/:id/dsl/historyMemberИстория версий, новейшие сверху.
POST /api/user/routers/:id/dsl/rollback/:versionDeveloper+Повторный линтинг и восстановление старой версии.

FAQ

Это и есть стратегия — вариант dsl наряду с cheapest / quality / balanced / adaptive. Остальные выбирают по цене и качеству; DSL выбирает по правилам, которые вы пишете над формой запроса, классификацией и состоянием агента. Вы всё ещё можете использовать delegate: к встроенной стратегии как эффект правила или как default.
Применяется эффект default: верхнего уровня. Он обязателен, поэтому всегда есть определённый исход — обычно delegate: balanced или конкретная страховочная модель.
Да. CEL исполняется в песочнице только с функциями стандартной библиотеки, с дедлайном вычисления в несколько миллисекунд, регулярными выражениями RE2 (линейное время, без ReDoS) и без доступа к базе данных, сети или файловой системе. Окружение переменных — фиксированный набор скаляров и списков.
Тремя способами: выполните пробный прогон (dry-run) против синтетического запроса в редакторе, оставьте его в теневом режиме и прочитайте отчёт о расхождениях, затем проведите канареечный запуск на небольшом проценте живого трафика, прежде чем нарастить до 100%.