Saltar para o conteúdo principal
As estratégias integradas — cheapest, quality, balanced, adaptive — escolhem um modelo por preço e qualidade. A Routing DSL é o nível abaixo disso, para quando o modelo certo depende do que a requisição realmente é: um turno longo de codificação agêntica, uma chamada barata de classificação, uma requisição de visão, uma nova tentativa após um teste que falhou. Você escreve regras; o gateway as avalia por requisição e roteia de acordo. É a estratégia dsl de um roteador nomeado — então sua aplicação continua chamando orcarouter/{name} e a lógica de roteamento fica no painel, versionada e editável sem uma reimplantação.

Quando recorrer à DSL

Use uma estratégia integrada quando “modelo ativo mais barato” ou “melhor qualidade” captura sua intenção. Recorra à DSL quando o roteamento depende do conteúdo ou contexto da requisição:
  • Especialização de tarefa — envie código para um modelo de codificação, visão para um modelo de visão, chat barato para um modelo barato.
  • Roteamento ciente de dificuldade — escale apenas as requisições difíceis para um modelo caro; mantenha as fáceis baratas.
  • Roteamento ciente do agente — roteie de forma diferente com base no estado da sessão (quais ferramentas o agente usou, se os testes acabaram de falhar, em quantos turnos ele está).
  • Regras de tempo / inquilino / cabeçalho — roteamento diferente por hora, grupo de usuários ou um cabeçalho de requisição.

Habilitando

No painel em Routing, abra um roteador e defina sua Estratégia como DSL. Isso revela o editor da DSL para este roteador. Todo o resto sobre o roteador ainda se aplica — o glob de Modelos permitidos, a rede de segurança do Modelo padrão e a invocação orcarouter/{name}.

O editor

O editor foi construído para levá-lo da intenção a um conjunto de regras funcional rapidamente:
  • Templates semeados com os modelos reais do seu workspace (via um diálogo único de mapeamento de níveis), para que você nunca comece de um arquivo em branco ou esbarre em um muro de “modelo desconhecido”.
  • Insert — insira um Model, um Router (orcarouter/<name>), ou um Pool do autocomplete em vez de digitar identificadores à mão.
  • Generate — descreva o roteamento que você quer em linguagem natural e receba de volta DSL compilada e sem erros de lint, fundamentada em seus modelos reais.
  • Explain — uma paráfrase em linguagem simples do que o conjunto de regras atual faz.
  • Lint inline — todo erro reporta {line, column, message} e todo código de lint tem um explicador ?. A precedência (a primeira correspondência vence) e os padrões CEL comuns são exibidos no local.

Estrutura do arquivo

Um conjunto de regras é YAML com três chaves de nível superior:
version: 1              # required — currently must be 1
rules: [...]            # required — 1 to 30 rules, evaluated in order
default: {...}          # required — the effect when no rule matches
Uma regra é uma condição when: e um efeito 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
As regras são avaliadas de cima para baixo; a primeira regra cuja when: é verdadeira vence. Se nenhuma corresponder, default: se aplica. Ordene suas regras das mais específicas primeiro — uma regra ampla logo no início ofusca tudo abaixo dela.

when: — a condição

As condições são escritas em CEL (Common Expression Language): segura por design — sem loops, sem I/O, avaliação em microssegundos, apenas regex RE2. Estes seis padrões cobrem a vasta maioria das regras reais:
PadrãoExemplo
Acesso a campotask_class == "agent"
Comparação numéricadifficulty > 0.6 && request.input_tokens < 50000
Lógica booleanaagent_state.has_edited && !agent_state.has_run_tests
Pertencimento a lista"Edit" in agent_state.tools_used
Macro de regexsystem_prompt_matches("(?i)planning agent")
Macro de ferramentatool_calls_present_any(["Edit","Write","apply_patch"])

Variáveis

Formato da requisição
VariávelTipo
modelstring
request.input_tokensint
request.output_max_tokensint
request.streambool
request.visionbool
request.message_countint
request.has_system_promptbool
request.has_toolsbool
Classificação (computada pelo gateway por requisição)
VariávelTipoSignificado
task_classstringchat / code / agent / vision / audio / rag / creative
difficultydouble0.01.0
code_keyword_densitydouble0.01.0
reasoning_cue_countintindícios de raciocínio detectados no prompt
tool_countintdefinições distintas de ferramentas na requisição
Sessão do agente (agent_state.*, persistida ao longo de uma conversa)
VariávelTipo
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>
Contexto
VariávelTipo
headers["x-foo"]string
user.id / user.groupint / string
token.id / token.nameint / string
time.hour / time.weekdayint (UTC)
workspace.idint

Macros

Funções CEL registradas para as verificações comuns de “olhar dentro da requisição”:
MacroRetorna
system_prompt_matches(regex)RE2 sobre as mensagens de sistema unidas
user_message_matches(regex)RE2 sobre a última mensagem do usuário
tool_definitions_include(name)uma ferramenta está declarada na requisição
tool_calls_present_any(list)a requisição carrega qualquer uma dessas chamadas de ferramenta
tool_results_from_any(list)a requisição tem mensagens de papel tool de qualquer um
header_matches(name, regex)RE2 sobre o valor de um cabeçalho

use: — o efeito

Um bloco use: nomeia um destino (exatamente um) e qualquer número de knobs opcionais por chamada.

