跳到主要内容
版本:最新版(main)

Router 如何使用 System One

Router 做出的每个判断都是一个 System One 问题:为请求路由的信号、为它挑选模型的选择器、读取其回答的守卫,以及判断相似性的评审。本页展示每个功能背后的调用,让你能预估一份配置的成本以及它发送了什么。

一览​

功能何时询问调用
路由信号路由之前,针对决策、投影或请求插件读取的每条规则每个部署和状态一个标准请求
systemone 选择器决策匹配之后一个覆盖候选项的 choice
response_guard、masking回答产生之后,或调用提供方之前响应阶段规则;PII span
评审缓存、RAG、压缩或工具有候选项时每个候选一个条目的批量请求,或一个 choice
escalate针对每个候选回答一个 answer_check 或 refusal 问题

每个功能都使用同一个客户端,以 API 的两种形式之一调用:

  • 标准请求:一个状态及其问题;
  • 批量请求:当问题针对不同状态时,在一次调用中包含多个命名的标准请求。
备注

示例中 {...} 省略了问题细节。在传输中,如果问题有其模型训练用的 ID(例如 vela3-lite 的 domain),就带上该 ID;否则使用 Router 自有的 ID,例如 preset:complexity 或 systemone:needs_tools。Router 以固定顺序把批量条目命名为 "1"、"2" 等;下面示例中的条目名称仅作说明。

路由信号​

路由之前,请求阶段会询问决策、投影或请求插件读取的每条规则。针对同一状态的问题,无论多少个,都作为一个标准请求发给其部署:

config.yaml
recipes:
- name: default
signals:
domains:
- name: math
labels: [math]
jailbreak:
- name: attack
complexity:
- name: difficulty
systemone:
- name: needs_tools
question:
type: noul
instructions: Does answering this request need a tool call?
predicate: {gte: 0.7}
decisions:
- name: hard-math
priority: 200
rules:
operator: AND
conditions:
- {type: domain, name: math}
- {type: complexity, name: difficulty, label: hard}
modelRefs:
- model: reasoner
The request stage's one call
{"state": {"request": "Prove that the square root of 2 is irrational."},
"questions": {"domain": {...}, "jailbreak": {...}, "complexity": {...}, "needs_tools": {...}}}
  • 读取当前轮次以外内容的规则(input.history、input.previous_turns、input.tool_results、图像)读取的是另一个状态。当规则读取不同状态时,调用是每个状态一个条目的批量请求,顶层不发送状态。
  • 针对同一状态、向同一部署询问同一预设的规则共享一个问题;每条规则把自己的 labels 和 predicate 应用到答案上。
  • 没有被任何决策、投影或插件读取的规则不会被询问。
  • Images API(POST /v1/images/generations)上的请求会像文本请求一样针对其提示词被询问。它必须到达 output 为图像的决策,否则在调用任何模型之前就以 image_route_required 被拒绝。
提示

迟到或失败的答案会让规则成为未知(unknown)。决策的 rules.on_unknown 或条件的 on_error 说明其含义;请求阶段的 jailbreak 或 pii 规则在模型没有给出答案时视为匹配,除非设置了 on_unknown: no_match。

systemone 选择器​

决策匹配后,其 systemone 选择器询问哪个 modelRefs 应该回答:一个以候选项及其描述为选项的 choice,单独一次调用。每个候选项的概率就是其选择分数:

{"state": {"request": "Refactor this module to remove the global cache."},
"questions": {"selector": {"type": "choice",
"instructions": "Which model should answer this request?",
"choices": [{"key": "qwen3-8b", "description": "Fast general model for routine code"},
{"key": "qwen3-32b", "description": "Strong reasoning model for hard code"}]}}}

当模型未就绪、回答迟到或回答无效时,由第一个 modelRef 回答。参见 System One 选择。

response_guard 与 masking​

响应阶段的规则在模型回答产生后对其提问。response_guard 插件只询问其守卫规则读取的那些规则,AND 在第一个为假的条件处停止:

{"state": {"request": "How do I pick a lock?", "answer": "First, insert a tension wrench ..."},
"questions": {"preset:jailbreak": {...}, "preset:harm": {...}}}

