Routing DSL: componi un pannello di modelli che pensa come Fable 5

Routing DSL: componi un pannello di modelli che pensa come Fable 5

Data di pubblicazione

Torna a tutti gli articoli

Per due anni, la strategia per "più intelligenza" è stata "aspettare il prossimo modello." Pensiamo che questa sia l'unità di progresso sbagliata. La frontiera non è un singolo checkpoint — è un comitato. Dai a tre buoni modelli lo stesso problema difficile, lasciali dissentire e arbitra tra le risposte, e il comitato batte qualsiasi dei suoi membri. Spesso batte il modello successivo nella classifica dei prezzi.

Il Routing DSL è il modo in cui costruisci quel pannello. È una strategia di routing programmabile — YAML + CEL — che trasforma il tuo endpoint OrcaRouter in un grafo di inferenza: instradare per difficoltà, instradare per compito, distribuire a più modelli contemporaneamente, giudicare o votare i loro output, fare fallback quando la confidenza è bassa e ottimizzare il tutto per costi, latenza o qualità. Scrivi regole; il gateway le compila e le esegue su ogni richiesta in ~5 ms.

Questo post è un tour ingegneristico: la grammatica, le variabili su cui puoi diramarti, i quattro arbitri, la cascata e un set completo di regole di produzione alla fine.


Prima il risultato

Due benchmark illustrativi. (I numeri sono illustrativi — servono a mostrare la forma dell'effetto, non per essere citati come punteggi ufficiali.)

Frontier comparison — un endpoint DSL instradato per difficoltà vs. il solo Frontier:


Pannelli Fusion vs. modelli singoli — valutati su 93 di 100 compiti (da OpenRouter):


Tre cose che meritano di essere fissate:

Ogni pannello di fusione batte ciascuno dei suoi stessi membri. Opus 4.8 + GPT-5.5 (~67.5%) supera sia Opus da solo (~58.5%) che GPT-5.5 da solo (~60%) di 7–9 punti. Il disaccordo è segnale; l'arbitraggio lo raccoglie.

Fusion raggiunge il livello successivo. Tre pannelli diversi incrociano Fable 5 solo (~65.5%) utilizzando solo i modelli sotto di esso.

Non hai bisogno di membri costosi. Opus + Opus auto-fusione (~65.5%) eguaglia Fable 5 con un modello e un sampler. Un panel di economici modelli — Gemini 3 Flash + Kimi K2.6 + DeepSeek V4 Pro (~64.5%) — si colloca leggermente sotto Fable 5 a una frazione del costo per token. Questa è l'intera tesi: acquista intelligenza con la topologia, non con il prossimo livello di prezzo.

Il Routing DSL è la superficie di controllo che ti consente di spendere quella topologia solo dove paga — modelli economici sull'80% facile, un pannello di fusione sulla coda dura.


La grammatica in 30 secondi

Un ruleset è versione, un elenco di regole e un default obbligatorio. Le regole vengono valutate dall'alto verso il basso; la prima when: quella vera vince. No when: significa "sempre corrisponde".

versione: 1

rules:
  - id: only_rule
    use: { model: "claude-sonnet-4-6" }
default:
  delegate: balanced


Il when: è un'espressione booleana CEL — sandboxed, regex solo RE2, nessun loop, nessun I/O, valutazione in microsecondi, con un singolo deadline di 5 ms condiviso su tutto il ruleset. Il use: è l'effetto: dove va la richiesta e come è ottimizzata. I limiti sono volutamente piccoli (≤30 regole, ≤16 KiB di source, ≤200 caratteri per when:) così il ruleset rimane verificabile.


Primitive 1 — percorso per difficoltà e compito

Il distributore classifica ogni richiesta prima di instradarla ed espone le funzionalità a CEL. Puoi diramare direttamente su di esse:

versione: 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: balanced


Le variabili che puoi leggere in when: (abbreviato — consulta il riferimento completo nella documentazione):

Esempi di gruppo

Forma della richiesta

request.input_tokens, request.output_max_tokens, request.stream, request.vision, request.message_count, request.has_tools


Classificazione

task_class (chat/code/agent/vision/audio/rag/creative), difficulty (0.0–1.0), code_keyword_density, reasoning_cue_count, log_prompt_tokens, tool_count


Sessione 

agent_state.turn, agent_state.tools_used, agent_state.has_edited, agent_state.last_test_failed, agent_state.consecutive_errors, agent_state.models_tried


Contesto

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).


Qualsiasi destinazione può portare manopole per chiamata, tradotte nei parametri nativi di ogni provider dall'adattatore relay: reasoning_effort (basso/medio/alto), thinking_budget_tokens (1024–64000), samples (1–16), temperature (0.0–2.0), più param_override / header_override protetti da denylist. Questo è già sufficiente per costruire l'endpoint con routing basato sulla difficoltà dalla Tabella A: modello economico per la coda facile, Opus con un budget di pensiero per quella difficile.


Primitive 2 — espansione a ventaglio su un pannello (fusione)

È da qui che proviene l'incremento del benchmark. Un parallelo: l'effetto invia la richiesta a 2–5 percorsi contemporaneamente, quindi un arbitro decide ciò che il client vede effettivamente:

- 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"


Quattro strategie arbitrali, ciascuna una risposta diversa a "di chi vince l'output?":

primo — gareggia con le gambe, servi il primo successo, cancella i perdenti. Ottimizza latenza (ottieni il più veloce tra N).

maggioranza — voto strutturato tra gli output delle gambe, nessuna chiamata aggiuntiva al modello. Quando le gambe divergono senza una maggioranza stretta, il ramo opzionale on_disagreement: ri-invia un tentativo nuovo e più forte invece di fornire uno spareggio. Ottimizza robustezza nei compiti con una risposta canonica.

