- 后端
- API网关
- 模型推理服务
- AI Agent
【免费下载链接】semantic-router
An open, programmable decision layer for models and compute.
导读
GMTRouter 是 vLLM Semantic Router(本项目为 open, programmable decision layer for models and compute)在模型选择层提供的基于异构图表征学习的个性化路由算法。它通过构建"用户—LLM—查询—响应—回合"五类节点的异构交互图,从多轮用户交互中在线学习每个用户对候选模型的主观偏好,从而把查询路由到"最适合该用户"的模型,而不是所有用户共享一个全局最优模型。本文以 gmtrouter.md 教程为骨架,结合 gmtrouter.go、gmtrouter_test.go、train_gmtrouter.py 等实现,系统讲解其算法流、数学基础、Go 实现细节、完整 YAML 配置、可选预训练以及适用场景与最佳实践。读完本文你将能:理解异构交互图如何在多用户环境中刻画偏好、配置并启用 gmtrouter 决策算法、通过 Feedback 接口喂入交互数据实现在线个性化,并在冷启动与个性化之间做出正确的工程取舍。
参考论文:GMTRouter: Personalized LLM Router over Multi-turn User Interactions(Wang et al.,arXiv:2511.08590)。本项目实现受该论文基于图的个性化思路启发。论文报告的提升幅度为相比非个性化路由 accuracy +0.9% ~ +21.6%,AUC +0.006 ~ +0.309,这些数据属于论文实验结论,实际收益取决于你的用户量、交互数据规模与场景特征。
一、为什么需要个性化路由:从一个共享路由器说起
传统的模型选择方案(静态路由、轮询、随机)不考虑"用户是谁"。在多用户环境中,这会产生一个普遍问题:不同用户对同一个模型的感知质量可能截然相反——有人偏好详实推理的 gpt-4,有人喜欢低延迟的 gpt-3.5-turbo。共享一个全局排序无法同时满足两类用户。
GMTRouter 的解法是用一个异构交互图把"用户—查询—响应—模型"之间的多轮关系显式建模出来,再通过图神经网络(HGT)消息传递学习每个用户独立的模型偏好。其目标是:对用户 u 的查询 q,预测"该用户会更满意哪个模型 m"(偏好分数 s_{u,q,m}),并路由到分数最高的模型。
从路由器的整体架构看,模型选择(model selection)是决策层的一部分:查询先命中某个决策(decision),决策提供候选模型列表(modelRefs)与算法配置(algorithm.type),随后由选择器(Selector)决定最终模型。GMTRouter 是选择器工厂中注册的算法之一,相关方法常量为MethodGMTRouter = "gmtrouter"(见 selector.go)。
二、算法流程:从查询到路由的完整链路
教程给出的算法流程如下(虚线部分为核心学习组件):
对照 Go 实现的 Select 方法,可以还原真实执行细节:
- 定位用户:
selCtx.UserID为空时回退为"anonymous",说明即使不传用户 ID 算法也能工作(冷启动/匿名模式)。 - 查询嵌入:通过嵌入函数
embedQuery将查询文本编码为向量(实现中为embeddingFunc,由工厂注入)。 - 更新图:
addQueryNode(userID, queryEmbed)在图中追加查询节点(实现中由updateGraph完成:user→turn、llm→turn、query→turn 三类边)。 - 运行 HGT 层:
runHGTLayers(userID)做多跳消息传递(实现中为recomputeUserPreferences,含 5 个计算阶段,见下文源码剖析)。 - 交叉注意力打分:对每个候选模型计算
crossAttentionPredict(userEmbed, queryEmbed, modelEmbed),取最高分者。 - 路由并记录反馈:
UpdateFeedback把"胜者/败者"写回用户状态,形成持续学习闭环。
三、数学基础:异构图、HGT 卷积与交叉注意力预测头
3.1 异构图表征结构
图定义为四类节点(论文视角),本仓库 Go 实现进一步细化为五类节点(NodeType常量见 gmtrouter.go):
G = (V, E) where V = V_user ∪ V_llm ∪ V_query ∪ V_response| 节点类型 | 含义 | 仓库实现细节 |
|---|---|---|
| User | 个体用户及其交互历史 | 零初始化 embedding(make([]float32, EmbeddingDimension)) |
| LLM | 可用语言模型 | 用模型 Description 做 PLM 编码得到能力表征(InitializeFromConfig) |
| Query | 用户提交的查询 | 查询文本 PLM 编码 |
| Response | 模型输出与质量信号 | 响应文本 PLM 编码,用于查询-响应一致性打分(论文 G4) |
| Turn(虚拟节点) | 聚合单轮交互信息 | 零初始化 embedding +Features存偏好分数(record.Rating),连接顺序交互形成多轮链 |
边类型与含义:
- User-Query:用户提交了该查询
- Query-Response:查询收到了该响应
- Response-LLM:响应由该 LLM 生成
- Turn 边:连接会话内的顺序交互(
TurnIndex字段记录轮次)
仓库中的图节点/边数据结构为GraphNode与GraphEdge,均带 JSON 标签,可直接序列化持久化。
3.2 HGT 卷积层
HGT(Heterogeneous Graph Transformer)卷积配合层归一化,是图消息传递的核心:
h_v^(l+1) = LayerNorm(h_v^(l) + HGTConv(h_v^(l), {h_u^(l) : u ∈ N(v)})) HGTConv applies type-specific attention: Attention(v, u) = softmax_u(W_τ(v),τ(u) · h_v · h_u^T / √d)其中 τ(v) 表示节点 v 的类型。类型特定的注意力意味着不同类型边(用户→回合、LLM→回合、查询→回合)使用不同的变换矩阵 W,这正是"异构"的关键——模型可以分别学习"谁在用哪个模型""什么查询触发了交互""响应质量如何"三类信号。
仓库的 Go 在线学习实现不直接依赖 PyTorch Geometric,而是用轻量消息传递近似 HGT,见recomputeUserPreferences的 5 阶段设计:
- 聚合直接交互:对每个模型聚合
Rating × 时间衰减权重 × 反馈类型权重的消息; - 注意力加权聚合(简化 HGT attention):以消息方差倒数作为注意力权重(方差越高、置信越低、权重越小),做
aggScore = mean × attention; - LLM 嵌入质量信号(多跳):取 LLM 节点嵌入的 L2 范数作为质量分,贡献
0.1 × qualityBonus; - 邻居影响(协同过滤式消息传递):对偏好相似度 ≥ 0.3 的其他用户,聚合其对同一模型的历史偏好,贡献
0.1 × neighborBonus; - 响应嵌入贡献(论文 G4):计算查询-响应嵌入余弦相似度
coherence × Rating,贡献0.15 × avgRespScore。
最终分数 clamp 到 [0, 1],写入UserPreferenceState.ModelPreferences[model]。
3.3 交叉注意力预测头
最终的用户-模型偏好分数(论文视角):
s_{u,q,m} = f_pred(h_u^(L), h_q^(0), h_m^(L)) where: h_u^(L) = user embedding after L layers h_q^(0) = query embedding h_m^(L) = model embedding after L layers即用"经过 L 层图卷积的用户嵌入 + 原始查询嵌入 + 经过 L 层图卷积的模型嵌入"三者共同计算偏好。Go 实现的computePersonalizedScores采用加权融合近似:score = 0.3 × baseScore + 0.7 × learnedPreference(当用户对该模型有偏好数据时);无偏好数据时baseScore × 0.9(轻微惩罚)。基础分来自模型目录证据(exact-effort evidence)归一化,缺失证据时回退中性分 0.5。
四、核心算法源码剖析(Go)
教程给出了 Select 的核心骨架,仓库完整实现位于 gmtrouter.go。关键要点:
// Select using graph-based preference learning func (s *GMTRouterSelector) Select(ctx context.Context, selCtx *SelectionContext) (*SelectionResult, error) { userID := s.getUserID(selCtx) queryEmbed := s.embedQuery(selCtx.Query) // Update graph with new query node s.addQueryNode(userID, queryEmbed) // Run HGT convolution layers embeddings := s.runHGTLayers(userID) // Compute preference scores via cross-attention var bestModel string var bestScore float64 = -1 for _, candidate := range selCtx.CandidateModels { userEmbed := embeddings.User[userID] modelEmbed := embeddings.LLM[candidate.Model] score := s.crossAttentionPredict(userEmbed, queryEmbed, modelEmbed) if score > bestScore { bestScore = score bestModel = candidate.Model } } return &SelectionResult{ SelectedModel: bestModel, Score: bestScore, Method: MethodGMTRouter, }, nil }实际实现中的几个关键工程行为值得注意:
- 个性化闸门:仅当
EnablePersonalization && TotalInteractions >= MinInteractionsForPersonalization时才走个性化打分,否则回退到冷启动基础分,并在Reasoning中说明"还需要 N 次交互"——这与测试TestGMTRouterSelector_ColdStartSelection验证的行为一致。 - 置信度计算:
computeConfidence用"最高分与次高分差值"做 sigmoid 式映射,clamp 到 [0.1, 0.95],供下游做阈值判断。 - 反馈写入:
UpdateFeedback对胜者记Rating=1.0、败者记Rating=0.0(存在 LoserModel 且非平局时),并同步更新图与重算偏好;Feedback结构体(selector.go)支持Query、Response、WinnerModel、LoserModel、UserID、SessionID、Tie等字段。 - 时间衰减:
computeRecencyWeight使用半衰期 168 小时(1 周)的指数衰减,越久远的交互对偏好贡献越低。 - 反馈类型权重:
rating=1.0、ranking=0.9、response=0.7(隐式反馈权重最低),默认FeedbackTypes = ["rating", "ranking"]。 - 并发安全:节点、边、用户状态分别用
sync.RWMutex保护,支持多请求并发读、反馈并发写。
测试验证
gmtrouter_test.go 覆盖了关键行为,是理解语义的最佳入口:
TestGMTRouterSelector_ColdStartSelection:新用户无交互时按质量分选择(选更高分的 gpt-4);TestGMTRouterSelector_PersonalizedSelection:同一用户连续 5 次反馈偏好 gpt-3.5-turbo 后,选择结果翻转为 gpt-3.5-turbo——证明"学习到的偏好覆盖基础质量分";TestGMTRouterSelector_MultipleUsers:两个用户偏好相反,各自路由到各自偏好的模型——验证多用户隔离;TestGMTRouterSelector_GraphNodeCreation:一次反馈即创建 user/llm/query/turn 四类节点;TestGMTRouterSelector_Persistence:StoragePath写入 JSON 状态文件,新选择器实例可加载恢复;TestGMTRouterSelector_ResponseEmbedding/TestGMTRouterSelector_ResponseCoherence:验证嵌入函数被调用(query+response)以及查询-响应一致性参与偏好打分。
五、配置指南:启用 GMTRouter
5.1 完整 YAML 示例(教程原文)
decision: algorithm: type: gmtrouter gmtrouter: num_layers: 2 # HGT layer depth hidden_dim: 64 # Embedding dimension num_heads: 4 # Attention heads learn_preferences: true # Enable preference learning model_path: null # Optional pre-trained weights models: - name: gpt-4 backend: openai - name: gpt-3.5-turbo backend: openai - name: claude-3-opus backend: anthropic5.2 仓库实现的配置字段(更贴近当前版本)
GMTRouterConfig(gmtrouter.go)与GMTRouterSelectionConfig(selection_config.go)声明的字段如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
enable_personalization | true | 是否启用用户级偏好学习 |
history_sample_size | 5 | 采样的交互历史条数(论文中的 k),用于归纳式训练/推理 |
embedding_dimension | 768 | 节点嵌入维度(PLM 编码维度) |
num_gnn_layers | 2 | HGT 层数(论文中的 L,建议 1-5) |
attention_heads | 8 | HGT 注意力头数 |
min_interactions_for_personalization | 3 | 触发个性化所需的最小交互数(冷启动阈值) |
max_interactions_per_user | 100 | 每用户保留的最大交互记录数(超出丢弃最旧) |
feedback_types | ["rating","ranking"] | 支持的反馈类型:rating / ranking / response |
model_path | 空 | 预训练权重路径(可选) |
storage_path | 空 | 交互图持久化路径(JSON 状态文件) |
注意:教程示例中的num_layers / hidden_dim / num_heads / learn_preferences是论文风格命名;当前 Go 实现对应的实际 YAML 键为num_gnn_layers / embedding_dimension / attention_heads / enable_personalization。写配置时以当前版本的字段名为准。
5.3 关键参数配置建议
min_interactions_for_personalization:调低可更快进入个性化(如测试中设为 1-2),但数据不足时偏好噪声大;调高则更稳但个性化滞后。默认 3 是较合理的平衡点。embedding_dimension:应与你注入的嵌入函数输出维度一致(默认 768 对应通用 PLM 输出;MiniLM 为 384)。维度不匹配会导致向量运算异常,务必对齐。attention_heads:8 是仓库默认;头数越多表达能力越强、计算开销越大。max_interactions_per_user:限制单用户历史长度,控制内存与存储占用;调大有利于长会话偏好记忆。
5.4 关于算法注册与迁移的重要说明
gmtrouter在决策算法目录中属于已退役(retired)类型:retiredAlgorithmTypes明确列出"session_aware", "elo", "rl_driven", "gmtrouter", "bandit", "personalization", "thompson", "router_r1"(algorithm_registry.go),校验器会拒绝decision.algorithm.type=gmtrouter并提示迁移到global.router.learning.adaptation(validator_decision.go)。因此:如果你使用的是当前仓库主分支版本,学习型算法(含 gmtrouter)应配置在全局学习/自适应(adaptation)通道下,而不是旧版文档中的 per-decisionalgorithm.type=gmtrouter写法;版本化文档(v0.1)展示的写法对应早期版本行为,两者不可混用。选择器工厂仍保留MethodGMTRouter的创建路径(factory.go),供学习通道内部调用。
六、图结构与多轮交互建模
GMTRouter 的图捕获多轮交互:
User ←→ Query ←→ Response ←→ LLM ↑ ↑ └── Turn ───┘- User-Query:用户提交了该查询
- Query-Response:查询收到了该响应
- Response-LLM:响应由该 LLM 生成
- Turn 边:连接会话内的顺序交互(
TurnIndex记录轮次)
在 Go 实现中,一次UpdateFeedback会在图中新增查询节点与回合节点,并建立user→turn、llm→turn、query→turn三条边;用户节点为user:<uid>,LLM 节点为llm:<model>,查询/回合节点带时间戳后缀以保证唯一性。SessionID字段用于跨轮次组织上下文——多轮越多,turn 链越长,个性化信号越丰富。
持久化与容错
当配置storage_path时:
saveGMTRouterState(gmtrouter_storage.go)先写临时文件再原子重命名,避免写坏状态;Close()时自动保存。loadGMTRouterState对损坏的 JSON 会备份为.corrupted文件并返回错误(测试TestGMTRouterStateStorageBacksUpCorruptedFile验证),启动时自动normalizeUserPreferenceStates补齐缺失字段。- 生产建议:为
storage_path挂载持久卷,实现跨实例/重启的偏好恢复。
七、可选预训练:加速冷启动
教程给出的训练入口为:
cd src/training/rl_model_selection python train_gmtrouter.py --data_path ./data/interactions.json仓库中该脚本实际位于 src/training/model_selection/rl_model_selection/train_gmtrouter.py,并配有 GPU 启动脚本 train_gmtrouter_gpu.sh。脚本头部明确标注OPTIONAL:Go 在线实现本身不依赖预训练,只有当你希望"利用历史交互数据加速冷启动"或"为生产部署训练更复杂的模型"时才需要它。
训练脚本要点:
- 支持完整 HGT 建模:5 类节点(user/llm/query/response/turn)、HGT 消息传递、交叉注意力预测头、归纳式少样本训练框架(
use_inductive_training=True、train_split=0.8); - 文本编码使用预训练语言模型
sentence-transformers/all-MiniLM-L6-v2(384 维输出),冻结参数仅作编码器; - 依赖
torch_geometric(HGTConv)、transformers;缺失时自动降级为简化 GNN 或随机嵌入,保证脚本可运行; - 主要超参:
embedding_dim=384、hidden_dim=256、num_gnn_layers=2、num_attention_heads=8、learning_rate=1e-4、num_epochs=50、history_sample_size=5(论文中的 k)。
训练完成后在配置中引用权重:
gmtrouter: model_path: /models/gmtrouter_trained.pt八、何时使用 GMTRouter
适合的场景:
- 多用户环境且用户偏好差异显著(如企业不同角色、不同语言/风格偏好的终端用户);
- 系统已有或能够积累丰富的多轮交互历史(turn 链越长,个性化越准);
- 需要跨会话保持个人化体验(同一用户在不同会话中偏好连续)。
应谨慎或改用其他算法的场景:
- 用户数少:没有足够交互数据支撑个性化(
min_interactions_for_personalization长期不满足); - 无历史数据:冷启动阶段与普通质量路由无异,个性化收益尚未显现;
- 延迟敏感应用:GNN 消息传递与偏好重算会引入额外开销(文档估计 GNN 增加约 10ms,实际以你的数据规模与硬件为准;
MaxInteractionsPerUser、attention_heads、num_gnn_layers是控制开销的主要旋钮)。
九、最佳实践清单
- 先不预训练:多数场景下在线学习(
enable_personalization: true)足够,先跑起来再评估是否需要预训练; - 持续采集交互数据:通过
UpdateFeedback或/api/v1/feedback类接口持续喂入Query/Response/WinnerModel/LoserModel/UserID,更多轮次 = 更好的个性化; - 监控 per-user 指标:利用
GetDebugState(userID)(返回节点/边数、用户交互数、model_preferences 明细)验证个性化确实生效,而不是停留在冷启动选择; embedding_dimension用适中值:默认 768 是质量与速度的平衡点;如嵌入函数维度更低(如 384),显式下调以避免浪费;- 配置
storage_path并挂持久卷:让偏好跨重启保留,避免每次冷启动损失学习成果; - 区分版本配置语义:新版仓库学习算法走
global.router.learning.adaptation,勿照搬旧版 per-decisionalgorithm.type=gmtrouter(会被校验器拒绝); - 为匿名流量兜底:不传 UserID 时自动落入
anonymous用户,设计上要接受"匿名用户个性化能力受限"这一现实。
十、相关资源导航
- 教程总览:overview.md(8 种选择算法对比与快速配置)
- 算法选型指南:choosing-algorithm.md
- 核心实现:gmtrouter.go
- 状态持久化:gmtrouter_storage.go
- 测试用例:gmtrouter_test.go
- 选择器工厂:factory.go
- 预训练脚本:train_gmtrouter.py 与 train_gmtrouter_gpu.sh
- 配置结构体:selection_config.go
- 算法注册与迁移说明:algorithm_registry.go、validator_decision.go
- 后端
- API网关
- 模型推理服务
- AI Agent
【免费下载链接】semantic-router
An open, programmable decision layer for models and compute.
相关推荐
semantic-router 实验性 Router 模型训练实战:Router-R1 强化学习路由与 GMTRouter 图网络个性化路由
semantic router 实验性 Router 模型训练实战:Router R1 强化学习路由与 GMTRouter 图网络个性化路由 本文围绕 实验性
后端API网关模型推理服务AI Agentsemantic-router GMT Router:基于异构图学习的个性化模型路由选择算法实战指南
semantic router GMT Router:基于异构图学习的个性化模型路由选择算法实战指南 导读 本文围绕 semantic router(vLLM
后端API网关模型推理服务AI Agentsemantic-router MLP 模型选择算法完全指南:在 Go 路由内用神经网络实现非线性模型路由
semantic router MLP 模型选择算法完全指南:在 Go 路由内用神经网络实现非线性模型路由 导读 本文深入讲解 semantic router
后端API网关模型推理服务AI Agent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考