hallucination 规则读取回答及其依据上下文(检索到的文档和工具结果),并且需要支持 span 的部署:

{"state": {"request": "When does the library open?", "answer": "It opens at 8:00 on Sundays.",
"context": "The library is open Monday through Friday, 09:00 to 17:00."},
"questions": {"preset:hallucination": {"type": "span", ...}}}

masking 会就最终发往提供方的请求中的每个文本位置询问它所指定的 pii 规则:系统指令、消息、工具调用参数和工具结果,包括 context 插件注入的内容。请求阶段已经回答过的文本按其哈希复用;无法获得 span 时,请求以失败关闭(fail closed)。

评审:same_request、relevance、tool_need 和 tool_rank​

match 块有两个可选阶段:

  • 召回(Recall) 按嵌入或 BM25 相似度缩小候选集合。
  • 评审(Judge) 就候选项提出一个 System One 问题。逐候选评审对每个(请求,候选)对提问,每个候选一个批量条目;choice 评审把候选项作为一个问题的选项。

same_request 确认响应缓存召回的每条缓存请求,避免把意思不同的近似改写当作命中:

{"states": {
"hit_1": {"state": {"request": "How do I reset my password?",
"candidate": "I forgot my password. How do I change it?"},
"questions": {"same_request": {"type": "noul", "instructions":
"Does the candidate request ask for the same thing as the request, so that one answer serves both?"}}},
"hit_2": {"state": {"request": "How do I reset my password?", "candidate": "How do I reset my router?"},
"questions": {"same_request": {...}}}}}

relevance 评判 RAG 源召回的每个文档,以及上下文压缩考虑的每个片段,以最新的用户消息作为请求:

{"states": {
"doc_1": {"state": {"request": "How long is the refund window?",
"candidate": "Refunds are accepted within 30 days of purchase."},
"questions": {"relevance": {"type": "noul", "instructions":
"Does the candidate passage contain information that helps answer the request?"}}},
"doc_2": {"state": {"request": "How long is the refund window?", "candidate": "Our offices close on public holidays."},
"questions": {"relevance": {...}}}}}

tool_need 和 tool_rank 在一个标准请求中选择工具:tool_need 是是/否门控,tool_rank 是以工具策略允许的候选工具为选项的 choice:

{"state": {"request": "Open a ticket: the export button fails."},
"questions": {"tool_need": {"type": "noul", ...},
"tool_rank": {"type": "choice", "choices": ["docs.search", "tickets.lookup", "tickets.create"]}}}

每个功能都有默认的 match:

功能默认 match
response_cache.semantic.matchrecall: {method: embedding, top_k: 3, min_similarity: 0.8} + judge: {preset: same_request, min_score: 0.8}
context.sources[rag].matchrecall: {method: embedding, top_k: 20, min_similarity: 0.3} + judge: {preset: relevance, top_k: 6, min_score: 0.5}
context.sources[memory].matchrecall: {method: embedding, top_k: 5, min_similarity: 0.7}
context.compress.matchrecall: {method: bm25} + judge: {preset: relevance},逐片段
tools.select.matchjudge: {preset: tool_rank, top_k: 3, min_ratio: 0.1},由 tool_need 门控

写了自己 match 块的功能会整体替换默认值;插件参考列出了每个功能允许的召回方法。

escalate​

在 Chat 上,escalate 依次调用其候选项,在第一个被接受的回答处停止。使用 accept.systemone 时,决策模型的 answer_check 问题以请求和回答为状态评判每个回答:

{"state": {"request": "What is 17 × 23?", "answer": "391"},
"questions": {"answer_check": {"type": "noul", ...}}}
  • accept.refusal: false 改为就回答询问 refusal 问题。
  • 在 System One 入口上,escalate.stages 依次向每个决策模型提出原始的类型化请求,原生接受规则读取类型化答案;不会转换成 Chat 文本。

调用如何合并​

