Zum Hauptinhalt springen
Die eingebauten Strategien — Cheapest, Quality, Balanced, Adaptive — wählen ein Modell nach Preis und Qualität. Die Routing-DSL ist die Stufe darunter, für den Fall, dass das richtige Modell davon abhängt, was die Anfrage tatsächlich ist: ein langer agentischer Coding-Turn, ein günstiger Klassifizierungsaufruf, eine Vision-Anfrage, ein Retry nach einem fehlgeschlagenen Test. Du schreibst Regeln; das Gateway wertet sie pro Anfrage aus und routet entsprechend. Es ist die dsl-Strategie eines benannten Routers — sodass deine Anwendung weiterhin orcarouter/{name} aufruft und die Routing-Logik im Dashboard lebt, versioniert und ohne Re-Deployment bearbeitbar.

Wann du zur DSL greifen solltest

Verwende eine eingebaute Strategie, wenn “günstigstes Live-Modell” oder “beste Qualität” deine Absicht erfasst. Greife zur DSL, wenn das Routing vom Inhalt oder Kontext der Anfrage abhängt:
  • Aufgabenspezialisierung — sende Code an ein Coding-Modell, Vision an ein Vision-Modell, günstigen Chat an ein günstiges Modell.
  • Schwierigkeitsbewusstes Routing — eskaliere nur die schwierigen Anfragen an ein teures Modell; halte die einfachen günstig.
  • Agent-bewusstes Routing — route unterschiedlich basierend auf dem Sitzungszustand (welche Tools der Agent verwendet hat, ob gerade Tests fehlgeschlagen sind, in welchem Turn er sich befindet).
  • Zeit-/Tenant-/Header-Regeln — unterschiedliches Routing nach Stunde, Benutzergruppe oder einem Anfrage-Header.

Aktivierung

Im Dashboard unter Routing öffnest du einen Router und setzt seine Strategie auf DSL. Das blendet den DSL-Editor für diesen Router ein. Alles andere am Router gilt weiterhin — der Erlaubte Modelle-Glob, das Standardmodell-Safety-Net und der orcarouter/{name}-Aufruf.

Der Editor

Der Editor ist darauf ausgelegt, dich schnell von der Absicht zu einem funktionierenden Regelsatz zu bringen:
  • Templates, geseedet mit den echten Modellen deines Workspace (über einen einmaligen Tier-Mapping-Dialog), sodass du nie mit einer leeren Datei startest oder gegen eine “Unbekanntes Modell”-Wand läufst.
  • Insert — füge ein Model, einen Router (orcarouter/<name>) oder einen Pool per Autovervollständigung ein, statt Bezeichner von Hand einzutippen.
  • Generate — beschreibe das gewünschte Routing in natürlicher Sprache und erhalte kompilierte, lint-saubere DSL, fundiert in deinen echten Modellen.
  • Explain — eine umgangssprachliche Umschreibung dessen, was der aktuelle Regelsatz tut.
  • Inline-Lint — jeder Fehler meldet {line, column, message} und jeder Lint-Code hat einen ?-Erklärer. Präzedenz (First-Match-Wins) und die gängigen CEL-Muster werden an Ort und Stelle aufgezeigt.

Dateistruktur

Ein Regelsatz ist YAML mit drei Top-Level-Schlüsseln:
version: 1              # required — currently must be 1
rules: [...]            # required — 1 to 30 rules, evaluated in order
default: {...}          # required — the effect when no rule matches
Eine Regel ist eine when:-Bedingung und ein use:-Effekt:
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
Regeln werden von oben nach unten ausgewertet; die erste Regel, deren when: wahr ist, gewinnt. Wenn keine passt, greift default:. Ordne deine Regeln am spezifischsten zuerst — eine breite frühe Regel überschattet alles darunter.

when: — die Bedingung

Bedingungen werden in CEL (Common Expression Language) geschrieben: sicher von Grund auf — keine Schleifen, kein I/O, Auswertung im Mikrosekundenbereich, nur RE2-Regex. Diese sechs Muster decken die große Mehrheit echter Regeln ab:
MusterBeispiel
Feldzugrifftask_class == "agent"
Numerischer Vergleichdifficulty > 0.6 && request.input_tokens < 50000
Boolesche Logikagent_state.has_edited && !agent_state.has_run_tests
Listenzugehörigkeit"Edit" in agent_state.tools_used
Regex-Makrosystem_prompt_matches("(?i)planning agent")
Tool-Makrotool_calls_present_any(["Edit","Write","apply_patch"])

