Passer au contenu principal
Les stratégies intégrées — cheapest, quality, balanced, adaptive — choisissent un modèle selon le prix et la qualité. Le DSL de routage est le niveau en dessous de cela, pour quand le bon modèle dépend de ce qu’est réellement la requête : un long tour de codage agentique, un appel de classification bon marché, une requête de vision, une nouvelle tentative après un test échoué. Vous écrivez des règles ; la passerelle les évalue par requête et route en conséquence. C’est la stratégie dsl d’un routeur nommé — donc votre application continue d’appeler orcarouter/{name} et la logique de routage réside dans le tableau de bord, versionnée et modifiable sans redéploiement.

Quand recourir au DSL

Utilisez une stratégie intégrée quand « modèle vivant le moins cher » ou « meilleure qualité » capture votre intention. Recourez au DSL quand le routage dépend du contenu ou contexte de la requête :
  • Spécialisation par tâche — envoyez le code à un modèle de codage, la vision à un modèle de vision, le chat bon marché à un modèle bon marché.
  • Routage sensible à la difficulté — n’escaladez que les requêtes difficiles vers un modèle coûteux ; gardez les faciles bon marché.
  • Routage sensible à l’agent — routez différemment selon l’état de session (quels outils l’agent a utilisés, si les tests viennent d’échouer, à combien de tours il en est).
  • Règles d’heure / locataire / en-tête — routage différent selon l’heure, le groupe d’utilisateurs ou un en-tête de requête.

L’activer

Dans le tableau de bord sous Routing, ouvrez un routeur et réglez sa Stratégie sur DSL. Cela révèle l’éditeur DSL pour ce routeur. Tout le reste du routeur s’applique toujours — le glob Modèles autorisés, le filet de sécurité Modèle par défaut, et l’invocation orcarouter/{name}.

L’éditeur

L’éditeur est conçu pour vous faire passer de l’intention à un jeu de règles fonctionnel rapidement :
  • Templates ensemencés avec les modèles réels de votre espace de travail (via une boîte de dialogue de mappage de niveaux à usage unique), de sorte que vous ne partez jamais d’un fichier vide ni ne vous heurtez à un mur « modèle inconnu ».
  • Insert — déposez un Model, un Router (orcarouter/<name>), ou un Pool depuis l’autocomplétion au lieu de saisir les identifiants à la main.
  • Generate — décrivez le routage que vous voulez en langage clair et obtenez en retour un DSL compilé, propre au lint, ancré dans vos modèles réels.
  • Explain — une paraphrase en français simple de ce que fait le jeu de règles actuel.
  • Lint en ligne — chaque erreur rapporte {line, column, message} et chaque code de lint a un explicateur ?. La priorité (premier-match-gagne) et les motifs CEL courants sont mis en évidence sur place.

Structure du fichier

Un jeu de règles est du YAML avec trois clés de premier niveau :
version: 1              # required — currently must be 1
rules: [...]            # required — 1 to 30 rules, evaluated in order
default: {...}          # required — the effect when no rule matches
Une règle est une condition when: et un effet 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
Les règles sont évaluées de haut en bas ; la première règle dont le when: est vrai gagne. Si aucune ne correspond, default: s’applique. Ordonnez vos règles de la plus spécifique d’abord — une règle large en début masque tout ce qui se trouve en dessous.

when: — la condition

Les conditions sont écrites en CEL (Common Expression Language) : sûr par conception — pas de boucles, pas d’E/S, évaluation en microsecondes, regex RE2 uniquement. Ces six motifs couvrent la grande majorité des règles réelles :
MotifExemple
Accès à un champtask_class == "agent"
Comparaison numériquedifficulty > 0.6 && request.input_tokens < 50000
Logique booléenneagent_state.has_edited && !agent_state.has_run_tests
Appartenance à une liste"Edit" in agent_state.tools_used
Macro regexsystem_prompt_matches("(?i)planning agent")
Macro d’outiltool_calls_present_any(["Edit","Write","apply_patch"])

Variables

Forme de la requête
VariableType
modelstring
request.input_tokensint
request.output_max_tokensint
request.streambool
request.visionbool
request.message_countint
request.has_system_promptbool
request.has_toolsbool
Classification (calculée par la passerelle par requête)
VariableTypeSignification
task_classstringchat / code / agent / vision / audio / rag / creative
difficultydouble0.01.0
code_keyword_densitydouble0.01.0
reasoning_cue_countintindices de raisonnement détectés dans le prompt
tool_countintdéfinitions d’outils distinctes sur la requête
Session d’agent (agent_state.*, persistée à travers une conversation)
VariableType
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>
Contexte
VariableType
headers["x-foo"]string
user.id / user.groupint / string
token.id / token.nameint / string
time.hour / time.weekdayint (UTC)
workspace.idint

Macros

Fonctions CEL enregistrées pour les vérifications courantes « regarder à l’intérieur de la requête » :
MacroRetourne
system_prompt_matches(regex)RE2 sur les messages système joints
user_message_matches(regex)RE2 sur le dernier message utilisateur
tool_definitions_include(name)un outil est déclaré sur la requête
tool_calls_present_any(list)la requête porte l’un de ces appels d’outil
tool_results_from_any(list)la requête a des messages de rôle outil provenant de l’un d’eux
header_matches(name, regex)RE2 sur la valeur d’un en-tête

use: — l’effet

Un bloc use: nomme une destination (exactement une) et un nombre quelconque de boutons de réglage optionnels par appel.

Destination

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 est rejeté (cela créerait une récursion). L’épinglage à des canaux spécifiques (channels: / @channel:) n’est pas disponible actuellement et est signalé au lint comme non pris en charge — routez plutôt par model, models ou pool.

