vLLM Semantic Router 深度剖析:重塑 LLM 流量分发的智能大脑

从 NeurIPS 论文到 Rust 工程落地:揭秘信号驱动架构如何实现 47% 延迟降低与 48% 成本节约

前言

当我们部署多个大语言模型(LLM)时,一个看似简单却至关重要的问题浮出水面:面对一个用户请求,应该交给哪个模型来处理? 这不仅仅是一个”选大模型还是小模型”的二分类问题——在真实生产环境中,我们需要同时考虑查询意图、安全合规、成本控制、延迟要求、多轮对话上下文等诸多因素。

路由的基础机制(请求分发、负载均衡、健康检查、Kubernetes 服务发现)参见 vLLM Router 概述。本文聚焦于路由的决策智能——信号驱动的语义路由如何决定请求去向。

vLLM Semantic Router(以下简称 VSR)正是为解决这一问题而诞生的开源框架。它的论文 “When to Reason: Semantic Router for vLLM” [6] 被 NeurIPS 2025 MLForSys Workshop 接收,提出了一种信号驱动的决策路由架构,在 MMLU-Pro 基准上实现了 10.2% 的准确率提升,同时降低了 47.1% 的延迟48.5% 的 Token 消耗

本文将从论文的核心思想出发,深入分析问题本质、方案设计和关键创新,然后结合项目代码库,详细解读每个核心组件的工程落地实现。


1. 问题分析:为什么需要语义路由?

语义路由的必要性源于一个事实:模型生态已经碎片化到单模型无法覆盖所有请求。本节先刻画碎片化的形态,再从信息论视角剖析路由问题的本质——最终收敛到路由决策本身的三个内在矛盾,为后文每一章提供要解决的问题。

1.1 模型异构化的挑战

今天的 LLM 生态已经高度碎片化:

  • 模态维度:文本、代码、视觉、图像生成
  • 规模维度:从 1B 到万亿参数不等
  • 成本维度:不同提供商之间的每 Token 定价差异可达 10 倍
  • 能力维度:通用模型 vs. 领域微调模型

企业越来越多地运营着异构模型集群——本地 vLLM 实例与 OpenAI、Anthropic、Azure、Bedrock、Gemini 等云端模型并存。每个模型有着不同的能力画像、定价策略和合规特性。

1.2 从信息论视角理解路由

论文提出了一个优雅的理论框架:将路由问题映射到 Shannon 的信息论体系。

在分析任何请求之前,路由的不确定性是最大的——对 K 个候选模型,路由熵为 $H(M r_{raw}) \approx \log_2 K$。每提取一个信号(关键词、语言、领域、复杂度…),就在减少这个不确定性,直到决策引擎可以做出近乎确定性的选择。

这自然引出了一个两层分解

信息论域(Information Theory Regime)   ←→   布尔代数域(Boolean Algebra Regime)
┌───────────────────────────────────┐     ┌──────────────────────────────────┐
│  Query → Signal Extraction → S(r) │  →  │  S(r) → Decision Engine → Model  │
│  (减少路由不确定性)                  │     │  (组合信号做出路由决策)             │
└───────────────────────────────────┘     └──────────────────────────────────┘

第一层是 Shannon 1948 年提出的概率信息论 [4]——通过信号提取最大化与路由结果的互信息;第二层是 Shannon 1938 年硕士论文的开关电路代数 [5]——通过 ${\text{AND}, \text{OR}, \text{NOT}}$ 组合信号条件来表达任意路由策略。信号向量 $S(r)$ 正是这两个域的接口。

1.3 比”难/易”二分类更复杂

先前的工作如 RouteLLM [1] 训练分类器在两个模型之间路由,RouterDC [2] 通过双对比学习嵌入,AutoMix [3] 将级联建模为 POMDP。但这些方法都是在孤立地解决模型选择,没有将信号提取、安全执行、多后端管理和插件扩展整合到统一框架中。

路由决策本身为什么难?把它拆开看,有三个内在矛盾——不是实现得不够好,而是任何路由系统都必须正面回答的问题。全文的架构设计,本质上就是对这三个矛盾的应答:

矛盾一:延迟与精度的矛盾。 路由决策发生在请求处理的最前端,它每花 1ms,TTFT 就多 1ms——但语义理解(领域、意图、复杂度)恰恰需要 ML 模型,而 ML 推理的延迟在 10-120ms 量级。路由要么选快(关键词、正则,但理解浅),要么选准(嵌入、分类器,但拖慢响应)。能否同时要快和准?

矛盾二:可解释与表达力的矛盾。 路由策略在合规场景(医疗、金融)必须可审计——”为什么这个请求去了这个模型”要有明确答案;但复杂的生产策略(多条件组合、跨提供商故障转移、多轮上下文)又要求表达能力。布尔规则可解释但呆板,神经网络表达力强但不可解释。能否用可解释的语法表达任意策略?

矛盾三:组合与统一的矛盾。 不同的部署场景(医疗合规、开发者工具、多云企业)需要完全不同的路由行为——但底层是同一套引擎。如果每个场景都写一套特殊逻辑,系统会分裂成互不相通的碎片;如果强行统一,每个场景都得不到满足。能否用”配置”而非”代码”切换场景,同时保持统一的行为语义?

后文每一章的展开都对应其中一个矛盾的解法:第 2 章的三层架构回答组合-统一,第 3 章按需计算 + 并行评估回答延迟-精度,第 4 章的布尔完备性回答可解释-表达力。第 12 章再回头评估这些解法各自的代价。

1.4 真实生产路由的完整需求

除了三个内在矛盾,生产路由系统还必须覆盖一组正交的功能需求——这些需求在论文中被列为设计目标,也是后文各章逐一落地的对象:

  • 多维信号:查询领域、模态、复杂度、语言、用户身份、实时性能指标
  • 隐私安全:Prompt 注入、PII 泄露、幻觉检测——且不同查询和用户角色需要不同的策略
  • 成本模型选择:在响应质量、推理成本和延迟之间寻找平衡
  • 部署多样性:同一框架要能同时服务于医疗合规部署(严格 PII 过滤)、开发者工具(激进缓存)和多云企业(跨提供商故障转移)——通过配置而非代码修改
  • 多轮状态:路由决策需在对话轮次间保持一致性

