设计文档:多协议适配器架构
作者: vLLM Semantic Router 团队
状态: 待实现
创建: 2026 年 2 月
最后更新: 2026 年 2 月
概述
本文描述 vLLM Semantic Router 的多协议适配器架构设计与实现思路,在 Envoy ExtProc 之外抽象 API 层,以支持多种前端协议。
背景
Semantic Router 曾通过 gRPC 与 Envoy External Processor(ExtProc)紧耦合。这虽能与 Envoy 深度集成,但对以下用户形成门槛:
- 希望在不部署 Envoy 的情况下使用路由器
- 偏好直接 HTTP/REST 集成
- 使用 Nginx 或其他反向代理
- 在开发或测试时需要更简化的部署拓扑
动机
- 灵活性:无需 Envoy 基础设施即可获得直连 HTTP API
- 测试:无需完整 Envoy 部署即可轻量测试
- 可扩展性:支持 nginx、原生 gRPC 与自定义协议
- 可复用性:所有协议共享同一路由引擎
- 部署形态:支持 serverless、边缘与简化部署
目标
主要目标
- 协议抽象:将路由逻辑与协议相关代码分离
- 多协议支持:允许多种协议同时工作
- 向后兼容:保留现有 ExtProc 能力
- 共享状态:缓存、重放与路由决策的单一事实来源
- 易于扩展:新增协议适配器的固定模式
非目标
- 替换或弃用 Envoy ExtProc
- 改变路由决策算法或分类逻辑
- 除适配器相关节外修改配置格式
- 支持破坏抽象层的协议专属特性
设计原则
1. 单一路由流水线
关键约束: 所有路由逻辑必须流经 RouterEngine.Route(),无一例外。
- 适配器将协议翻译为
RouteRequest→ 调用RouterEngine.Route() RouterEngine.Route()返回RouteResponse→ 适配器再翻译回协议- 适配器不得重复实现分类、安全、缓存、重放逻辑
- 适配器不得直接调用分类器、缓存或重放记录器
2. 薄适配器层
适配器仅做协议翻译:
- 解析协议专属请求格式
- 转换为
RouteRequest - 调用
RouterEngine.Route() - 将
RouteResponse转换为协议格式 - 返回给客户端
3. RouterEngine 拥有全部路由
RouterEngine.Route() 是唯一发生以下行为之处:
- 分类
- PII/越狱检测
- 缓存读/写
- 工具选择
- 重放记录
- 后端选择
- 代理(或返回代理信息)
设计
架构概览
┌────────────────────────────────────────────────────────────┐
│ Application Layer │
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Adapter Manager │ │
│ │ - Reads adapter config │ │
│ │ - Creates protocol adapters │ │
│ │ - Manages lifecycle │ │
│ └────── ┬────────┬────────┬───────────┬──────────────┘ │
│ │ │ │ │ │
│ ┌──────▼──┐ ┌───▼─── ┐ ┌─▼──────┐ ┌──▼─────┐ │
│ │ ExtProc │ │ HTTP │ │ gRPC │ │ Nginx │ │
│ │ Adapter │ │Adapter │ │Adapter │ │Adapter │ │
│ │ ┌─────┐ │ │ ┌─────┐│ │ ┌─────┐│ │ ┌─────┐│ │
│ │ │Parse│ │ │ │Parse││ │ │Parse││ │ │Parse││ │
│ │ │ExtP │ │ │ │HTTP ││ │ │gRPC ││ │ │NJS ││ │
│ │ └──┬──┘ │ │ └─┬───┘│ │ └─┬───┘│ │ └──┬──┘│ │
│ │ │Conv│ │ │Con │ │ │Con │ │ │Con│ │
│ │ ▼ │ │ ▼ │ │ ▼ │ │ ▼ │ │
│ │ ┌─────┐ │ │ ┌────┐ │ │ ┌────┐ │ │ ┌─────┐│ │
│ │ │Req │ │ │ │Req │ │ │ │Req │ │ │ │Req ││ │
│ │ └──┬──┘ │ │ └─┬──┘ │ │ └─┬──┘ │ │ └──┬──┘│ │
│ └────┼────┘ └───┼────┘ └───┼────┘ └────┼───┘ │
│ │ │ │ │ │
│ └──────────┴──────────┴──────────┘ │
│ Single Entry Point │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ RouterEngine.Route() │ │
│ │ 1. Classify request │ │
│ │ 2. Check PII / jailbreak │ │
│ │ 3. Check cache │ │
│ │ 4. Select tools │ │
│ │ 5. Select model/backend │ │
│ │ 6. Record replay │ │
│ │ 7. Proxy to backend (via Backend Layer) │ │
│ │ 8. Update cache │ │
│ └──────────────┬───────────────────────────┘ │
│ │ │
│ ▼ │
│ RouteResponse │
│ │ │
│ ┌──────────────┼──────────────┬───────────┐ │
│ │ │ │ │ │
│ ┌─────▼─────┐ ┌──────▼────┐ ┌───────▼───┐ ┌─────▼─────┐ │
│ │ ExtProc │ │ HTTP │ │ gRPC │ │ Nginx │ │
│ │ Adapter │ │ Adapter │ │ Adapter │ │ Adapter │ │
│ │ ┌───────┐ │ │ ┌───────┐ │ │ ┌───────┐ │ │ ┌───────┐ │ │
│ │ │Convert│ │ │ │Convert│ │ │ │Convert│ │ │ │Convert│ │ │
│ │ │to gRPC│ │ │ │to HTTP│ │ │ │gRPC │ │ │ │to NJS │ │ │
│ │ └───────┘ │ │ └───────┘ │ │ └───────┘ │ │ └───────┘ │ │
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │ │ │
└────────┼─────────────┼─────────────┼─────────────┼─────────┘
│ │ │ │
└─────────────┴─────────────┴─────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Backend Abstraction Layer │
└──────┬──────────────────┬───────────────┘
│ │
┌────────▼────────┐ ┌──────▼──────────┐
│ Envoy Proxy │ │ Direct Proxy │
│ (ExtProc mode) │ │ (HTTP/gRPC) │
│ - Dynamic fwd │ │ - HTTP client │
│ - Headers only │ │ - Full response │
└────────┬────────┘ └──────┬──────────┘
│ │
└──────────┬───────┘
▼
┌────────────────────────────┐
│ Inference Backends │
│ ┌────────┐ ┌────────┐ │
│ │ vLLM │ │Ollama │ │
│ │Server │ │Server │ │
│ └────────┘ └────────┘ │
└────────────────────────────┘
要点: 适配器是薄翻译层,智能全部在 RouterEngine。
组件设计
1. RouterEngine(核心)
位置: pkg/router/engine/
职责:
- 与协议无关的路由逻辑
- 请求分类与决策求值
- 语义缓存操作
- 工具选择与嵌入
- 路由器重放记录
- PII 与越狱检测
- 模型选择
主要方法:(与英文原文一致,见代码块)
type RouterEngine struct {
Config *config.RouterConfig
Classifier *classification.Classifier
PIIChecker *pii.PolicyChecker
Cache cache.CacheBackend
ToolsDatabase *tools.ToolsDatabase
ModelSelector *selection.Registry
ReplayRecorders map[string]*routerreplay.Recorder
}
func (e *RouterEngine) Route(ctx context.Context, req *RouteRequest) (*RouteResponse, error)
func (e *RouterEngine) ClassifyRequest(ctx context.Context, messages []Message) (*ClassificationResult, error)
func (e *RouterEngine) CheckCache(ctx context.Context, model, query, decisionName string) (string, bool, error)
func (e *RouterEngine) UpdateCache(ctx context.Context, model, query, response, decisionName string) error
func (e *RouterEngine) SelectTools(ctx context.Context, query string, topK int) ([]openai.ChatCompletionToolParam, error)
func (e *RouterEngine) RecordReplay(ctx context.Context, decisionName string, record *routerreplay.RoutingRecord) error
设计决策:
- 所有适配器共享单实例
- 有状态(维护缓存、重放记录器)
- 无协议专属逻辑
- 返回与协议无关的数据结构
2. 适配器接口
位置: pkg/adapter/manager.go
type Adapter interface {
Start() error // Start the adapter (blocks)
Stop() error // Graceful shutdown
GetEngine() *engine.RouterEngine // Access to shared engine
}
设计决策: 接口最小化以保留灵活性;各适配器自管生命周期;接口中无协议专属方法;适配器在独立 goroutine 中运行。
3. Adapter Manager
位置: pkg/adapter/manager.go
职责: 解析适配器配置、按配置实例化、在独立 goroutine 中启动、协调优雅关闭。
主要方法:
func (m *Manager) CreateAdapters(cfg *config.RouterConfig, eng *engine.RouterEngine, configPath string) error
func (m *Manager) StartAll() error
func (m *Manager) StopAll() error
func (m *Manager) Wait()