Boutons par appel

À combiner avec n’importe quelle destination pour façonner l’appel amont :
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 et header_override appliquent une liste de blocage — vous ne pouvez pas remplacer model, messages, stream, tools, les en-têtes d’authentification, etc. (cela subvertirait la facturation, l’audit ou l’état de l’agent).

Cascades de confiance et ensembles (avancé)

Deux effets avancés permettent à une règle de réagir à une première réponse faible ou de se déployer sur plusieurs modèles. Ils se rédigent de la même façon que n’importe quelle règle. Cascade — nouvelle tentative sur un signal de faible confiance avec un effet plus fort :
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 — émettez plusieurs branches en parallèle et laissez un arbitre choisir :
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
Le runtime d’ensemble / cascade est restreint et désactivé par défaut. Comme chaque branche parallèle et chaque réparation en cascade est facturée comme son propre appel, le runtime de déploiement est derrière un drapeau serveur pendant que la facturation par branche est validée. Lorsqu’il est désactivé, une règle parallel: ne sert que la première branche et une cascade enregistre son signal mais ne redéclenche pas — le jeu de règles passe quand même le lint, s’enregistre et route son effet principal normalement. Contactez-nous pour activer le runtime d’ensemble pour votre espace de travail.

Déployer en toute sécurité

Un nouveau jeu de règles ne prend pas en charge votre trafic dès l’instant où vous l’enregistrez :
  • Mode shadow — pendant une fenêtre après le premier enregistrement, le DSL est évalué mais pas utilisé : votre stratégie précédente sert toujours le trafic pendant que la passerelle enregistre ce que le DSL aurait fait. Le tableau de bord affiche un rapport de diff — pourcentage de routes divergentes, delta de coût projeté, nombre de déclenchements par règle, et à quelle fréquence il est retombé sur default:. Lisez-le avant de faire confiance aux règles.
  • Canary — montez progressivement le DSL sur un pourcentage du trafic en direct (5 → 25 → 50 → 100), en surveillant les métriques par tranche, et revenez en arrière instantanément en faisant glisser le pourcentage à 0.
Vous pouvez aussi faire un dry-run d’un jeu de règles contre une requête synthétique (classe de tâche, difficulté, état d’agent, forme de requête) directement dans l’éditeur et voir la trace et la règle correspondante — aucun trafic, rien de persisté.

Limites et validation

Chaque enregistrement exécute un lint strict ; les jeux de règles invalides sont rejetés avec {line, column, message, rule} :
  • Schéma — clés requises, types/enums corrects, aucun champ inconnu.
  • Taille — ≤ 30 règles, ≤ 16 Kio de YAML, ≤ 200 caractères par when:.
  • CEL — analyse, vérification de type contre l’environnement de variables, aucun identifiant inconnu, et when: doit s’évaluer en un bool.
  • Effet — exactement une destination par bloc use: ; toutes les références model / models / @pool: doivent se résoudre dans votre espace de travail.
  • Plages des boutonsthinking_budget_tokens ∈ [1024, 64000], temperature ∈ [0, 2], samples ∈ [1, 16].
  • Réservé — les ids de règle commençant par _ sont réservés ; default comme id de règle est rejeté (utilisez le bloc default: de premier niveau).
Chaque enregistrement et retour en arrière écrit une ligne d’audit ; les éditions concurrentes sont détectées et le second enregistrement est invité à réessayer contre un état frais.

Un exemple complet

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
Pour confirmer vers quel modèle une requête s’est résolue, lisez les en-têtes de réponse X-Orca-Router et X-Orca-Resolved-Model.

Référence API

Le DSL est géré par routeur ; les écritures requièrent Developer+.
Méthode et cheminRôleBut
GET /api/user/routers/:id/dslMemberSource + version + état shadow/canary.
PUT /api/user/routers/:id/dslDeveloper+Lint + enregistrement (nouvelle version, audité).
POST /api/user/routers/:id/dsl/lintMemberLint d’un brouillon → {errors:[…]}.
POST /api/user/routers/dsl/lintMemberLint sans état (sans id de routeur).
POST /api/user/routers/:id/dsl/dryrunMemberÉvalue une requête synthétique → trace + règle correspondante.
GET /api/user/routers/:id/dsl/historyMemberHistorique des versions, la plus récente d’abord.
POST /api/user/routers/:id/dsl/rollback/:versionDeveloper+Re-lint et restaure une version antérieure.

FAQ

C’est une stratégie — l’option dsl aux côtés de cheapest / quality / balanced / adaptive. Les autres choisissent selon le prix et la qualité ; le DSL choisit selon des règles que vous écrivez sur la forme, la classification et l’état d’agent de la requête. Vous pouvez toujours delegate: vers une stratégie intégrée comme effet d’une règle ou comme valeur par défaut.
L’effet default: de premier niveau s’applique. Il est requis, donc il y a toujours un résultat défini — couramment delegate: balanced ou un modèle filet de sécurité spécifique.
Oui. CEL s’exécute dans un bac à sable avec seulement les fonctions de la bibliothèque standard, une échéance d’évaluation de quelques millisecondes, des regex RE2 (temps linéaire, pas de ReDoS), et aucun accès à la base de données, au réseau ou au système de fichiers. L’ environnement de variables est un ensemble fixe de scalaires et de listes.
Trois façons : faites-en un dry-run contre une requête synthétique dans l’éditeur, laissez-le en mode shadow et lisez le rapport de diff, puis faites-en un canary sur un petit pourcentage de trafic en direct avant de monter à 100 %.