Destino

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 é rejeitado (causaria recursão). Fixar em canais específicos (channels: / @channel:) não está disponível atualmente e gera lint como não suportado — roteie por model, models ou pool em vez disso.

Knobs por chamada

Combine com qualquer destino para moldar a chamada upstream:
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 e header_override impõem uma lista de bloqueio — você não pode sobrescrever model, messages, stream, tools, cabeçalhos de autenticação, etc. (isso subverteria a cobrança, a auditoria ou o estado do agente).

Cascatas de confiança e ensembles (avançado)

Dois efeitos avançados permitem que uma regra reaja a uma primeira resposta fraca ou se ramifique por vários modelos. Eles são escritos da mesma forma que qualquer regra. Cascade — tente novamente diante de um sinal de baixa confiança com um efeito mais forte:
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 — emita várias pernas em paralelo e deixe um árbitro escolher:
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
O runtime de ensemble / cascade é restrito e desligado por padrão. Como cada perna paralela e cada reparo de cascata é cobrado como sua própria chamada, o runtime de ramificação fica atrás de uma flag de servidor enquanto a cobrança por perna é validada. Com ele desligado, uma regra parallel: serve apenas a primeira perna e uma cascata registra seu sinal mas não redespacha — o conjunto de regras ainda passa pelo lint, salva e roteia seu efeito primário normalmente. Entre em contato conosco para habilitar o runtime de ensemble para seu workspace.

Implantando com segurança

Um novo conjunto de regras não assume seu tráfego no momento em que você o salva:
  • Modo shadow — por uma janela após o primeiro salvamento, a DSL é avaliada mas não usada: sua estratégia anterior ainda serve o tráfego enquanto o gateway registra o que a DSL teria feito. O painel mostra um relatório de diff — porcentagem de rotas divergentes, delta de custo projetado, contagens de disparo por regra e com que frequência caiu para default:. Leia-o antes de confiar nas regras.
  • Canary — aumente gradualmente a DSL para uma porcentagem do tráfego ao vivo (5 → 25 → 50 → 100), observando métricas por fatia, e reverta instantaneamente deslizando a porcentagem para 0.
Você também pode fazer um dry-run de um conjunto de regras contra uma requisição sintética (classe da tarefa, dificuldade, estado do agente, formato da requisição) direto no editor e ver o trace e a regra correspondente — sem tráfego, nada persistido.

Limites e validação

Todo salvamento executa um lint estrito; conjuntos de regras inválidos são rejeitados com {line, column, message, rule}:
  • Schema — chaves obrigatórias, tipos/enums corretos, sem campos desconhecidos.
  • Tamanho — ≤ 30 regras, ≤ 16 KiB de YAML, ≤ 200 caracteres por when:.
  • CEL — faz parse, verifica tipos contra o ambiente de variáveis, sem identificadores desconhecidos, e when: deve avaliar para um bool.
  • Efeito — exatamente um destino por bloco use:; todas as referências model / models / @pool: devem resolver em seu workspace.
  • Faixas de knobsthinking_budget_tokens ∈ [1024, 64000], temperature ∈ [0, 2], samples ∈ [1, 16].
  • Reservado — ids de regra começando com _ são reservados; default como id de regra é rejeitado (use o bloco default: de nível superior).
Todo salvamento e rollback escreve uma linha de auditoria; edições concorrentes são detectadas e o segundo salvamento é solicitado a tentar novamente contra um estado atualizado.

Um exemplo completo

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
Para confirmar para qual modelo uma requisição resolveu, leia os cabeçalhos de resposta X-Orca-Router e X-Orca-Resolved-Model.

Referência da API

A DSL é gerenciada por roteador; escritas exigem Developer+.
Método e caminhoPapelPropósito
GET /api/user/routers/:id/dslMemberFonte + versão + estado de shadow/canary.
PUT /api/user/routers/:id/dslDeveloper+Lint + salvar (nova versão, auditada).
POST /api/user/routers/:id/dsl/lintMemberLint de um rascunho → {errors:[…]}.
POST /api/user/routers/dsl/lintMemberLint sem estado (sem id de roteador).
POST /api/user/routers/:id/dsl/dryrunMemberAvaliar uma requisição sintética → trace + regra correspondente.
GET /api/user/routers/:id/dsl/historyMemberHistórico de versões, mais recente primeiro.
POST /api/user/routers/:id/dsl/rollback/:versionDeveloper+Refazer o lint e restaurar uma versão mais antiga.

FAQ

Ela é uma estratégia — a opção dsl ao lado de cheapest / quality / balanced / adaptive. As outras escolhem por preço e qualidade; a DSL escolhe por regras que você escreve sobre o formato, a classificação e o estado do agente da requisição. Você ainda pode usar delegate: para uma estratégia integrada como efeito de uma regra ou como o default.
O efeito default: de nível superior se aplica. Ele é obrigatório, então há sempre um resultado definido — comumente delegate: balanced ou um modelo específico de rede de segurança.
Sim. O CEL roda em um sandbox apenas com funções da biblioteca padrão, um prazo de avaliação de alguns milissegundos, regex RE2 (tempo linear, sem ReDoS) e nenhum acesso ao banco de dados, à rede ou ao sistema de arquivos. O ambiente de variáveis é um conjunto fixo de escalares e listas.
De três formas: faça um dry-run contra uma requisição sintética no editor, deixe-o em modo shadow e leia o relatório de diff, e então faça o canary dele em uma pequena porcentagem do tráfego ao vivo antes de subir para 100%.