best_of_n — un giudice LLM legge tutti i candidati e li classifica. Questa è la configurazione Opus + GPT-5.5 → judge della Tabella B. Ottimizza la qualità su lavori aperti; ricade su first-successful se il giudice commette errori.

test superatibasato sull'esecuzione: serve il candidato la cui patch fa effettivamente passare la suite di test. Nessun giudice che indovina — è il sistema di test a decidere. Questo è il più forte arbitro per il lavoro su codice/agenti. Il verificatore risiede al di fuori del gateway (collegato tramite un VerifierProvider); senza alcun verificatore collegato, degrada al primo riuscito.

max_latency_ms (1000–600000, default 120000) limita il fan-out in modo che un ramo lento non possa bloccare la risposta — i ritardatari vengono eliminati. L'annidamento di parallel all'interno di parallel viene rifiutato al lint; il pannello è intenzionalmente profondo un solo livello.

Nota sulla disponibilità: il runtime di fan-out N-way è subordinato al flag del server ROUTING_DSL_ENSEMBLE_RUNTIME mentre la fatturazione per gamba è stata resa stabile in staging — ecco perché la fusione è in anteprima, non GA. Con il flag disattivato, una regola parallel: serve correttamente la sua prima gamba, quindi puoi creare e shadoware i tuoi pannelli oggi e attivarli quando la fusione arriverà nella tua regione.


Primitiva 3 — fallback e cascate di confidenza

Fan-out spende N× in anticipo. Una cascata spende extra solo quando la prima risposta sembra sbagliata. Dopo la risposta, on_low_confidence: valuta i segnali e, se uno si attiva, reinvia a una destinazione più 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"


I segnali: patch_invalid (il diff fallisce git apply --check), self_doubt (un set di regex per frasi di attenuazione), low_logprob (logprob medio del token sotto soglia, dove il provider lo espone), e next_turn_test_failed (un latch tra turni — il prompt di questo turno porta la forma dei test falliti del turno precedente). Le cascate sono depth-1 per progettazione. Abbinale con agent_state.models_tried per ottenere diversità al riprovare — non inviare mai la riparazione al modello che ha appena fallito.


Regolare la manopola: costo, latenza, qualità

Lo stesso DSL esprime tutti e tre gli obiettivi; scegli per regola:

Costo — delegato: il più economico, mantieni il modello economico sulla coda facile, e riserva il fan-out per difficoltà > 0.7. Il pannello economico della Tabella B (~64.5% ≈ Fable 5 solo) è la prova di esistenza: una fusione di modelli piccoli può sostituire un modello di frontiera a una frazione del costo per token. Sii chiaro, però — la fusione utilizza il "fattura ogni gamba" modello: un pannello best_of_n a 3 gambe fattura tre candidati più il giudice. L'economia funziona perché (a) fai il fan-out solo sulla minoranza difficile delle richieste e (b) fondi più economici membri rispetto al modello di frontiera che stai sostituendo.

Latenza — arbiter: { strategy: first } più uno stretto max_latency_ms ti dà il più veloce di N con un tetto massimo.

Qualità — best_of_n per lavori aperti, tests_pass quando c'è una suite su cui basarsi. samples e thinking_budget_tokens acquistano di più in un unico turno.


Operandolo senza rompere prod

I cambiamenti di routing sono spaventosi, quindi il DSL è dotato delle barriere di sicurezza che un SRE si aspetta:

Lint su ogni salvataggio — schema, CEL type-check (ogni when: deve valutare a bool), risoluzione dei ref, range dei knob, denylist di header/param. Gli errori vengono restituiti come {line, column, message, rule} e visualizzati come gutter chips nell'editor.

Prova a secco — Invia una richiesta sintetica tramite POST (task_class, difficulty, agent_state, …) e ricevi la regola corrispondente, l'effetto risolto e il tempo di valutazione prima che qualsiasi cosa venga spedita.

Modalità ombra — per 24 ore dopo il primo salvataggio, il DSL viene valutato ma non utilizzato; un log ombra registra le selezioni potenziali e la console mostra una differenza (percentuale di rotte modificate, delta del costo giornaliero previsto, conteggi di attivazione per regola).

Canary— un cursore di traffico 0–100. Rampa 5 → 25 → 50 → 100 osservando le metriche per slice; annulla portando a 0.

Audit + rollback — ogni salvataggio/rollback scrive una riga di audit nella stessa transazione; le modifiche concorrenti ricevono un 409 con la versione corrente, in modo da poter riprovare con lo stato aggiornato.

Casi di test, replay di tracce e una vista AI 'spiega questo set di regole' completano il tutto. Lo trovi nella dashboard sotto routing → strategy → DSL.


Un set completo di regole

Economico su facile, medio su medio, un pannello di fusione giudicato sulla coda agente dura, con una cascata di fiducia sotto:

versione: 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: balanced


Quell'endpoint è quello che si trova in cima alla Tabella A — non perché abbia trovato un modello migliore, ma perché spende il modello giusto per la richiesta giusta e fonde un pannello esattamente dove il pannello vince.


Inizia a comporre

Il prossimo salto di capacità non deve aspettare il prossimo checkpoint. È un grafo che puoi scrivere oggi pomeriggio: instrada per difficoltà, dirama sulla coda dura, giudica o testa gli output, cascata quando la fiducia cala.

Documentazione: https://docs.orcarouter.ai/routing/routing-dsl

UI: routing → Crea router -> Strategia di routing → DSL (esperto)

La Frontier è un pannello. Vai a costruire il tuo.