Variablen

Anfrageform
VariableTyp
modelstring
request.input_tokensint
request.output_max_tokensint
request.streambool
request.visionbool
request.message_countint
request.has_system_promptbool
request.has_toolsbool
Klassifizierung (vom Gateway pro Anfrage berechnet)
VariableTypBedeutung
task_classstringchat / code / agent / vision / audio / rag / creative
difficultydouble0.01.0
code_keyword_densitydouble0.01.0
reasoning_cue_countintim Prompt erkannte Reasoning-Cues
tool_countintdistinkte Tool-Definitionen auf der Anfrage
Agent-Sitzung (agent_state.*, über eine Konversation hinweg persistiert)
VariableTyp
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>
Kontext
VariableTyp
headers["x-foo"]string
user.id / user.groupint / string
token.id / token.nameint / string
time.hour / time.weekdayint (UTC)
workspace.idint

Makros

Registrierte CEL-Funktionen für die gängigen “Schau in die Anfrage hinein”-Checks:
MakroGibt zurück
system_prompt_matches(regex)RE2 über die zusammengefügten System-Messages
user_message_matches(regex)RE2 über die letzte User-Message
tool_definitions_include(name)ein Tool ist auf der Anfrage deklariert
tool_calls_present_any(list)die Anfrage trägt einen dieser Tool-Calls
tool_results_from_any(list)die Anfrage hat Tool-Role-Messages von einem davon
header_matches(name, regex)RE2 über einen Header-Wert

use: — der Effekt

Ein use:-Block benennt ein Ziel (genau eines) und beliebig viele optionale Knobs pro Aufruf.

Ziel

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 wird abgelehnt (es würde rekursieren). Das Anpinnen an bestimmte Channels (channels: / @channel:) ist derzeit nicht verfügbar und lintet als nicht unterstützt — route stattdessen per model, models oder pool.

Knobs pro Aufruf

Kombiniere sie mit jedem Ziel, um den Upstream-Aufruf zu formen:
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 und header_override erzwingen eine Denylist — du kannst model, messages, stream, tools, Auth-Header usw. nicht überschreiben (das würde Billing, Audit oder Agent-State unterwandern).

Confidence-Kaskaden & Ensembles (fortgeschritten)

Zwei fortgeschrittene Effekte lassen eine Regel auf eine schwache erste Antwort reagieren oder über mehrere Modelle hinweg fächern. Sie werden genauso verfasst wie jede Regel. Cascade — Retry bei einem Low-Confidence-Signal mit einem stärkeren Effekt:
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 — gib mehrere Legs parallel aus und lass einen Arbiter wählen:
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
Die Laufzeit von Ensemble / Cascade ist gated und standardmäßig aus. Da jeder parallele Leg und jeder Cascade-Repair als eigener Aufruf abgerechnet wird, liegt die Fan-out-Laufzeit hinter einem Server-Flag, während das Per-Leg-Billing validiert wird. Wenn sie aus ist, bedient eine parallel:-Regel nur den ersten Leg und eine Cascade zeichnet ihr Signal auf, dispatcht aber nicht erneut — der Regelsatz lintet, speichert und routet seinen primären Effekt weiterhin normal. Kontaktiere uns, um die Ensemble-Laufzeit für deinen Workspace zu aktivieren.

Sicheres Ausrollen

Ein neuer Regelsatz übernimmt deinen Verkehr nicht in dem Moment, in dem du ihn speicherst:
  • Shadow-Modus — für ein Fenster nach dem ersten Speichern wird die DSL ausgewertet, aber nicht verwendet: deine vorherige Strategie bedient weiterhin den Verkehr, während das Gateway aufzeichnet, was die DSL getan hätte. Das Dashboard zeigt einen Diff-Report — Prozentsatz abweichender Routen, prognostizierte Kostendifferenz, Fire-Counts pro Regel und wie oft auf default: durchgefallen wurde. Lies ihn, bevor du den Regeln vertraust.
  • Canary — ramp die DSL auf einen Prozentsatz des Live-Verkehrs hoch (5 → 25 → 50 → 100), beobachte dabei die Metriken pro Slice und rolle sofort zurück, indem du den Prozentsatz auf 0 schiebst.
