跳转到主要内容
内置策略——cheapest、quality、balanced、adaptive—— 按价格和质量挑模型。Routing DSL 则是它们之下的层级,适用于正确 的模型取决于请求究竟是什么的场景:一次漫长的 agentic 编码回合、一次 廉价的分类调用、一次视觉请求、一次测试失败后的重试。你写规则;网关 在每次请求时求值这些规则,并据此路由。 它是命名路由器dsl 策略——所以你的 应用照旧调用 orcarouter/{name},而路由逻辑存放在控制台里,带版本, 无需重新部署即可编辑。

何时该用 DSL

当”最便宜的在线模型”或”最高质量”就能表达你的意图时,用内置策略。当 路由取决于请求的内容或上下文时,才动用 DSL:
  • 任务特化 —— 把代码发给编码模型,视觉发给视觉模型,廉价闲聊发给 廉价模型。
  • 难度感知路由 —— 只把困难请求升级到昂贵模型;简单的保持廉价。
  • agent 感知路由 —— 根据会话状态做不同路由(agent 用过哪些工具、 测试是否刚刚失败、当前进行到第几回合)。
  • 时间 / 租户 / 请求头规则 —— 按小时、用户组或某个请求头做不同路由。

启用

在控制台的 Routing 下,打开一个路由器,把它的 Strategy 设为 DSL。这会为该路由器显示 DSL 编辑器。路由器的其余配置依旧生效—— Allowed models glob、Default model 兜底,以及 orcarouter/{name} 调用方式。

编辑器

编辑器的设计目标是让你从意图快速抵达一份可用的规则集:
  • Templates(模板)以工作区里真实的模型为种子(通过一次性的 分层映射对话),所以你绝不会从空白文件开始,也不会撞上”未知模型”的 墙。
  • Insert(插入)—— 从自动补全里放入一个 Model、一个 Routerorcarouter/<name>)或一个 Pool,而不用手敲标识符。
  • Generate(生成)—— 用自然语言描述你想要的路由,得到一份编译过、 lint 干净、且立足于你真实模型的 DSL。
  • Explain(解释)—— 用通俗英文复述当前规则集所做的事。
  • Inline lint(内联 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_countint在提示中检测到的推理线索数
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 角色消息
header_matches(name, regex)对某个请求头的值做 RE2 匹配

use: —— 效果

一个 use: 块命名一个目标(恰好一个)以及任意数量的可选逐调用 旋钮

目标

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 会被拒绝(它会造成递归)。钉死到特定渠道 (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(集成) —— 并行发出若干条腿,并让一个仲裁者挑选:
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
**集成 / 级联的运行时受开关控制,且默认关闭。**因为每条并行腿和每次 级联修复都会作为各自独立的调用计费,所以在逐腿计费得到验证之前,扇出 运行时被放在一个服务端 flag 之后。在它关闭的情况下,parallel: 规则只 服务第一条腿,级联会记录它的信号但不会重新派发——规则集照旧能通过 lint、保存,并正常路由它的主效果。如需为你的工作区启用集成运行时,请 联系我们。

安全地灰度上线

一份新规则集并不会在你保存的那一刻就接管你的流量:
  • Shadow mode(影子模式) —— 在首次保存后的一个窗口内,DSL 会被 求值但不被采用:你之前的策略仍在服务流量,同时网关记录 DSL 本会 做出的决定。控制台会展示一份 diff 报告——路由结果差异的百分比、预计 成本变化、每条规则的命中次数,以及它落到 default: 的频率。在信任这 些规则之前先读它。
  • Canary(金丝雀) —— 把 DSL 灰度到一定百分比的真实流量上 (5 → 25 → 50 → 100),观察各切片的指标,并能通过把百分比滑到 0 来 即时回滚。
你也可以就在编辑器里,对一个合成请求(任务分类、难度、agent 状态、请求 形态)**dry-run(试跑)**一份规则集,看到 trace 和命中的规则——不动用 流量,不持久化任何东西。

限制与校验

每次保存都会运行一次严格的 lint;无效的规则集会被拒绝并返回 {line, column, message, rule}
  • Schema —— 必需键、正确的类型/枚举、没有未知字段。
  • Size —— ≤ 30 条规则、≤ 16 KiB 的 YAML、每个 when: ≤ 200 字符。
  • CEL —— 能解析、能针对变量环境做类型检查、没有未知标识符,且 when: 必须求值为 bool
  • 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。
会应用顶层的 default: 效果。它是必需的,所以总会有一个确定的结果 ——常见的是 delegate: balanced 或某个具体的兜底模型。
安全。CEL 运行在一个仅含标准库函数的沙箱里,有几毫秒的求值期限、RE2 正则(线性时间,无 ReDoS),且无法访问数据库、网络或文件系统。变量 环境是一组固定的标量和列表。
有三种方式:在编辑器里对一个合成请求 dry-run(试跑);把它留在 **shadow mode(影子模式)**并阅读 diff 报告;然后在灰度到 100% 之前, 先 **canary(金丝雀)**到一小部分真实流量上。