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、一個 Router
(
orcarouter/<name>)或一個 Pool,不用手敲標識符。 - Generate —— 用自然語言描述你想要的路由,拿回一份已編譯、lint 干凈、 且基於你真實模型的 DSL。
- Explain —— 用通俗英文轉述當前規則集到底做了什麼。
- Inline lint —— 每個錯誤都報告
{line, column, message},每個 lint 代碼都有一個?解釋器。優先級(first-match-wins)和常見的 CEL 模式 都會就地呈現。
文件結構
一份規則集是帶三個頂層鍵的 YAML:when: 條件加一個 use: 效果:
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"]) |
變量
請求形態| 變量 | 類型 |
|---|---|
model | string |
request.input_tokens | int |
request.output_max_tokens | int |
request.stream | bool |
request.vision | bool |
request.message_count | int |
request.has_system_prompt | bool |
request.has_tools | bool |
| 變量 | 類型 | 含義 |
|---|---|---|
task_class | string | chat / code / agent / vision / audio / rag / creative |
difficulty | double | 0.0–1.0 |
code_keyword_density | double | 0.0–1.0 |
reasoning_cue_count | int | prompt 中檢測到的推理線索數 |
tool_count | int | 請求上不同的工具定義數 |
agent_state.*,在一次對話中持久保留)
| 變量 | 類型 |
|---|---|
agent_state.turn | int |
agent_state.tools_used | list<string> |
agent_state.files_read | list<string> |
agent_state.has_edited | bool |
agent_state.has_run_tests | bool |
agent_state.last_test_failed | bool |
agent_state.consecutive_errors | int |
agent_state.elapsed_seconds | int |
agent_state.models_tried | list<string> |
| 變量 | 類型 |
|---|---|
headers["x-foo"] | string |
user.id / user.group | int / string |
token.id / token.name | int / string |
time.hour / time.weekday | int (UTC) |
workspace.id | int |
宏
為常見的「窺探請求內部」檢查注冊的 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)。
目的地
delegate: dsl 會被拒絕(它會遞歸)。釘到特定 channel(channels: /
@channel:)目前不可用,會被 lint 為不支持——請改用 model、models
或 pool 來路由。逐調用旋鈕
與任意目的地組合,以塑造上游調用:param_override 和 header_override 強制執行一份拒絕名單——你不能覆蓋
model、messages、stream、tools、認證頭部等(這些會破壞計費、
審計或 agent 狀態)。
置信度級聯與集成(進階)
兩種進階效果讓一條規則能對偏弱的首個回答做出反應,或扇出到多個模型。 它們的編寫方式和任何規則一樣。 Cascade(級聯) —— 在低置信信號上以更強的效果重試:集成 / 級聯的運行時受開關控制,默認關閉。 由於每一路并行和每一次
級聯修復都會作為各自獨立的調用計費,在逐路計費被驗證之前,扇出運行時
位於一個服務端開關之后。開關關閉時,一條
parallel: 規則只服務第一路,
而一條級聯規則只記錄它的信號但不重新派發——規則集仍照常 lint、保存、
并路由其主效果。請聯系我們為你的工作區啟用集成運行時。安全灰度上線
一份新規則集不會在你保存的那一刻就接管你的流量:- Shadow mode(影子模式) —— 首次保存后的一段窗口期內,DSL 會被評估
但不被使用:你之前的策略仍在服務流量,而網關記錄 DSL 本會做出什麼。
控制臺會展示一份 diff 報告——路由出現差異的百分比、預計的成本變化、
逐規則的觸發次數,以及它落到
default:的頻率。在你信任這些規則之前 先讀它。 - Canary(金絲雀) —— 把 DSL 逐步推到一定百分比的線上流量上 (5 → 25 → 50 → 100),同時觀察逐切片的指標,并可通過把百分比滑回 0 來即時回滾。
限制與校驗
每次保存都運行一輪嚴格 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:塊)。
一個完整示例
X-Orca-Router 和
X-Orca-Resolved-Model 響應頭。
API 參考
DSL 按路由器維度管理;寫操作需要 Developer+。| 方法與路徑 | 角色 | 用途 |
|---|---|---|
GET /api/user/routers/:id/dsl | Member | 源碼 + 版本 + shadow/canary 狀態。 |
PUT /api/user/routers/:id/dsl | Developer+ | Lint + 保存(新版本,帶審計)。 |
POST /api/user/routers/:id/dsl/lint | Member | 對一份草稿做 lint → {errors:[…]}。 |
POST /api/user/routers/dsl/lint | Member | 無狀態 lint(無路由器 id)。 |
POST /api/user/routers/:id/dsl/dryrun | Member | 評估一份合成請求 → trace + 命中的規則。 |
GET /api/user/routers/:id/dsl/history | Member | 版本歷史,最新在前。 |
POST /api/user/routers/:id/dsl/rollback/:version | Developer+ | 重新 lint 并恢復一個較舊的版本。 |
FAQ
它和命名路由器的策略有什麼區別?
它和命名路由器的策略有什麼區別?
它就是一種策略——與 cheapest / quality / balanced / adaptive 并列的
dsl 選項。其他幾種按價格與質量挑選;而 DSL 按你針對請求的形態、
分類和 agent 狀態所寫的規則來挑選。你仍可以把 delegate: 到某個內建
策略作為一條規則的效果,或作為默認。如果沒有任何規則匹配會怎樣?
如果沒有任何規則匹配會怎樣?
套用頂層的
default: 效果。它是必需的,所以總會有一個確定的結果
——通常是 delegate: balanced 或某個特定的安全兜底模型。在熱路徑上運行不受信任的 CEL 安全嗎?
在熱路徑上運行不受信任的 CEL 安全嗎?
安全。CEL 在沙箱中運行,僅含標準庫函數、幾毫秒的求值截止時間、
RE2 正則(線性時間、無 ReDoS),且無法訪問數據庫、網絡或文件系統。
變量環境是一組固定的標量與列表。
我能在規則集接觸真實流量之前先測試它嗎?
我能在規則集接觸真實流量之前先測試它嗎?
有三種方式:在編輯器里拿一份合成請求對它做 dry-run、把它留在
shadow mode 并閱讀 diff 報告,再把它 canary 到一小部分線上
流量上,然后逐步放量到 100%。