2. 架构设计:可组合的信号编排

第 1 章把路由拆成了三个内在矛盾——其中”组合-统一”矛盾(不同场景需要不同行为,但底层要同一套引擎)是架构层的总纲。VSR 对它的解法是可组合性:将复杂的路由逻辑分解为三个正交的层,每层通过明确定义的接口协作,使得不同部署场景通过配置而非代码来切换。

2.1 核心创新——三层架构

VSR 的核心设计贡献是可组合信号编排(Composable Signal Orchestration):将路由分解为三个正交的层,每一层都有明确定义的接口。

      ┌────────────────────────────────────────────────────────────────────────────────────────┐
      │         Programmable Neural-Symbolic Configuration  Γ = (S, D, Π, E)                   │
      │                             YAML / Kubernetes CRD                                      │
      └──────────────┬──────────────────────────────┬─────────────────────────────┬────────────┘
                     ↓                              ↓                             ↓
      ┌──────────────────────────┐   ┌──────────────────────────┐   ┌──────────────────────────┐
      │       INPUT LAYER        │   │      HIDDEN LAYERS       │   │    PROJECTION LAYER      │
r ───►│    Signal Extraction     ├─s►│    Decision Blocks       ├d*►│    Plugin Chain          ├m*──► MoM
      ├──────────────────────────┤   ├──────────────────────────┤   ├──────────────────────────┤
      │ Heuristic  (<1ms):       │   │ Boolean φ (AND/OR/NOT):  │   │ Pre-routing:             │
      │  · keyword               │   │  任意嵌套表达式树           │   │  · cache / RAG inject    │
      │  · language              │   │                          │   │  · memory retrieval      │
      │  · context length        │   │ Select d*:               │   │  · modality routing      │
      │  · authz                 │   │  · Priority-based        │   │  · system prompt         │
      ├──────────────────────────┤   │  · Confidence-weighted   │   │  · header auth           │
      │ ML-based  (10-120ms):    │   │                          │   ├──────────────────────────┤
      │  · embedding             │   │ Per-decision pool:       │   │ Model Select:            │
      │  · domain                │   │  M_d* ⊆ M                │   │  13 algos → m*           │
      │  · complexity            │   │                          │   │  (Elo/KNN/AutoMix…)      │
      │  · modality              │   │                          │   ├──────────────────────────┤
      │  · jailbreak / PII       │   │                          │   │ Post-routing:            │
      │  · factual / feedback    │   │                          │   │  · HaluGate detection    │
      │  · preference            │   │                          │   │  · cache write-back      │
      └──────────────────────────┘   └──────────────────────────┘   └──────────────────────────┘
                       ▲                                                          │
                       │    Response signals: hallucination / feedback / latency  │
                       └──────────────────────────────────────────────────────────┘

关键洞察:不同的部署场景只是在同一架构上的不同配置 $\Gamma = (\mathcal{S}, \mathcal{D}, \Pi, \mathcal{E})$:

场景 活跃信号 选择算法 关键插件
医疗合规 authz, domain, language 静态(仅合规模型) 严格 PII 过滤, 无缓存
开发者工具 complexity, embedding, keyword AutoMix 级联 激进语义缓存
多云企业 domain, modality, authz 延迟感知 多端点故障转移, 提供商认证
多轮助手 embedding, feedback, preference Elo + 会话固定 Responses API, 记忆检索

2.2 设计原则

三层架构的可行性依赖四条原则,每一条都是对”配置驱动”这一承诺的兑现:

  • 可组合性(Composability):复杂路由策略由简单原语组合而成——布尔信号条件组合为决策,类型化插件序列组合为执行链。
  • 正交性(Orthogonality):信号、决策和插件是独立模块。添加新信号类型只需实现一个评估函数,决策引擎仅通过类型和名称引用信号。
  • 闭环自适应(Closed-loop):双向信号流——响应端信号(幻觉检测、用户反馈、延迟度量)反馈回路由策略。
  • 决策级作用域(Per-decision scoping):安全阈值、缓存策略、候选模型等都绑定到单个决策而非全局应用。

3. 信号提取层:从请求中”读取”路由信息

三层架构确定了信号在系统中的位置——它是决策层的输入。但第 1 章的延迟-精度矛盾在这里立刻成为现实:语义理解需要 ML 模型(10-120ms),而路由发生在一线请求路径上。VSR 的解法是把信号拆成两级——毫秒级启发式 + 学习型——再配以”按需计算 + 并行评估”把墙钟时间压到可接受范围。本节详细介绍 13 种信号类型及其实现机制。

3.1 十三种信号类型

信号层将请求 $r$ 映射为结构化信号结果 $S(r)$——每个维度由一个二值匹配指示和置信度分数组成。

启发式信号(<1ms)

启发式信号基于规则和模式匹配,计算开销极低,适合快速筛选。

信号 机制 延迟
Keyword 正则/BM25/N-gram 模式匹配 + AND/OR/NOR 组合器 <0.1ms
Context Length Token 数量区间匹配 [min, max] <0.1ms
Language 统计 N-gram 语言检测(100+语言) <0.5ms
Authorization RBAC 角色信号,可插拔认证工厂(API Key, OAuth2, JWT…) <0.1ms

学习型信号(10-120ms)

学习型信号利用机器学习模型深入理解语义,虽然延迟略高但准确性更强。

