Vai al contenuto principale
Le strategie integrate — cheapest, quality, balanced, adaptive — scelgono un modello in base a prezzo e qualità. Il Routing DSL è il livello sottostante, per quando il modello giusto dipende da cos’è realmente la richiesta: un lungo turno di coding agentico, una chiamata di classificazione economica, una richiesta di vision, un retry dopo un test fallito. Tu scrivi le regole; il gateway le valuta per ciascuna richiesta e instrada di conseguenza. È la strategia dsl di un router con nome — così la tua applicazione continua a chiamare orcarouter/{name} e la logica di routing vive nella dashboard, versionata e modificabile senza una ridistribuzione.

Quando ricorrere al DSL

Usa una strategia integrata quando “modello attivo più economico” o “migliore qualità” cattura il tuo intento. Ricorri al DSL quando il routing dipende dal contenuto o dal contesto della richiesta:
  • Specializzazione per task — invia il codice a un modello di coding, la vision a un modello di vision, la chat economica a un modello economico.
  • Routing consapevole della difficoltà — escala verso un modello costoso solo le richieste difficili; mantieni economiche quelle facili.
  • Routing consapevole dell’agente — instrada in modo diverso in base allo stato della sessione (quali strumenti l’agente ha usato, se i test sono appena falliti, a che turno si trova).
  • Regole per tempo / tenant / header — routing diverso per ora, gruppo utenti o un header della richiesta.

Abilitarlo

Nella dashboard sotto Routing, apri un router e imposta la sua Strategia su DSL. Questo rivela l’editor DSL per questo router. Tutto il resto del router continua ad applicarsi — il glob dei Modelli consentiti, la rete di sicurezza del Modello predefinito e l’invocazione orcarouter/{name}.

L’editor

L’editor è progettato per portarti dall’intento a un ruleset funzionante rapidamente:
  • Template seedati con i modelli reali del tuo workspace (tramite un dialog di mappatura dei tier una tantum), così non parti mai da un file vuoto né ti scontri con un muro di “modello sconosciuto”.
  • Insert — inserisci un Model, un Router (orcarouter/<name>) o un Pool dall’autocompletamento invece di digitare gli identificatori a mano.
  • Generate — descrivi il routing che vuoi in linguaggio naturale e ottieni in cambio DSL compilato e privo di errori di lint, fondato sui tuoi modelli reali.
  • Explain — una parafrasi in inglese semplice di ciò che fa il ruleset corrente.
  • Lint inline — ogni errore riporta {line, column, message} e ogni codice di lint ha uno spiegatore ?. La precedenza (vince il primo match) e i pattern CEL comuni sono mostrati in loco.

Struttura del file

Un ruleset è YAML con tre chiavi di primo livello:
version: 1              # required — currently must be 1
rules: [...]            # required — 1 to 30 rules, evaluated in order
default: {...}          # required — the effect when no rule matches
Una regola è una condizione when: e un effetto 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
Le regole vengono valutate dall’alto verso il basso; vince la prima regola il cui when: è vero. Se nessuna corrisponde, si applica default:. Ordina le tue regole partendo dalla più specifica — una regola ampia messa presto oscura tutto ciò che sta sotto.

when: — la condizione

Le condizioni sono scritte in CEL (Common Expression Language): sicuro per progettazione — niente loop, niente I/O, valutazione in microsecondi, solo regex RE2. Questi sei pattern coprono la stragrande maggioranza delle regole reali:
PatternEsempio
Accesso a un campotask_class == "agent"
Confronto numericodifficulty > 0.6 && request.input_tokens < 50000
Logica booleanaagent_state.has_edited && !agent_state.has_run_tests
Appartenenza a una lista"Edit" in agent_state.tools_used
Macro regexsystem_prompt_matches("(?i)planning agent")
Macro per strumentitool_calls_present_any(["Edit","Write","apply_patch"])

Variabili

Forma della richiesta
VariabileTipo
modelstring
request.input_tokensint
request.output_max_tokensint
request.streambool
request.visionbool
request.message_countint
request.has_system_promptbool
request.has_toolsbool
Classificazione (calcolata dal gateway per ogni richiesta)
VariabileTipoSignificato
task_classstringchat / code / agent / vision / audio / rag / creative
difficultydouble0.01.0
code_keyword_densitydouble0.01.0
reasoning_cue_countintindizi di ragionamento rilevati nel prompt
tool_countintdefinizioni di strumenti distinte nella richiesta
Sessione dell’agente (agent_state.*, persistita lungo una conversazione)
VariabileTipo
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>
Contesto
VariabileTipo
headers["x-foo"]string
user.id / user.groupint / string
token.id / token.nameint / string
time.hour / time.weekdayint (UTC)
workspace.idint

Macro

Funzioni CEL registrate per i comuni controlli “guarda dentro la richiesta”:
MacroRestituisce
system_prompt_matches(regex)RE2 sui messaggi di sistema uniti
user_message_matches(regex)RE2 sull’ultimo messaggio dell’utente
tool_definitions_include(name)uno strumento è dichiarato nella richiesta
tool_calls_present_any(list)la richiesta contiene una qualsiasi di queste chiamate a strumenti
tool_results_from_any(list)la richiesta ha messaggi con ruolo tool provenienti da uno qualsiasi
header_matches(name, regex)RE2 sul valore di un header

use: — l’effetto

Un blocco use: nomina una destinazione (esattamente una) e un numero qualsiasi di knob opzionali per chiamata.

