跳轉到主要內容
內建策略——cheapest、quality、balanced、adaptive—— 按價格與質量挑模型。Routing DSL 則是它下面的一層,適用於正確的 模型取決於請求究竟是什麼的場景:一次漫長的 agent 編碼回合、一次廉價的 分類調用、一次 vision 請求、一次測試失敗后的重試。你來寫規則;網關 按請求逐一評估它們,并據此路由。 它是命名路由器dsl 策略——所以 你的應用照舊調用 orcarouter/{name},而路由邏輯活在控制臺里,帶版本、 可編輯,無需重新部署。

什麼時候該用 DSL

當「最便宜的在線模型」或「最高質量」就能表達你的意圖時,用內建策略。 當路由取決於請求的內容或上下文時,再用 DSL:
  • 任務專精 —— 把代碼發給編碼模型,vision 發給 vision 模型,廉價 chat 發給廉價模型。
  • 難度感知路由 —— 只把困難的請求升級到昂貴模型;簡單的繼續走廉價 路線。
  • agent 感知路由 —— 按會話狀態做不同路由(agent 用過哪些工具、是否 剛剛測試失敗、進行到第幾個回合)。
  • 時間 / 租戶 / 頭部規則 —— 按小時、用戶組或某個請求頭做不同路由。

啟用它

在控制臺的 Routing 中打開一個路由器,把它的 Strategy 設為 DSL。這會展現出該路由器的 DSL 編輯器。路由器的其他一切照舊生效 —— Allowed models glob、Default model 安全兜底,以及 orcarouter/{name} 調用方式。

編輯器

編輯器的設計目標是讓你從意圖快速抵達一份可用的規則集:
  • Templates工作區的真實模型作為種子(通過一次性的 tier 映射 對話框),所以你永遠不必從空白文件開始,也不會撞上「未知模型」的牆。
  • Insert —— 從自動補全里直接放入一個 Model、一個 Routerorcarouter/<name>)或一個 Pool,不用手敲標識符。
  • Generate —— 用自然語言描述你想要的路由,拿回一份已編譯、lint 干凈、 且基於你真實模型的 DSL。
  • Explain —— 用通俗英文轉述當前規則集到底做了什麼。
  • Inline lint —— 每個錯誤都報告 {line, column, message},每個 lint 代碼都有一個 ? 解釋器。優先級(first-match-wins)和常見的 CEL 模式 都會就地呈現。

文件結構

一份規則集是帶三個頂層鍵的 YAML:
version: 1              # required — currently must be 1
rules: [...]            # required — 1 to 30 rules, evaluated in order
default: {...}          # required — the effect when no rule matches
一條規則是一個 when: 條件加一個 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
規則自上而下評估;第一條 when: 為真的規則勝出。 若沒有任何規則 匹配,則套用 default:。請把最具體的規則排在最前——一條過於寬泛的 早期規則會遮蔽它下面的一切。

when: —— 條件

條件用 CEL(Common Expression Language)編寫:天生安全——無循環、無 I/O、微秒級評估、僅 RE2 正則。 以下六種模式覆蓋了絕大多數真實規則:
模式示例
字段訪問task_class == "agent"
數值比較difficulty > 0.6 && request.input_tokens < 50000
布爾邏輯agent_state.has_edited && !agent_state.has_run_tests
列表成員判斷"Edit" in agent_state.tools_used
正則宏system_prompt_matches("(?i)planning agent")
工具宏tool_calls_present_any(["Edit","Write","apply_patch"])

變量

請求形態
變量類型
modelstring
request.input_tokensint
request.output_max_tokensint
request.streambool
request.visionbool
request.message_countint
request.has_system_promptbool
request.has_toolsbool
分類(由網關按請求計算)
變量類型含義
task_classstringchat / code / agent / vision / audio / rag / creative
difficultydouble0.01.0
code_keyword_densitydouble0.01.0
reasoning_cue_countintprompt 中檢測到的推理線索數
tool_countint請求上不同的工具定義數
agent 會話agent_state.*,在一次對話中持久保留)
變量類型
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>
上下文
變量類型
headers["x-foo"]string
user.id / user.groupint / string
token.id / token.nameint / string
time.hour / time.weekdayint (UTC)
workspace.idint

為常見的「窺探請求內部」檢查注冊的 CEL 函數:
返回
system_prompt_matches(regex)對拼接后的 system 消息做 RE2 匹配
user_message_matches(regex)對最后一條 user 消息做 RE2 匹配
tool_definitions_include(name)請求上聲明了某個工具
tool_calls_present_any(list)請求帶有其中任意一個工具調用
tool_results_from_any(list)請求含有來自其中任意一者的 tool-role 消息
header_matches(name, regex)對某個頭部值做 RE2 匹配

use: —— 效果

一個 use: 塊指定一個目的地(恰好一個)以及任意數量的可選逐調用 旋鈕(knobs)

目的地

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 會被拒絕(它會遞歸)。釘到特定 channel(channels: / @channel:)目前不可用,會被 lint 為不支持——請改用 modelmodelspool 來路由。