信号 机制 延迟
Embedding 请求嵌入与参考文本的余弦相似度 15ms
Domain LoRA 分类器,训练于 MMLU 类别 60ms
Factual 二分类判断是否需要事实验证(HaluGate 哨兵) 55ms
Modality 三分类:自回归/扩散/两者 50ms
Complexity 对比嵌入分类器,Hard/Easy 候选集对比 50ms
Jailbreak BERT 分类器 + 对比嵌入(多轮检测) 55ms
PII Token 级 NER(人名、邮箱、SSN、信用卡…) 55ms
Feedback 多分类:满意/不满/澄清请求/想要替代 55ms
Preference 基于用户交互历史的个性化路由(默认调用外部 LLM,Generate 至 vLLM 端点,MaxTokens=1000) 200–500ms

关键优化——按需计算:引擎只计算被至少一个活跃决策引用的信号类型。典型配置中只有 3-5 个信号类型活跃,这比穷举计算减少了 50-70% 的提取延迟。所有信号并行评估,因此墙钟时间取决于最慢的活跃信号而非总和——典型配置(不含 Preference 这类 LLM 型信号)下约 120ms(对应论文给出的 ML 信号 10-120ms 范围上限)。一旦启用 Preference,墙钟时间由外部 LLM 调用决定(200-500ms)。

3.2 为什么选择 Encoder 模型

论文对这个选择给出了深刻的信息论解释:路由需要理解查询(领域、意图、复杂度、敏感内容位置),这对应于最大化隐状态 $H$ 与任务标签 $Y$ 之间的互信息 $I(H; Y)$。

  • 双向编码器(ModernBERT):每个 Token 基于完整上下文双向建模,产生捕获输入完整信息结构的表示
  • 因果解码器:每个 Token 仅基于左侧上下文——其表示优化于下一 Token 预测,捕获的是生成而非判别信息

这种双向”理解”在三个不同粒度上被利用:

  • 序列级(CLS 池化):领域分类、越狱检测、事实检查
  • Token 级(逐 Token 隐状态):PII 检测、幻觉 span 识别
  • 跨序列(交叉编码器):NLI 推理用于幻觉解释

3.3 代码映射:分类器实现

信号提取的代码实现在 src/semantic-router/pkg/classification/ 目录中:

classification/
├── classifier.go                  # 核心分类器,协调所有信号提取
├── keyword_classifier.go          # 关键词信号(Regex/BM25/N-gram)
├── embedding_classifier.go        # 嵌入相似度信号
├── complexity_classifier.go       # 复杂度信号(对比嵌入)
├── language_classifier.go         # 语言检测信号
├── context_classifier.go          # 上下文长度信号
├── authz_classifier.go            # 授权信号
├── fact_check_classifier.go       # 事实检查信号
├── feedback_detector.go           # 用户反馈信号
├── hallucination_detector.go      # 幻觉检测信号
├── contrastive_jailbreak_classifier.go  # 对比越狱检测
├── preference_classifier.go       # 偏好信号
├── unified_classifier.go          # 统一分类器(LoRA 架构)
└── vllm_classifier.go             # 外部 vLLM 模型分类

配置层面,信号类型在 pkg/config/config.go 中定义了 13 种信号常量:

// 定义支持的 13 种信号类型常量
const (
    SignalTypeKeyword      = "keyword"
    SignalTypeEmbedding    = "embedding"
    SignalTypeDomain       = "domain"
    SignalTypeFactCheck    = "fact_check"
    SignalTypeUserFeedback = "user_feedback"
    SignalTypePreference   = "preference"
    SignalTypeLanguage     = "language"
    SignalTypeContext       = "context"
    SignalTypeComplexity   = "complexity"
    SignalTypeModality     = "modality"
    SignalTypeAuthz        = "authz"
    SignalTypeJailbreak    = "jailbreak"
    SignalTypePII          = "pii"
)

4. 决策引擎:布尔代数驱动路由策略

信号层产出了一组结构化的信号结果,但”有信号”不等于”有决策”——把 13 种信号组合成一条路由规则,正是第 1 章可解释-表达力矛盾的战场。VSR 的选择是布尔代数:用 ${\text{AND}, \text{OR}, \text{NOT}}$ 构建任意嵌套的表达式树,既保证策略可审计(每个决策就是一条可读的布尔公式),又借助功能完备性定理证明表达力不设上限。本节介绍布尔表达式树的构建、功能完备性保证以及两种选择策略。

4.1 递归规则节点——布尔表达式树

每个决策定义为一个布尔公式 $\varphi$,通过递归规则节点构建任意嵌套的表达式树:

# 示例:仅当请求是代码领域且为英语时路由到代码模型
decisions:
  - name: "code_routing"
    priority: 100
    rules:
      operator: "and"
      conditions:
        - type: "domain"
          name: "computer_science"
        - type: "keyword"
          name: "code"
        - type: "language"
          name: "en"
    modelRefs:
      - model: "deepseek-coder"

更复杂的嵌套逻辑:

# NOR 逻辑:路由所有非 STEM 查询到通用模型
rules:
  operator: "not"
  conditions:
    - operator: "or"
      conditions:
        - type: "domain"
          name: "cs"
        - type: "domain"
          name: "math"
        - type: "domain"
          name: "physics"

4.2 功能完备性证明

论文给出了一个重要的理论保证:运算符集合 {AND, OR, NOT} 是功能完备的——任何布尔函数都可以用这些运算符表达。这意味着:

  • 单决策完备性:任何关于信号匹配指示器的布尔函数,都存在一个使用 AND/OR/NOT 的规则节点来表达。
  • 路由策略完备性:任何将信号向量映射到模型选择的策略,都可以通过带优先级排序的决策集来实现。

这个保证类似于数字电路设计中的可编程逻辑阵列(PLA)到通用组合电路的层次结构。

4.3 两种选择策略

决策引擎支持两种决策选择策略,分别适用于不同的管理需求:

\[d^* = \argmax_{d \in D_{\text{match}}} p_d \quad \text{(优先级策略:确定性、管理员控制)}\] \[d^* = \argmax_{d \in D_{\text{match}}} \text{conf}(d) \quad \text{(置信度策略:数据驱动、自适应)}\]

