Routing DSL: compor um painel de modelos que pensa como o Fable 5
Por dois anos, o manual para 'mais inteligência' tem sido 'esperar pelo próximo modelo.' Achamos que essa é a unidade errada de progresso. A fronteira não é um único ponto de verificação — é um painel. Dê a três bons modelos o mesmo problema difícil, deixe-os discordar e arbitre entre as respostas, e o painel supera qualquer um de seus membros. Frequentemente, ele supera o próximo modelo na tabela de preços.
O DSL de Roteamento é como você constrói esse painel. É uma estratégia de roteamento programável — YAML + CEL — que transforma seu endpoint OrcaRouter em um grafo de inferência: rotear por dificuldade, rotear por tarefa, distribuir para vários modelos de uma vez, julgar ou votar em suas saídas, fazer fallback quando a confiança é baixa, e ajustar tudo para custo, latência ou qualidade. Você escreve regras; o gateway as compila e executa em cada requisição em ~5 ms.
Este post é o tour de engenharia: a gramática, as variáveis nas quais você pode ramificar, os quatro árbitros, a cascata e um conjunto completo de regras de produção no final.
O resultado primeiro
Dois benchmarks ilustrativos. (Os números são ilustrativos — destinam-se a mostrar a forma do efeito, não para serem citados como pontuações oficiais.)
Comparação do Frontier — um endpoint DSL roteado por dificuldade vs. a fronteira solo:

Painéis Fusion vs. modelos solo — avaliado em 93 de 100 tarefas (do OpenRouter):