逐調用旋鈕

與任意目的地組合,以塑造上游調用:
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_overrideheader_override 強制執行一份拒絕名單——你不能覆蓋 modelmessagesstreamtools、認證頭部等(這些會破壞計費、 審計或 agent 狀態)。

置信度級聯與集成(進階)

兩種進階效果讓一條規則能對偏弱的首個回答做出反應,或扇出到多個模型。 它們的編寫方式和任何規則一樣。 Cascade(級聯) —— 在低置信信號上以更強的效果重試:
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(集成) —— 并行發出若干路(legs),讓一個仲裁者來挑選:
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
集成 / 級聯的運行時受開關控制,默認關閉。 由於每一路并行和每一次 級聯修復都會作為各自獨立的調用計費,在逐路計費被驗證之前,扇出運行時 位於一個服務端開關之后。開關關閉時,一條 parallel: 規則只服務第一路, 而一條級聯規則只記錄它的信號但不重新派發——規則集仍照常 lint、保存、 并路由其主效果。請聯系我們為你的工作區啟用集成運行時。

安全灰度上線

一份新規則集不會在你保存的那一刻就接管你的流量:
  • Shadow mode(影子模式) —— 首次保存后的一段窗口期內,DSL 會被評估 但不被使用:你之前的策略仍在服務流量,而網關記錄 DSL 本會做出什麼。 控制臺會展示一份 diff 報告——路由出現差異的百分比、預計的成本變化、 逐規則的觸發次數,以及它落到 default: 的頻率。在你信任這些規則之前 先讀它。
  • Canary(金絲雀) —— 把 DSL 逐步推到一定百分比的線上流量上 (5 → 25 → 50 → 100),同時觀察逐切片的指標,并可通過把百分比滑回 0 來即時回滾。
你也可以在編輯器里直接拿一份合成請求(task class、difficulty、agent state、請求形態)對規則集做 dry-run,查看 trace 和命中的規則 ——不走流量,不持久化任何東西。

限制與校驗

每次保存都運行一輪嚴格 lint;不合法的規則集會以 {line, column, message, rule} 被拒絕:
  • Schema —— 必需的鍵、正確的類型/枚舉、無未知字段。
  • Size —— ≤ 30 條規則、≤ 16 KiB 的 YAML、每個 when: ≤ 200 字符。
  • CEL —— 可解析、針對變量環境做類型檢查、無未知標識符,且 when: 必須求值為布爾
  • Effect —— 每個 use: 塊恰好一個目的地;所有 model / models / @pool: 引用都必須能在你的工作區內解析。
  • Knob ranges(旋鈕取值範圍) —— thinking_budget_tokens ∈ [1024, 64000]temperature ∈ [0, 2]samples ∈ [1, 16]
  • Reserved(保留) —— 以 _ 開頭的規則 id 被保留;用 default 作規則 id 會被拒絕(請用頂層的 default: 塊)。
每次保存和回滾都會寫入一行審計記錄;并發編輯會被檢測到,第二次保存會被 要求針對最新狀態重試。

一個完整示例

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
要確認一次請求解析到了哪個模型,請查看 X-Orca-RouterX-Orca-Resolved-Model 響應頭

API 參考

DSL 按路由器維度管理;寫操作需要 Developer+
方法與路徑角色用途
GET /api/user/routers/:id/dslMember源碼 + 版本 + shadow/canary 狀態。
PUT /api/user/routers/:id/dslDeveloper+Lint + 保存(新版本,帶審計)。
POST /api/user/routers/:id/dsl/lintMember對一份草稿做 lint → {errors:[…]}
POST /api/user/routers/dsl/lintMember無狀態 lint(無路由器 id)。
POST /api/user/routers/:id/dsl/dryrunMember評估一份合成請求 → trace + 命中的規則。
GET /api/user/routers/:id/dsl/historyMember版本歷史,最新在前。
POST /api/user/routers/:id/dsl/rollback/:versionDeveloper+重新 lint 并恢復一個較舊的版本。

FAQ

就是一種策略——與 cheapest / quality / balanced / adaptive 并列的 dsl 選項。其他幾種按價格與質量挑選;而 DSL 按你針對請求的形態、 分類和 agent 狀態所寫的規則來挑選。你仍可以把 delegate: 到某個內建 策略作為一條規則的效果,或作為默認。
套用頂層的 default: 效果。它是必需的,所以總會有一個確定的結果 ——通常是 delegate: balanced 或某個特定的安全兜底模型。
安全。CEL 在沙箱中運行,僅含標準庫函數、幾毫秒的求值截止時間、 RE2 正則(線性時間、無 ReDoS),且無法訪問數據庫、網絡或文件系統。 變量環境是一組固定的標量與列表。
有三種方式:在編輯器里拿一份合成請求對它做 dry-run、把它留在 shadow mode 并閱讀 diff 報告,再把它 canary 到一小部分線上 流量上,然后逐步放量到 100%。