决策评估的计算复杂度为 $O(M \cdot L_{\text{max}})$,其中 $M$ 是决策数,$L_{\text{max}}$ 是每个决策最大条件数。实践中 $M \le 50$、$L_{\text{max}} \le 10$,决策评估仅需 <0.1ms。

4.4 代码映射:决策引擎

核心实现在 pkg/decision/engine.go 中:

type DecisionEngine struct {
    keywordRules   []config.KeywordRule
    embeddingRules []config.EmbeddingRule
    categories     []config.Category
    decisions      []config.Decision
    strategy       string  // "priority" 或 "confidence"
}

type SignalMatches struct {
    KeywordRules      []string
    EmbeddingRules    []string
    DomainRules       []string
    FactCheckRules    []string
    JailbreakRules    []string
    PIIRules          []string
    ComplexityRules   []string
    // ... 共 13 种信号匹配
    SignalConfidences map[string]float64 // "signalType:ruleName" → 置信度
}

布尔表达式树通过 RuleNode 结构递归定义(pkg/config/decision_config.go):

type RuleNode struct {
    Type       string     `yaml:"type,omitempty" json:"type,omitempty"`       // 叶节点:信号类型
    Name       string     `yaml:"name,omitempty" json:"name,omitempty"`       // 叶节点:规则名称
    Operator   string     `yaml:"operator,omitempty" json:"operator,omitempty"`   // 组合节点:and/or/not
    Conditions []RuleNode `yaml:"conditions,omitempty" json:"conditions,omitempty"` // 子节点
}

func (n *RuleNode) IsLeaf() bool {
    return n.Type != ""
}

5. 十三种模型选择算法

决策引擎回答了”请求该走哪条路由”,但每条路由背后往往挂着多个候选模型——”选哪个”是另一个独立问题。第 1 章提到的成本模型选择需求在这里具体化:VSR 集成了七大家族、十三种算法,覆盖从静态评分到强化学习的多种选择策略。这一章的读法不是”记下十三种算法”,而是理解选择算法要优化的目标不同——有的追求质量稳定(Rating-Based),有的追求延迟优先(Latency-Aware),有的用历史数据学习(Classical ML),有的干脆不选而是组合(ReMoM)。

5.1 算法家族概览

代码中通过 SelectionMethod 枚举定义了 12 种选择方法常量(pkg/selection/selector.go),另有 ReMoM 作为更高层的多轮编排模式通过 AlgorithmConfig.ReMoM 配置。合计覆盖七大家族、十三种算法

家族 算法 核心机制
Rating-Based Static, Elo 静态评分 / Bradley-Terry 模型偏好反馈
Embedding RouterDC, Hybrid 双对比学习 / 多维加权评分
Cascading AutoMix POMDP,从便宜模型开始逐级升级
Classical ML KNN, KMeans, SVM, MLP 基于历史路由记录的分类器
RL RL-Driven, GMTRouter Router-R1 奖励驱动 / 异构图神经网络
Latency Latency-Aware 基于实时 TPOT/TTFT 的自适应选择
Multi-Round ReMoM 多轮并行推理 + LLM 驱动合成

5.2 ReMoM:多轮推理的新范式

ReMoM (Reasoning for Mixture of Models) 是 VSR 与其余 12 种算法结构上最不同的一个——它不是”选一个模型”,而是多轮并行推理 + 合成:每轮同时让多个模型回答问题,用合成 prompt 将响应融合后进入下一轮,逐轮收窄宽度,最终输出单一合成结果。它受 PaCoRe(arXiv:2601.05593)启发,实现位于 pkg/looper/remom.go,通过 AlgorithmConfig.ReMoM 配置(而非 SelectionMethod 枚举)启用。

Round 1 (b₁=4): Query → [m_A, m_B, m_A, m_C] → 4 responses (并行)
                        ↓ Synthesis Prompt
Round 2 (b₂=2): Synthesized → [m_B, m_C] → 2 responses (并行)
                        ↓ Synthesis Prompt
Final (b=1):    Final synthesis → single output

配置示例:

algorithm:
  type: remom
  remom:
    breadth_schedule: [4, 2]
    model_distribution: equal
    temperature: 1.0
    include_reasoning: true

breadth_schedule(代码定义:ReMoMAlgorithmConfig.BreadthSchedule []int,默认 [4])的每个元素对应一轮并行推理的宽度——上述 [4, 2] 即第一轮并行 4 个候选、第二轮并行 2 个候选;最终合成轮固定输出单个结果,不计入 schedule。其他可调参数:compaction_strategy(轮间上下文压缩策略)、max_concurrent(并行请求上限)、synthesis_template(合成 prompt 模板)、include_intermediate_responses(是否保留中间轮响应)。ReMoM 设计受 PaCoRe(arXiv:2601.05593)启发。

5.3 代码映射:选择算法工厂

所有 13 种算法实现在 pkg/selection/ 目录中,通过统一接口暴露:

selection/
├── selector.go         # 统一 Selector 接口和工厂
├── factory.go          # 算法工厂,按配置创建选择器
├── static.go           # Static 静态评分
├── elo.go              # Elo Rating(Bradley-Terry)
├── router_dc.go        # RouterDC 双对比学习
├── hybrid.go           # Hybrid 多维加权
├── automix.go          # AutoMix (POMDP 级联)
├── ml_adapter.go       # KNN/KMeans/SVM 适配器(Linfa Rust)
├── rl_driven.go        # Router-R1 RL 驱动选择
├── router_r1_client.go # Router-R1 外部客户端
├── gmtrouter.go        # GMTRouter 图神经网络
├── latency_aware.go    # 延迟感知选择
├── pomdp_solver.go     # POMDP 求解器(AutoMix 用)
├── metrics.go          # 实时延迟指标收集
└── storage.go          # 选择记录持久化

6. 安全子系统:信号驱动的安全架构