Três coisas que valem a pena contemplar:
Todo painel de fusão supera cada um de seus próprios membros. Opus 4.8 + GPT-5.5 (~67.5%) supera tanto Opus solo (~58.5%) quanto GPT-5.5 solo (~60%) por 7 a 9 pontos. Desacordo é sinal; a arbitragem o colhe.
Fusion atinge o próximo nível. Três painéis diferentes cruzam Fable 5 solo (~65.5%) usando apenas os modelos abaixo dele.
Você não precisa de membros caros. Opus + Opus autofusão (~65.5%) corresponde ao Fable 5 com um modelo e um amostrador. Um painel de baratos modelos — Gemini 3 Flash + Kimi K2.6 + DeepSeek V4 Pro (~64.5%) — fica um pouco abaixo do Fable 5 por uma fração do custo por token. Essa é a tese: compre inteligência com topologia, não com o próximo nível de preço.
O Routing DSL é a superfície de controle que permite gastar essa topologia apenas onde compensa — modelos baratos nos fáceis 80%, um painel de fusão na cauda difícil.
A gramática em 30 segundos
Um conjunto de regras é versão, uma lista de regras, e um padrão obrigatório. As regras são avaliadas de cima para baixo; o primeiro quando: que é verdadeiro vence. Nenhum quando: significa "sempre corresponde".
versão: 1
rules:
- id: only_rule
use: { model: "claude-sonnet-4-6" }
default:
delegate: balancedO when: é uma CEL expressão booleana — isolado, regex somente RE2, sem loops, sem I/O, avaliação em microssegundos, com um único prazo de 5 ms compartilhado em todo o conjunto de regras. O use: é o efeito: para onde a solicitação vai e como ela é ajustada. Os limites são deliberadamente pequenos (≤30 regras, ≤16 KiB de código fonte, ≤200 caracteres por when:) para que um conjunto de regras permaneça auditável.
Primitivo 1 — rota por dificuldade e tarefa
O distribuidor classifica cada solicitação antes do roteamento e expõe as funcionalidades ao CEL. Você ramifica diretamente nelas:
versão: 1
rules:
- id: hard_reasoning
when: difficulty > 0.8
use:
model: "claude-opus-4-8"
reasoning_effort: "high"
thinking_budget_tokens: 32000
- id: code_path
when: task_class == "code" && code_keyword_density > 0.5
use: { model: "gpt-5.5" }
- id: cheap_chat
when: difficulty < 0.3
use: { model: "gemini-3-flash" }
default:
delegate: balancedAs variáveis que você pode ler em when: (abreviado — veja a referência completa na documentação):
Exemplos de Grupo
Forma da solicitação
request.input_tokens, request.output_max_tokens, request.stream, request.vision, request.message_count, request.has_toolsClassificação
task_class (chat/code/agent/vision/audio/rag/creative), difficulty (0.0–1.0), code_keyword_density, reasoning_cue_count, log_prompt_tokens, tool_countSessão
agent_state.turn, agent_state.tools_used, agent_state.has_edited, agent_state.last_test_failed, agent_state.consecutive_errors, agent_state.models_triedContexto
headers["x-…"], user.group, token.name, time.hour, workspace.id
…plus six macros for the things regex-over-payload is good at: system_prompt_matches(re), user_message_matches(re), tool_definitions_include(name), tool_calls_present_any([…]), tool_results_from_any([…]), header_matches(name, re).Qualquer destino pode carregar botões por chamada, traduzidos para parâmetros nativos de cada provedor pelo adaptador de retransmissão: reasoning_effort (low/medium/high), thinking_budget_tokens (1024–64000), samples (1–16), temperature (0.0–2.0), além de param_override / header_override protegidos por lista de negação. Isso já é suficiente para construir o endpoint roteado por dificuldade da Tabela A: modelo barato na cauda fácil, Opus com um orçamento de pensamento na cauda difícil.
Primitivo 2 — distribuir em leque para um painel (fusão)
É daqui que vem o ganho do benchmark. Um efeito paralelo: despacha a requisição para 2–5 ramos simultaneamente, então um árbitro decide o que o cliente realmente vê:
- id: hard_tail_panel
when: difficulty > 0.7 && task_class == "agent"
use:
parallel:
- { model: "anthropic/claude-opus-4-8", reasoning_effort: "high" }
- { model: "openai/gpt-5.5", thinking_budget_tokens: 16000 }
- { model: "google/gemini-3.1-pro", temperature: 0.3 }
arbiter:
strategy: best_of_n
model: "anthropic/claude-sonnet-4-6" # the judge
template: judge_code
max_latency_ms: 120000
on_disagreement: # majority-only escape hatch
model: "anthropic/claude-opus-4-8"
reasoning_effort: "high"Quatro estratégias de árbitro, cada uma uma resposta diferente para "qual saída vence?":
primeiro — corra as pernas, sirva o primeiro sucesso, cancele os perdedores. Otimiza a latência (você obtém o mais rápido de N).
maioria — voto estruturado entre as saídas dos ramos, sem chamada extra de modelo. Quando os ramos divergem sem maioria estrita, o branch opcional on_disagreement: reenvia uma nova tentativa mais forte em vez de servir um desempate. Otimiza robustez em tarefas com uma resposta canônica.
best_of_n — um juiz LLM lê todos os candidatos e os classifica. Esta é a configuração Opus + GPT-5.5 → judge da Tabela B. Otimiza qualidade em trabalho aberto; retorna ao primeiro bem-sucedido se o juiz errar.
testes_passam — execução-fundamentada: atende o candidato cujo patch realmente faz a suíte de testes passar. Sem adivinhação de juiz — o harness decide. Este é o árbitro mais forte para trabalho de código/agente. O verificador vive fora do gateway (conectado via um VerifierProvider); sem nenhum conectado, ele degrada para o primeiro bem-sucedido.
max_latency_ms (1000–600000, padrão 120000) limita o fan-out para que uma perna lenta não possa travar a resposta — os retardatários são descartados. Aninhar parallel dentro de parallel é rejeitado no lint; o painel é intencionalmente de um nível de profundidade.
Nota de disponibilidade: o runtime de fan-out N-way é controlado pela flag do servidor ROUTING_DSL_ENSEMBLE_RUNTIME enquanto o faturamento por perna está estabilizado no staging — é por isso que fusion está pré-visualização, não GA. Com a flag desativada, uma regra parallel: serve limpidamente sua primeira perna, então você pode criar e espelhar seus painéis hoje e ativá-los quando fusion chegar em sua região.
Primitivo 3 — fallbacks e cascatas de confiança
Fan-out gasta N× adiantado. Uma cascata gasta extra apenas quando a primeira resposta parece errada. Após a resposta, on_low_confidence: avalia sinais e, se um dispara, re-despacha para um destino mais forte:
- id: agent_with_safety_net
when: task_class == "agent"
use:
pool: "@pool:fast"
on_low_confidence:
signals: [patch_invalid, self_doubt, next_turn_test_failed]
threshold: { low_logprob: -1.5 }
use:
model: "claude-opus-4-8"
reasoning_effort: "high"Os sinais: patch_invalid (o diff falha no git apply --check), self_doubt (um conjunto de regex de frases de hesitação), low_logprob (logprob médio por token abaixo do limite, desde que o provedor o exponha), e next_turn_test_failed (um trinco entre turnos — o prompt deste turno carrega a forma dos testes que falharam no turno anterior). Cascatas são profundidade-1 por design. Combine-os com agent_state.models_tried para obter diversidade na repetição — nunca envie o reparo para o modelo que acabou de falhar.
Ajustando o dial: custo, latência, qualidade
A mesma DSL expressa todos os três objetivos; você escolhe por regra:
Custo — delegate: cheapest, keep the cheap model on the easy tail, and reserve fan-out for difficulty > 0.7. Table B's cheap panel (~64.5% ≈ Fable 5 solo) is the existence proof: uma fusão de modelos pequenos pode substituir um modelo frontier a uma fração do custo por token. Seja realista, porém — a fusão usa o "cobrar cada perna" modelo: um painel best_of_n de 3 pernas cobra três candidatos mais o juiz. A economia funciona porque você (a) só faz fan-out na minoria difícil das solicitações e (b) funde mais baratos membros do que o modelo frontier que você está substituindo.
Latência — árbitro: { estratégia: first } além de um max_latency_ms restrito fornece o mais rápido de N com um teto rígido.
Qualidade — best_of_n para trabalho aberto, tests_pass quando há um conjunto de testes para fundamentar. samples e thinking_budget_tokens compram mais dentro de uma única etapa.
Operá-lo sem quebrar o prod
Alterações de roteamento são assustadoras, então o DSL vem com os trilhos de segurança que um SRE espera:
Lint em cada salvamento — esquema, verificação de tipo CEL (todo 'when' deve ser avaliado como bool), resolução de referências, intervalos de knob, listas de bloqueio de cabeçalho/param. Erros retornam como {linha, coluna, mensagem, regra} e são exibidos como chips na margem do editor.
Simulação — Faça um POST de uma requisição sintética (task_class, difficulty, agent_state, …) e obtenha de volta a regra correspondente, o efeito resolvido e o tempo de avaliação antes que qualquer coisa seja enviada.
Modo sombra — por 24 h após o primeiro salvamento, o DSL é avaliado mas não usado; um shadow log registra as seleções potenciais e o console mostra um diff (percentual de rotas alteradas, delta de custo diário projetado, contagens de acionamentos por regra).
Canary — um controle deslizante de tráfego de 0 a 100. Aumente de 5 → 25 → 50 → 100 observando métricas por fatia; reverta deslizando para 0.
Auditoria + rollback — cada salvamento/rollback escreve uma linha de auditoria na mesma transação; edições concorrentes recebem um 409 com a versão atual para que você tente novamente com o estado atualizado.
Casos de teste, replicação de traces e uma visão de IA "explique este conjunto de regras" complementam. Você o encontra no painel emroteamento → estratégia → DSL.
Um conjunto completo de regras
Barato no fácil, médio no médio, um painel de fusão julgado na cauda agentiva difícil, com uma cascata de confiança por baixo:
versão: 1
rules:
- id: trivial
when: difficulty < 0.3 && !has_tools
use: { model: "gemini-3-flash" }
- id: standard
when: difficulty < 0.7
use:
model: "gpt-5.5"
on_low_confidence:
signals: [self_doubt, low_logprob]
use: { model: "claude-opus-4-8", reasoning_effort: "high" }
- id: hard_agent_panel
when: difficulty >= 0.7 && task_class == "agent"
use:
parallel:
- { model: "anthropic/claude-opus-4-8", reasoning_effort: "high" }
- { model: "openai/gpt-5.5", thinking_budget_tokens: 16000 }
- { model: "google/gemini-3.1-pro" }
arbiter:
strategy: tests_pass # execution-grounded; judged fallback if no harness
max_latency_ms: 180000
on_disagreement:
model: "claude-opus-4-8"
reasoning_effort: "high"
default:
delegate: balancedEsse endpoint é aquele que fica no topo da Tabela A — não porque encontrou um modelo melhor, mas porque gasta o modelo certo na solicitação certa e funde um painel exatamente onde o painel vence.
Iniciar composição
O próximo salto em capacidade não precisa esperar pelo próximo checkpoint. É um gráfico que você pode escrever esta tarde: rotear por dificuldade, espalhar na cauda difícil, julgar ou testar as saídas, cascatear quando a confiança cair.
Documentos: https://docs.orcarouter.ai/routing/routing-dsl
UI: roteamento → Criar roteador -> Estratégia de roteamento → DSL (especialista)
A fronteira é um painel. Vá construir o seu.