Destinazione

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 viene rifiutato (ricorrerebbe su se stesso). Il pinning a canali specifici (channels: / @channel:) non è attualmente disponibile e il lint lo segnala come non supportato — instrada invece per model, models o pool.

Knob per chiamata

Combinali con qualsiasi destinazione per modellare la chiamata 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 applicano una denylist — non puoi sovrascrivere model, messages, stream, tools, gli header di auth, ecc. (lo farebbero a discapito di billing, audit o stato dell’agente).

Cascate di confidenza ed ensemble (avanzato)

Due effetti avanzati permettono a una regola di reagire a una prima risposta debole o di distribuirsi su più modelli. Si definiscono nello stesso modo di qualsiasi regola. Cascade — riprova su un segnale di bassa confidenza con un effetto più 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 — emette più leg in parallelo e lascia che un arbiter scelga:
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
Il runtime di ensemble / cascade è gated e disattivato per impostazione predefinita. Poiché ogni leg parallela e ogni riparazione di cascade viene fatturata come una chiamata a sé, il runtime di fan-out è dietro un flag del server mentre il billing per leg viene validato. Con esso disattivato, una regola parallel: serve solo la prima leg e una cascade registra il proprio segnale ma non effettua un nuovo dispatch — il ruleset supera comunque il lint, viene salvato e instrada normalmente il suo effetto primario. Contattaci per abilitare il runtime di ensemble per il tuo workspace.

Distribuire in sicurezza

Un nuovo ruleset non prende il controllo del tuo traffico nel momento in cui lo salvi:
  • Shadow mode — per una finestra dopo il primo salvataggio, il DSL viene valutato ma non usato: la tua strategia precedente continua a servire il traffico mentre il gateway registra cosa avrebbe fatto il DSL. La dashboard mostra un report di diff — percentuale di route divergenti, delta di costo previsto, conteggi di attivazione per regola e con quale frequenza è caduto su default:. Leggilo prima di fidarti delle regole.
  • Canary — porta il DSL su una percentuale di traffico reale (5 → 25 → 50 → 100), osservando le metriche per slice, e fai rollback istantaneo riportando la percentuale a 0.
Puoi anche fare un dry-run di un ruleset contro una richiesta sintetica (classe del task, difficoltà, stato dell’agente, forma della richiesta) direttamente nell’editor e vedere la trace e la regola corrispondente — nessun traffico, nulla persistito.

Limiti e validazione

Ogni salvataggio esegue un lint rigoroso; i ruleset non validi vengono rifiutati con {line, column, message, rule}:
  • Schema — chiavi obbligatorie, tipi/enum corretti, nessun campo sconosciuto.
  • Dimensione — ≤ 30 regole, ≤ 16 KiB di YAML, ≤ 200 caratteri per when:.
  • CEL — fa il parse, supera il type-check rispetto all’ambiente delle variabili, nessun identificatore sconosciuto, e when: deve valutare a un bool.
  • Effetto — esattamente una destinazione per blocco use:; tutti i riferimenti model / models / @pool: devono risolvere nel tuo workspace.
  • Range dei knobthinking_budget_tokens ∈ [1024, 64000], temperature ∈ [0, 2], samples ∈ [1, 16].
  • Riservati — gli id di regola che iniziano con _ sono riservati; default come id di regola viene rifiutato (usa il blocco default: di primo livello).
Ogni salvataggio e rollback scrive una riga di audit; le modifiche concorrenti vengono rilevate e al secondo salvataggio viene chiesto di riprovare contro lo stato aggiornato.

Un esempio 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
Per confermare a quale modello una richiesta si è risolta, leggi gli header di risposta X-Orca-Router e X-Orca-Resolved-Model.

Riferimento API

Il DSL è gestito per router; le scritture richiedono Developer+.
Metodo e pathRuoloScopo
GET /api/user/routers/:id/dslMemberSource + versione + stato shadow/canary.
PUT /api/user/routers/:id/dslDeveloper+Lint + salvataggio (nuova versione, con audit).
POST /api/user/routers/:id/dsl/lintMemberLint di una bozza → {errors:[…]}.
POST /api/user/routers/dsl/lintMemberLint stateless (nessun id di router).
POST /api/user/routers/:id/dsl/dryrunMemberValuta una richiesta sintetica → trace + regola corrispondente.
GET /api/user/routers/:id/dsl/historyMemberCronologia delle versioni, dalla più recente.
POST /api/user/routers/:id/dsl/rollback/:versionDeveloper+Ripete il lint e ripristina una versione precedente.

FAQ

È una strategia — l’opzione dsl accanto a cheapest / quality / balanced / adaptive. Le altre scelgono in base a prezzo e qualità; il DSL sceglie in base alle regole che scrivi sulla forma della richiesta, sulla classificazione e sullo stato dell’agente. Puoi comunque usare delegate: verso una strategia integrata come effetto di una regola o come default.
Si applica l’effetto default: di primo livello. È obbligatorio, quindi c’è sempre un esito definito — comunemente delegate: balanced o uno specifico modello di rete di sicurezza.
Sì. CEL gira in una sandbox con sole funzioni di libreria standard, un deadline di valutazione di pochi millisecondi, regex RE2 (tempo lineare, niente ReDoS) e nessun accesso al database, alla rete o al filesystem. L’ambiente delle variabili è un insieme fisso di scalari e liste.
Tre modi: fai un dry-run contro una richiesta sintetica nell’editor, lascialo in shadow mode e leggi il report di diff, poi fai il canary su una piccola percentuale di traffico reale prima di salire al 100%.