前三章构建了”信号 → 决策 → 选择”的主链,但第 1 章的隐私安全需求暗示了一个架构选择:安全检测(越狱、PII)放在链路的哪个位置?串行插件(先安全检查、再路由)会叠加延迟;独立旁路又脱离路由上下文。VSR 的答案是把安全检测做成信号——与意图信号在同一层并行评估。这种架构实现了零额外延迟、可组合的安全策略和统一可观测性。

6.1 安全即信号,而非串行插件

VSR 的安全设计理念非常独到:越狱检测和 PII 检测都是第一等公民信号,而不是串行插件。它们运行在信号提取层,与所有其他信号(关键词、嵌入、领域等)并行评估。

三个关键优势:

  1. 零额外延迟:安全分类器与意图信号并发执行,墙钟时间是 max(safety, intent) 而非 safety + intent。
  2. 可组合:安全信号可与领域信号通过 AND/OR 逻辑组合——如”仅对金融查询启用严格越狱检测”。
  3. 统一可观测性:安全结果作为标准信号匹配出现在 HTTP 头中。

6.2 越狱检测:BERT + 对比嵌入双管齐下

BERT 分类器方法:高精度单轮检测,支持二/三分类(benign/injection/jailbreak),可配置阈值。

对比嵌入方法:专门针对多轮”温水煮青蛙”攻击,通过 max-contrastive-chain 聚合:

\[\delta(m) = \max_{j \in K_{jb}} \cos(m, j) - \max_{b \in K_{ben}} \cos(m, b)\] \[\Delta = \max_{m \in M_{user}} \delta(m)\]

include_history 启用时,系统评估对话中的每一条用户消息并取最大分数——即使当前消息无害,但如果历史中存在高分轮次,也会触发检测。

6.3 HaluGate:门控幻觉检测

HaluGate 是一个三阶段门控管道:

Query → [Sentinel] →(factual?)→ Response → [Detector] → [Explainer] → Results
                    ↓(no)
                    skip (40-60% 的请求)
  1. Sentinel(门控):轻量级二分类,判断查询是否需要事实验证,40-60% 请求可跳过
  2. Detector(Span 识别):Token 级分类器,识别响应中不被支持的 span
  3. Explainer(NLI 解释):自然语言推理模型,区分”矛盾”和”不支持”

期望成本: $E[\text{Cost}] = C_{\text{sent}} + p_{\text{factual}} \cdot (C_{\text{det}} + \bar{k} \cdot C_{\text{nli}})$,其中 $C_{\text{sent}}$ 为门控分类器成本、$p_{\text{factual}}$ 为查询通过门控的概率、$C_{\text{det}}$ 为 span 检测器成本、$\bar{k}$ 为平均检测到的 span 数、$C_{\text{nli}}$ 为单条 NLI 解释成本

支持四种响应策略:block(拒绝)、header(元数据传播)、body(警告注入)、none(仅记录)。


7. 多运行时 ML 推理架构

信号层依赖 9 种学习型信号,每一种背后都是 ML 推理——这带来一个此前各章回避的问题:推理运行时放在哪。如果路由进程内嵌 Python 推理栈,信号提取会背上 GIL 和解释器开销;如果外置成服务,每次信号提取都多一次网络往返。VSR 的工程答案是用 Rust 原生 ML 推理替代 Python,通过 C FFI 和 CGo 暴露给 Go 路由进程——消除了 Python 运行时开销,同时保持了高性能的 GPU 推理能力。这一选择不是性能洁癖:第 3 章的延迟-精度矛盾,最终是在运行时这一层才被彻底解决的。

7.1 四个推理运行时

四个并行的 Rust 推理运行时通过 CGo/FFI 桥接层与 Go 路由进程集成:

┌────────────────────────────────────────────────────────────────┐
│            Go Routing Process (信号提取 · 决策引擎 · 插件链)       │
└───────┬──────────┬──────────────┬──────────────┬───────────────┘
    CGo/FFI    CGo/FFI       CGo/FFI        CGo/FFI
        ↓          ↓              ↓              ↓
┌───────────┐ ┌──────────┐ ┌──────────────┐ ┌───────────────┐
│  Candle   │ │  Linfa   │ │ ONNX Runtime │ │  NLP Binding  │
│ (GPU/CPU) │ │ (CPU)    │ │  (CPU/GPU)   │ │   (CPU)       │
│           │ │          │ │              │ │               │
│ BERT,     │ │ KNN,     │ │ mmBERT-32K   │ │ BM25, N-gram  │
│ ModernBERT│ │ KMeans,  │ │ 2D Matryoshka│ │ AND/OR/NOR    │
│ LoRA,MLP  │ │ SVM      │ │ Layer早退     │ │ 关键词信号     │
│ Flash Attn│ │          │ │ 维度截断      │ │               │
└───────────┘ └──────────┘ └──────────────┘ └───────────────┘
运行时 目标硬件 任务 框架
Candle GPU (CUDA), CPU 分类、LoRA、MLP HuggingFace Candle
Linfa CPU only KNN、KMeans、SVM Linfa (Rust ML)
ONNX RT CPU, GPU 嵌入计算 ONNX Runtime
NLP Binding CPU only BM25、N-gram bm25 + ngrammatic

Rust 实现的代码在 candle-binding/src/ 目录:

candle-binding/src/
├── classifiers/        # 统一/LoRA/MLP/传统分类器
├── core/               # 相似度、分词、配置加载
├── ffi/                # C FFI 导出供 Go 使用
├── model_architectures/# BERT, ModernBERT, DeBERTa 等模型架构
├── utils/              # 辅助工具
└── lib.rs

7.2 LoRA 多任务架构

线性内存扩展问题:6 个分类任务 × 150M 参数 = 900M 参数总内存。

LoRA 解决方案:1 个共享基座模型 + 6 个微小适配器(每个约 0.2MB),实现 ~6x 内存缩减。

\[M_{\text{LoRA}} = |\theta_{\text{base}}| + n \times 2rd \quad (r{=}32,\; d{=}768,\; \text{每适配器仅 49,152 参数})\]