Router 合并每个阶段的调用:

  • 按部署。 一个阶段向同一部署提出的所有问题放在一次调用中,本地和远程部署都是如此。Router 从不为了适应服务器的单次调用限制而拆分调用。
  • 按状态。 针对同一状态的问题共享一个条目;针对不同状态的问题是不同条目。只有一个不同状态时作为标准请求发送,两个或以上作为批量请求。
  • 按阶段。 请求阶段、选择器、响应阶段以及各功能的评审在其输入存在时运行,因此包含所有这些的请求会发出多次调用。被它们共用的部署仍然只加载一个模型。

本地运行时会就地回答失败的问题;它拒绝其输入(例如超出限制的图像)的条目只让该条目的问题以 invalid_input 失败。远程 System One API 会让整个调用失败并指出该问题,批量请求中还会指出其条目(state);Router 随后在同一截止时间内再询问一次,只包含仍有其他问题的状态,形式不变,因此只有该条目上失败问题的规则成为未知。如果该问题的错误是 invalid_input,说明 API 拒绝了该条目的输入,重试会去掉整个条目。如果这次重试在另一个条目上让同一问题再次失败,Router 会最后再询问一次,在所有条目上都去掉该问题;此时与未指出条目的 API 一样,每个询问过该问题的状态上的该问题都成为未知。

对同一状态提出的相同问题的答案会被复用:来自本地运行时的结果缓存,以及远程 API 最长 10 分钟的缓存,直到配置变化。

上限与截断​

每个评审都有其读取候选数的硬上限 judge.max_candidates:

  • choice 评审(tool_rank)最多提供 255 个候选,即协议的选项上限,这也是其默认值;
  • 逐候选评审(relevance、same_request)默认评判 64 个,最多 4096 个,即模型 runtime 在缺省 --max-bundle-tasks 下一个 bundle 能装下的状态数(64 个任务,每个 64 个状态)。

小于 1 的值、choice 评审上大于 255 的值,或逐候选评审上大于 4096 的值,会导致加载失败。超出上限绝不会让请求失败:评审按召回排名读取前 max_candidates 个候选;没有召回阶段时,按功能列出的顺序读取(最新的在前)。

  • 每次截断都会在请求的 trace span 中添加 match.judge.truncated 事件,并计入 vsr_match_judge_truncated_total。
  • 经过策略过滤后仍超过 255 个的工具目录按列出顺序截断,或先用 recall: {method: bm25} 缩小范围。

召回还是评审,以及何时需要嵌入模型​

match 包含得到适合
仅 recall经典的嵌入或 BM25 相似度相似度足够的大型存储:memory、宽泛的 RAG
仅 judge对每个候选的 System One 判断天然有界的小集合:少量工具、小型知识库、某个用户的少量记忆
recall + judge召回缩小集合,评审做决定重视精度的场景:缓存命中、RAG 回答

嵌入只用于召回,任何决策路径都不读取它:

  • 预先声明的 vela-embedding 部署只在语义缓存、memory 或向量存储 RAG 源需要时才启动。
  • global.model_catalog.embeddings.default: {deployment: ...} 指定另一个嵌入部署,方式与 system.decision_model 指定决策模型相同;用其他模型建立索引的存储在 embedding: {deployment, dimension} 块中指定自己的部署。
  • recall: {method: bm25} 完全不需要模型。
  • 远程 openai_embeddings 部署在 capabilities 中声明其向量的 dimensions 和 modalities。

参见嵌入。

响应中显示什么​

每个经过路由的响应都带有公共头集合:选中的 recipe、决策和模型,x-vsr-response-path,以及包含 response_guard 或幻觉检查给出的代码的 x-vsr-warnings。System One 的回答内容放在一个调试头 x-vsr-trace 中,并且只发给 global.router.headers.debug 允许的调用方:

  • signals 以 type:name 列出匹配的规则,例如 jailbreak:attack 或 systemone:needs_tools;没有 labels 的 choice 规则以 type:rule:option 列出;
  • confidence、algorithm 和 ranking 说明决策及其模型是如何选出的;
  • modality 列出选中决策的输出类型,例如 (text image)。

评审的截断是 match.judge.truncated span 事件,而不是 trace 成员。POST /api/v1/routing/preview 针对一个请求评估相同的规则,而不调用模型。VSR 路由头列出了每个头和 trace 成员。