Du kannst einen Regelsatz auch direkt im Editor per Dry-Run gegen eine synthetische Anfrage (Aufgabenklasse, Schwierigkeit, Agent-State, Anfrageform) ausführen und den Trace sowie die passende Regel sehen — kein Verkehr, nichts persistiert.

Limits & Validierung

Jedes Speichern führt einen strikten Lint aus; ungültige Regelsätze werden mit {line, column, message, rule} abgelehnt:
  • Schema — erforderliche Schlüssel, korrekte Typen/Enums, keine unbekannten Felder.
  • Größe — ≤ 30 Regeln, ≤ 16 KiB YAML, ≤ 200 Zeichen pro when:.
  • CEL — parst, typecheckt gegen die Variablenumgebung, keine unbekannten Bezeichner, und when: muss zu einem bool auswerten.
  • Effekt — genau ein Ziel pro use:-Block; alle model / models / @pool:-Referenzen müssen in deinem Workspace auflösbar sein.
  • Knob-Bereichethinking_budget_tokens ∈ [1024, 64000], temperature ∈ [0, 2], samples ∈ [1, 16].
  • Reserviert — Regel-IDs, die mit _ beginnen, sind reserviert; default als Regel-ID wird abgelehnt (verwende den Top-Level-default:-Block).
Jedes Speichern und jeder Rollback schreibt eine Audit-Zeile; gleichzeitige Edits werden erkannt und das zweite Speichern wird gebeten, gegen frischen Zustand erneut zu versuchen.

Ein vollständiges Beispiel

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
Um zu bestätigen, auf welches Modell eine Anfrage aufgelöst wurde, lies die X-Orca-Router- und X-Orca-Resolved-Model-Antwort-Header.

API-Referenz

Die DSL wird pro Router verwaltet; Schreibvorgänge erfordern Developer+.
Methode & PfadRolleZweck
GET /api/user/routers/:id/dslMemberQuelle + Version + Shadow-/Canary-Status.
PUT /api/user/routers/:id/dslDeveloper+Lint + Speichern (neue Version, auditiert).
POST /api/user/routers/:id/dsl/lintMemberLint eines Entwurfs → {errors:[…]}.
POST /api/user/routers/dsl/lintMemberZustandsloser Lint (ohne Router-ID).
POST /api/user/routers/:id/dsl/dryrunMemberWerte eine synthetische Anfrage aus → Trace + passende Regel.
GET /api/user/routers/:id/dsl/historyMemberVersionshistorie, neueste zuerst.
POST /api/user/routers/:id/dsl/rollback/:versionDeveloper+Erneut linten und eine ältere Version wiederherstellen.

FAQ

Es ist eine Strategie — die dsl-Option neben Cheapest / Quality / Balanced / Adaptive. Die anderen wählen nach Preis und Qualität; die DSL wählt nach Regeln, die du über Form, Klassifizierung und Agent-State der Anfrage schreibst. Du kannst weiterhin als Effekt einer Regel oder als Default an eine eingebaute Strategie delegate:-en.
Der Top-Level-default:-Effekt greift. Er ist erforderlich, sodass es immer ein definiertes Ergebnis gibt — üblicherweise delegate: balanced oder ein bestimmtes Safety-Net-Modell.
Ja. CEL läuft in einer Sandbox mit ausschließlich Standardbibliotheksfunktionen, einer Auswertungs-Deadline von wenigen Millisekunden, RE2-Regex (linear-time, kein ReDoS) und ohne Zugriff auf Datenbank, Netzwerk oder Dateisystem. Die Variablenumgebung ist ein fester Satz von Skalaren und Listen.
Auf drei Wegen: führe ihn per Dry-Run gegen eine synthetische Anfrage im Editor aus, belasse ihn im Shadow-Modus und lies den Diff-Report, dann canary ihn auf einen kleinen Prozentsatz des Live-Verkehrs, bevor du auf 100 % hochrampst.