MoM 模型家族涵盖:领域分类、PII 检测、越狱检测、事实检查、幻觉检测、NLI 解释、用户反馈、模态分类、语义嵌入、工具选择、意图分类等 11 个任务。


8. 请求处理管道:Envoy ExtProc

前六章各自回答了系统的一个组件,但组件要成为系统,还需要回答:这些机制以什么顺序、在什么时机作用于一个真实的请求? VSR 的部署答案是作为 Envoy External Processor 运行——在 HTTP 请求的四个阶段被 Envoy 调用,通过透明拦截实现请求处理管道。本节详细介绍四个处理阶段和 10 步管道流程——它把信号提取、决策、缓存、选择、安全全部串成一条严格顺序的流水线。

8.1 透明拦截

VSR 作为 Envoy External Processor (ExtProc) 运行——一个双向 gRPC 服务,在 HTTP 请求的四个阶段被 Envoy 调用。客户端发送标准 OpenAI 兼容请求,完全无感知路由层的存在。

四个处理阶段与对应职责:

阶段 入口文件 职责
Request Header processor_req_header.go 端点检测、头部变更、多端点解析
Request Body processor_req_body.go 核心路由逻辑:信号提取、决策、缓存、模型选择
Response Header processor_res_header.go 运行时元数据注入、头部变更
Response Body processor_res_body.go 幻觉检测、流式聚合、缓存回填、记忆存储

每个阶段通过双向 gRPC 流与 Envoy 通信——Envoy 发送 ProcessingRequest,ExtProc 返回 ProcessingResponse,可修改头部、替换身体或提前返回响应(如缓存命中或安全拦截)。

8.2 请求体管道

核心路由逻辑实现为一个严格顺序的管道:

r → parse → signals S(r) → decide d* → [plugins_pre] → select m* → route e*

完整的 10 个阶段:

  1. API 翻译(Responses API → Chat Completions)
  2. 请求解析和提供商检测
  3. 信号提取和决策评估
  4. fast_response 检查(安全拦截)
  5. 语义缓存查询
  6. RAG 上下文注入
  7. 模态路由(文本 vs. 扩散)
  8. 记忆检索
  9. 模型选择、系统提示注入、头变更
  10. 多端点解析和提供商认证注入

8.3 代码映射:ExtProc 实现

管道主入口在 pkg/extproc/processor_req_body.go

func (r *OpenAIRouter) handleRequestBody(v *ext_proc.ProcessingRequest_RequestBody,
    ctx *RequestContext) (*ext_proc.ProcessingResponse, error) {
    // 1. Responses API 翻译
    requestBody, earlyResponse := r.translateResponseAPIRequest(ctx.OriginalRequestBody, ctx)

    // 2. 快速字段提取(gjson,避免完整反序列化)
    fast, err := r.extractFastRequestState(requestBody, ctx)

    // 3. 信号提取 + 决策 + 缓存/安全
    decisionState, earlyResponse := r.runRequestPreRoutingStages(originalModel, fast, ctx)

    // 4. 准备请求(仅在需要 body 变更时才完整解析)
    openAIRequest, earlyResponse, err := r.prepareRequestForModelRouting(requestBody, ...)

    // 5. 模型选择和路由(注意 decisionState 被解构为三个独立参数)
    return r.handleModelRouting(
        openAIRequest, originalModel,
        decisionState.decisionName,
        decisionState.reasoningDecision,
        decisionState.selectedModel,
        ctx,
    )
}

请求/响应过滤器以 req_filter_*.go / res_filter_*.go 命名分布在 pkg/extproc/ 中,分别对应信号提取、缓存、记忆、RAG、模态、图像生成、推理模式、系统提示等管道阶段。

8.4 多端点和多提供商

VSR 原生支持跨异构后端路由——vLLM、OpenAI、Anthropic、Azure、Bedrock、Gemini、Vertex AI,自动处理提供商特定的协议翻译、认证注入和端点拓扑。

可插拔的认证工厂支持:API Key、OAuth2/OIDC、Cloud IAM(AWS SigV4、GCP、Azure AD)、透传和自定义认证。


9. 记忆系统和 RAG

第 8 章的管道把单次请求处理得井井有条,但第 1 章的多轮状态需求戳中一个盲区:单次路由是无状态的,对话却是有状态的——用户上一轮说”帮我改那段代码”,下一轮说”再改一下”,路由必须记得”那段代码”指的是什么。VSR 的记忆系统维护用户作用域的对话知识,支持情景记忆存储和混合检索。本节介绍 ReflectionGate 过滤机制和三路并行检索管道——它让路由决策在对话轮次间保持一致,而不只是逐请求独立判断。

9.1 情景记忆 + ReflectionGate

核心设计分为写入和读取两条路径:

写入路径

  • 每轮对话直接存储为情景块(Q:/A: 格式),无需 LLM——消除写入时的推理开销
  • 熵门控:丢弃低信号轮次(问候、确认、单词回复),减少索引污染
  • 每 $s$ 轮(存储间隔,默认 3)额外存储一个跨越最近 $w$ 轮(窗口宽度,默认 5)的滑动窗口块——捕捉跨轮次的上下文依赖

读取路径

  • 混合检索:向量相似度 + BM25 + N-gram
  • ReflectionGate 四阶段过滤:安全(正则阻止列表)→ 时效衰减 → 去重(Jaccard)→ 预算限制
  • 注入为独立对话消息,位于系统指令之后、用户轮次之前

实现分布在 pkg/memory/(store/extractor/reflection/milvus_store),生产后端为 Milvus。

9.2 混合 RAG 检索管道

三路并行检索 + 分数融合:

Query → [Embed    ] → Vector Index  → cosine ∈ [0,1]  ─┐
      → [Tokenize ] → BM25 Index   → norm → [0,1]      ├→ Score Fusion → Ranked Results
      → [Char-gram] → N-gram Index → Jaccard ∈ [0,1]  ─┘

融合模式:

  • 加权融合w_v·s_vec + w_b·s_bm25 + w_n·s_ngram(默认 0.7:0.2:0.1)。
  • RRF(倒数排名融合)score(d) = Σ 1/(k + rank_r(d))

10. DSL 配置语言:可编程的神经符号推理引擎

第 2 章承诺”不同场景通过配置而非代码切换”——但配置本身会膨胀:13 种信号、嵌套布尔规则、算法参数、插件链、后端端点,如果全部写进 YAML,配置文件会比代码更难维护。VSR 的回答是为配置发明一种语言:DSL 配置语言。VSR 将整个系统解释为一个可编程的神经符号推理引擎,DSL 正是这一设计理念的体现——信号是神经层,布尔规则是符号层,DSL 让两者在同一份文本中可组合。

  • 神经信号提取层 ≈ Transformer 的嵌入层(将非结构化输入转为结构化表示)
  • 符号决策评估层 ≈ Mixture-of-Experts 门控(布尔公式作为专家门)
  • 优先级排序 ≈ 早退机制(第一个匹配的决策捕获请求)

10.1 DSL 语法示例

以下是一个完整的 DSL 配置示例,定义了信号、路由决策和后端:

# 信号定义:声明路由引擎可使用的信号类型
SIGNAL domain math {
  description: "Mathematics"
  mmlu_categories: ["math"]
}

SIGNAL keyword urgent_request {
  operator: "any"
  keywords: ["urgent"]
}

# 路由决策:布尔表达式 + 模型选择 + 插件
ROUTE math_route {
  PRIORITY 100
  WHEN keyword("urgent_request") AND domain("math")
  MODEL "qwen2.5:3b" (reasoning = true, effort = "high")
  ALGORITHM confidence {
    confidence_method: "hybrid"
    threshold: 0.5
  }
  PLUGIN system_prompt {
    system_prompt: "You are a math expert."
  }
}

# 后端定义:推理服务端点
BACKEND vllm_endpoint ollama {
  address: "127.0.0.1"
  port: 11434
}

# 全局配置
GLOBAL {
  default_model: "qwen2.5:3b"
  strategy: "priority"
}

其中 WHEN 子句支持完整的布尔表达式语法,包括嵌套括号、AND/OR/NOT 组合:

# 复杂布尔逻辑示例
WHEN (domain("math") OR domain("physics")) AND NOT domain("other")

10.2 编译管道

DSL 的处理流程为:

DSL 源码 → Lexer → Parser (participle) → AST → Validator → Compiler → RouterConfig
                                                                        ↓
                                                    Decompiler ← RouterConfig
  • 解析器parser.go):基于 participle 框架,支持错误恢复(将输入拆分为顶层块独立解析)
  • 验证器validator.go):三级诊断——DiagError(语法错误)、DiagWarning(引用错误)、DiagConstraint(约束违反),支持 QuickFix 自动修复建议
  • 编译器compiler.go):AST → config.RouterConfig,支持多目标发射(扁平 YAML、Kubernetes CRD、Helm Charts)
  • 反编译器decompiler.go):config.RouterConfig → DSL 源码,实现往返变换

DSL 实现在 pkg/dsl/ 目录中,支持从自然语言规范通过 LLM Coding Agent 自动合成路由策略。


11. 评估结果

本节展示 GPU 加速推理、LoRA 内存效率和端到端路由的实验数据(信号提取延迟已在 §3.1 给出)。

11.1 GPU 加速推理

mmBERT-32K 分类器,AMD MI300X:

序列长度 CPU (ms) GPU SDPA (ms) Flash Attention (ms)
512 120 6.0 19
2,048 809 14.1 32
8,192 9,656 OOM (3 模型) 105
32,768 - OOM 756

Flash Attention 使得 3 个分类器并发加载时仍可处理 32K Token,而 SDPA 在 4K 以上就 OOM。

11.2 LoRA 内存效率

下表展示了不同任务数量下独立模型与 LoRA 方案的内存占用对比:

任务数 (n) 独立模型 (MB) LoRA (MB)
1 573 573
3 1,719 574
6 3,438 575

6 个任务下 ~6x 内存缩减。

11.3 端到端路由

通过 8 个场景 Profile 的 E2E 测试验证了多端点路由、多提供商认证、RBAC、ML 模型选择、关键词路由、嵌入路由、RAG + Responses API 等场景的正确性。


12. 代价与边界

前十一章把 VSR 的设计讲成了一条顺理成章的解决链。但第 1 章承诺过:每个矛盾的解法都有代价。这一章把代价摆到桌面上——哪些是设计权衡(事实),哪些是推演(分析),分开标注。

12.1 路由决策本身的延迟成本

事实:信号提取的墙钟时间在典型配置下约 120ms,启用 Preference 后 200-500ms。分析:路由发生在请求路径的最前端——这 120ms 是叠加在 TTFT 上的。论文报告端到端延迟降低 47.1%(13.09s vs 24.76s),但那是模型推理占主导的长响应场景;对短响应、TTFT 敏感的场景(交互式聊天、代码补全),120ms 的路由决策可能吃掉响应预算的 10-30%。路由的收益来自”选对模型”——但选对的收益在长响应中才显著,而路由的成本在短响应中占比更高。这是路由系统最根本的适用边界:收益与负载类型强相关,不是普适加速。

12.2 ReMoM 的成本结构

事实:ReMoM 每轮并行 N 个模型调用,[4, 2] 配置即 4+2=6 次推理加 2 次合成。分析:token 消耗是单模型路由的 6 倍以上。它在质量敏感、延迟不敏感的场景(离线评测、复杂推理)成立;在成本敏感或高并发场景,等于把路由系统的成本放大一个量级。第 5 章的算法表里它被列为一种”选择算法”,但它和其余 12 种根本不在同一个成本维度——它是”用 N 倍成本买质量上限”,其余是”在固定成本内选最优”。

12.3 “安全即信号”的暗面

事实:安全信号与意图信号同池并行评估,墙钟取 max 而非 sum。分析:max 语义确实消除了”串行叠加”,但它没有消除竞争——墙钟时间取决于所有活跃信号中谁最慢。这意味着:在轻量配置(只有 keyword、language 等毫秒级信号)中启用安全信号,墙钟会从 <1ms 直接跳到安全信号的 55ms——安全成为新的最慢信号;而在已含 ML 信号的配置(最慢 60ms 的 domain)中,55ms 的安全信号藏在 max 之下,表面”零额外延迟”。所以”零额外延迟”只在”安全信号不是最慢”时成立——安全是新增计算,其成本从”串行加法”变成了”与意图信号竞争最慢位置”。轻量配置恰恰是安全最该便宜的地方,但 max 语义让它在轻量配置里最贵。

12.4 完备性 ≠ 易用性

事实:4.2 证明 AND/OR/NOT 可表达任意布尔函数。分析:理论完备性和实际可维护性之间隔着一条沟——表达一个”除 A、B、C 外的所有领域”的策略,YAML 需要写一层 NOT(OR(A,B,C)),而每个嵌套都增加心智负担。M≤50 决策、L≤10 条件的实践中,复杂策略的配置文件会膨胀到难以审查。完备性保证了”不存在表达不了的策略”,但没有保证”表达起来不难”。DSL(第 10 章)缓解了 YAML 的冗长,但把学习成本转移到了 DSL 语法本身。

12.5 十三种信号的维护成本

事实:每种学习型信号都是一个要训练、评估、监控的模型。分析:13 种信号 × 训练数据 + 精度监控 + 漂移处理,是持续的运维负担。第 3 章说”典型配置只激活 3-5 个”——但激活之外,未使用的信号仍然需要维护(代码、依赖、模型注册)。VSR 的”正交性”设计让新增信号只需实现一个评估函数,但生产环境中每一个信号都是长期负债。

12.6 故障面的不对称

事实:VSR 是 Envoy 的 ExtProc,位于请求关键路径。分析:路由到错误模型 = 一次质量损失(可接受);路由进程本身故障 = 全部请求失败(灾难)。13 信号 × 13 算法 × 4 运行时 × DSL 编译器的复杂度意味着故障面远大于一个简单的静态路由。文章和论文都没有系统讨论降级策略——ExtProc 超时后 Envoy 是放行还是拒绝?路由进程崩溃时是否有 fallback 到直连后端?这是生产部署前必须回答的问题。

12.7 与轻量方案的分工

事实:RouteLLM [1] 训练一个分类器完成双模型路由,RouterDC [2] 用双对比嵌入。分析:VSR 的能力密度(13 信号、13 算法、安全、记忆、RAG、DSL)是这些轻量方案的 10 倍以上,但复杂度也随之而来。这不是缺点——它服务的是”异构模型集群 + 合规 + 多云”的重型场景。真正的问题在于:什么时候该用 VSR,什么时候该用 RouteLLM? 判断标准是需求维度:如果你只需要”难的问题走大模型,简单的问题走小模型”,一个二分类器就是最优解;当你需要可审计的策略、跨提供商的认证、多轮记忆时,才值得引入 VSR 的完整栈。


13. 总结与展望

本文从第 1 章的三个内在矛盾出发,沿着”架构 → 信号 → 决策 → 选择 → 安全 → 运行时 → 管道 → 记忆 → DSL”的解决链,完整解析了 vLLM Semantic Router;第 12 章又把每个解法的代价摆回桌面。现在可以做一个有边界的总结——它擅长什么、在什么边界内成立、往哪走。

13.1 核心贡献回顾

vLLM Semantic Router 的核心价值在于提出了一种从理论到实践都自洽的路由框架

  1. 信息论基础:将路由问题映射为 Shannon 不确定性缩减,为信号提取提供理论指导。
  2. 可组合架构:三层分离(信号-决策-插件),不同部署场景通过配置而非代码切换。
  3. 工程卓越:Rust 原生 ML 推理消除 Python 开销,Flash Attention 支持长上下文,LoRA 统一多任务。
  4. 生产就绪:600+ 合并贡献,50+ 工程师参与,Envoy ExtProc + K8s Operator 完整部署栈。

13.2 未来方向

论文和项目为多个研究方向留下了空间:

  • 学习型决策策略:用神经路由网络替代手工布尔规则
  • 自适应成本优化:在线学习持续优化模型和提供商选择
  • 跨提供商一致性:确保同一对话跨不同提供商路由时的行为一致性
  • Agent 驱动策略合成:LLM Coding Agent 从自然语言规范自动生成路由配置
  • 多协议适配:从 Envoy ExtProc 扩展到 HTTP REST、原生 gRPC、Nginx 等

vLLM Semantic Router 不仅是一个路由组件——它展示了如何在 LLM 系统中引入系统级智能,让”哪个模型处理哪个请求”这个决策变得科学、可配置、可解释且可验证。


参考文献

[1] I. Ong et al., “RouteLLM: Learning to Route LLMs with Preference Data,” arXiv preprint arXiv:2406.18665, 2024. [2] K. Wang et al., “RouterDC: Query-Based Router by Dual Contrastive Learning,” arXiv preprint arXiv:2409.00409, 2024. [3] A. Aggarwal et al., “AutoMix: Automatically Mixing Language Models,” arXiv preprint arXiv:2404.08815, 2024. [4] C. E. Shannon, “A Mathematical Theory of Communication,” Bell System Technical Journal, vol. 27, no. 3, pp. 379-423, 1948. [5] C. E. Shannon, “A Symbolic Analysis of Relay and Switching Circuits,” Trans. AIEE, vol. 57, no. 12, pp. 713-723, 1938. [6] vLLM Semantic Router Team, “When to Reason: Semantic Router for vLLM,” arXiv preprint arXiv:2510.08731, NeurIPS 2025 MLForSys Workshop.