news 2026/10/13 22:29:02

Semantic Router 的 Elo 评分模型选择:基于在线反馈的动态路由算法指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Semantic Router 的 Elo 评分模型选择:基于在线反馈的动态路由算法指南
  • 后端
  • API网关
  • 模型推理服务
  • AI Agent

【免费下载链接】semantic-router

An open, programmable decision layer for models and compute.

项目地址:https://gitcode.com/gh_mirrors/sem/semantic-router
点击查看免费下载

本文围绕 vLLM Semantic Router 项目中的Elo 评分模型选择(Elo Rating Selection)展开,介绍一种基于运行时用户反馈、借助 Bradley-Terry 成对比较框架持续更新模型评分的在线学习式路由策略。读者将掌握该算法的数学原理、Go 核心实现、完整配置方式、反馈提交接口与持久化存储机制,并能在实际网关部署中通过elo算法让表现更好的模型在候选集中脱颖而出、被更频繁地选中。

Elo 评分选择的核心思想

传统的模型路由(如 RouteLLM 论文中训练的方式)是基于偏好数据训练出的静态路由器模型,一次训练、长期生效。而 Semantic Router 的 Elo 评分选择采用了完全不同的路线:一套运行时评分系统,根据实时用户反馈动态更新模型排名。

其基本逻辑是:

  • 模型收到正向反馈(thumbs up)时,评分增加;
  • 模型收到负向反馈(thumbs down)时,评分降低;
  • 随着时间推移,表现更好的模型评分攀升,被选中的概率也随之提高。

这套方法基于Bradley-Terry 模型(成对比较框架),通过在线学习持续改进模型选择质量——不需要离线训练,反馈即学习,评分即策略。该实现的核心位于 src/semantic-router/pkg/selection/elo.go,代码注释中明确标注其思想参考自 RouteLLM 论文(arXiv:2406.18665),但在工程形态上改为运行时动态更新。

算法工作流程

流程可以拆解为以下四步(与原文档一致):

  1. 所有模型以初始评分起步(默认1500);
  2. 用户在响应后提供反馈(赞 / 踩);
  3. 评分依据反馈与"期望结果"之间的差距进行调整;
  4. 高评分模型被更频繁地选中。

其中第 3 步"期望结果"是关键:评分更新不是简单地加分减分,而是同时考虑两个模型的相对实力——这正是 Bradley-Terry 模型发挥作用的地方。

数学基础:Bradley-Terry 期望得分与评分更新

期望得分(Expected Score)

模型 A 击败模型 B 的期望概率为:

E_A = 1 / (1 + 10^((R_B - R_A) / 400))

其中:

  • R_A= 模型 A 的评分
  • R_B= 模型 B 的评分
  • E_A= 期望得分(A 获胜的概率)

评分更新公式

收到反馈后,评分按如下公式更新:

R'_A = R_A + K × (S_A - E_A)

其中:

  • K= K 因子(控制波动幅度,默认 32)
  • S_A= 实际结果(1 = 胜,0.5 = 平,0 = 负)
  • E_A= 期望得分

计算示例

模型 A:评分 1500,模型 B:评分 1400 A 的期望得分:1 / (1 + 10^((1400-1500)/400)) = 0.64 若 A 获胜(S_A = 1): 新评分 = 1500 + 32 × (1 - 0.64) = 1500 + 11.5 = 1511.5 若 A 落败(S_A = 0): 新评分 = 1500 + 32 × (0 - 0.64) = 1500 - 20.5 = 1479.5

可以看到:爆冷获胜时加分多,正常获胜时加分少;同理,爆冷落败时扣分也多。这种不对称性正是 Elo 系统能快速识别真实实力差距的原因。

源码级实现:EloSelector 的 Select 与 UpdateFeedback

在 src/semantic-router/pkg/selection/elo.go 中,EloSelector是整套算法的核心类型,文档中的核心 Go 代码即来自该文件的实际实现:

// Select returns the model with highest Elo rating func (s *EloSelector) Select(ctx context.Context, selCtx *SelectionContext) (*SelectionResult, error) { var bestModel string var bestRating float64 = math.Inf(-1) for _, candidate := range selCtx.CandidateModels { rating := s.getRating(candidate.Model, selCtx.DecisionName) if rating > bestRating { bestRating = rating bestModel = candidate.Model } } return &SelectionResult{ SelectedModel: bestModel, Score: bestRating, Method: MethodElo, }, nil } // UpdateFeedback adjusts ratings based on user feedback func (s *EloSelector) UpdateFeedback(winner, loser string, tie bool) { ratingA := s.getRating(winner) ratingB := s.getRating(loser) // Calculate expected scores expectedA := 1.0 / (1.0 + math.Pow(10, (ratingB-ratingA)/400.0)) expectedB := 1.0 - expectedA // Actual scores var actualA, actualB float64 if tie { actualA, actualB = 0.5, 0.5 } else { actualA, actualB = 1.0, 0.0 } // Update ratings s.setRating(winner, ratingA + s.kFactor*(actualA-expectedA)) s.setRating(loser, ratingB + s.kFactor*(actualB-expectedB)) }

从当前仓库的完整源码看,实际实现比示例更丰富,包含若干值得注意的增强点:

1. 选择阶段(Select)

真实实现(elo.go第 300-400 行)在选出最高分模型之外,还做了这些工作:

  • 按候选模型逐个取分:getRatingsForCandidates按"分类专属评分优先、全局评分兜底、默认初始评分最后"的顺序取分(elo.go);
  • 概率化打分:选择得分采用 Bradley-Terry 概率标度P = 10^(R/400) / Σ 10^(R_i/400)计算,而不是直接使用原始 Elo 分值,便于与其他选择器统一比较(elo.go);
  • 成本调节:当配置了cost_scaling_factor且上下文携带CostWeight时,会对得分做归一化成本惩罚——更便宜的模型获得加分,从而实现质量与成本的平衡(elo.go);
  • 置信度计算:基于min_comparisons阈值用 sigmoid 函数计算置信度1 / (1 + e^(-k*(comparisons-threshold))),比较次数越多置信度越高(elo.go);
  • 结果说明:返回的SelectionResult包含Reasoning(如 "Selected based on Elo rating 1632.0 (win rate: 20/25)"),支持决策可解释性。

2. 反馈更新阶段(UpdateFeedback)

真实实现支持三种反馈模式(elo.go):

模式输入更新方式
成对比较同时设置WinnerModel与LoserModel标准 Elo 双向更新
正向单模型反馈仅设置WinnerModel评分增加K × 0.1(K 因子的 10% 奖励)
负向单模型反馈仅设置LoserModel评分减少K × 0.1(K 因子的 10% 惩罚)

单模型反馈模式(applyWinnerOnlyFeedbackLocked/applyLoserOnlyFeedbackLocked)专门服务于"只知道单个模型表现好坏、没有对照模型"的自动信号式反馈场景,例如基于信号检测的隐式反馈(elo.go)。反馈对象Feedback结构体定义于 selector.go,支持Query、WinnerModel、LoserModel、Tie、DecisionName、UserID、SessionID、FeedbackType、Confidence等字段,其中UserID可用于按用户个性化学习。

3. 分类加权(Category-Weighted)

EloConfig.CategoryWeighted默认开启(DefaultEloConfig中置为true)。开启后,EloSelector内部维护两套评分表:全局评分globalRatings与按决策分类区分的categoryRatings(decision name → model → rating),反馈会同时更新两套评分(elo.go)。这意味着同一模型在不同类型请求(如"代码生成"与"数学推理")下可以有各自独立的评分,路由决策更具针对性。InitializeFromConfig还可以把配置中已有的静态 ModelScores(0-1 区间)线性映射到 Elo 评分区间1000 + score × 1000(即 1000-2000),作为冷启动先验(elo.go)。

4. 选择器工厂注册

在 src/semantic-router/pkg/selection/factory.go 中,当配置method: elo时,工厂会调用NewEloSelector(f.cfg.Elo)创建选择器并注册到Registry(选择器接口Selector定义于 selector.go,统一暴露Select/Method/UpdateFeedback等接口)。

配置详解

原文档给出的配置结构如下,它对应真实配置中decision.algorithm部分的elo算法块:

decision: algorithm: type: elo elo: k_factor: 32 # How quickly ratings change (16-64 typical) initial_rating: 1500 # Starting rating for new models storage_path: /data/elo-ratings.json # Persist ratings auto_save_interval: 1m # Save frequency models: - name: gpt-4 backend: openai - name: gpt-3.5-turbo backend: openai - name: claude-3-opus backend: anthropic

对照 src/semantic-router/pkg/config/selection_config.go 中EloSelectionConfig的定义,完整的elo配置块支持以下全部参数:

参数默认值说明
initial_rating1500新模型的起始评分
k_factor32评分变化速率(16-64 为典型区间)
category_weightedtrue是否启用按分类(decision)区分的评分
decay_factor0.0对历史比较施加的时间衰减(0-1,0 表示不衰减)
min_comparisons5评分被认为是"稳定"所需的最小比较次数,也用于置信度计算
cost_scaling_factor0.0成本考虑因子(0 = 完全忽略成本)
storage_path空评分持久化文件路径(如/var/lib/vsr/elo_ratings.json),不设置则仅存内存
auto_save_interval30s自动保存频率(仅当设置storage_path时生效)

这些默认值在 elo.go 的DefaultEloConfig()中定义:InitialRating: 1500、KFactor: 32、CategoryWeighted: true、DecayFactor: 0、MinComparisons: 5、CostScalingFactor: 0。代码中对应的常量DefaultEloRating = 1500.0、EloKFactor = 32.0位于同一文件顶部(elo.go)。注意原文档中的auto_save_interval: 1m与代码默认 30s 都合法,实际以配置为准,resolveAutoSaveInterval会对非正数等非法值做防御性回退。

模型清单部分的name/backend声明用于确定候选模型池;若模型在 配置的 ModelParams 中带有Pricing.PromptPer1M单价信息,InitializeFromConfig还会将其作为成本表注入选择器,供成本调节使用。

提交反馈:驱动评分更新的入口

反馈通过 API 提交,原文档给出的接口示例如下:

# Positive feedback (model performed well) curl -X POST http://localhost:8080/api/v1/feedback \ -H "Content-Type: application/json" \ -d '{ "request_id": "req-123", "model": "gpt-4", "rating": 1 }' # Negative feedback (model performed poorly) curl -X POST http://localhost:8080/api/v1/feedback \ -H "Content-Type: application/json" \ -d '{ "request_id": "req-456", "model": "gpt-3.5-turbo", "rating": -1 }'

需要注意的是,文档中的反馈 API 是面向网关部署形态的示例入口;在库的源码层面,驱动评分更新的真正接口是EloSelector.UpdateFeedback(ctx, *Feedback)。对照 selector.go 中Feedback的字段设计,一个完整的成对比较反馈(更贴近底层语义)大致对应:

feedback := &Feedback{ Query: "translate this paragraph", WinnerModel: "gpt-4", LoserModel: "gpt-3.5-turbo", Tie: false, DecisionName: "translation", // 分类加权时指定决策上下文 } selector.UpdateFeedback(ctx, feedback)

正向/负向单模型反馈则只需设置WinnerModel或LoserModel其中之一,对应 API 中"rating": 1/"rating": -1的语义。

查看当前评分

curl http://localhost:8080/api/v1/ratings

响应示例:

{ "ratings": { "gpt-4": 1632, "gpt-3.5-turbo": 1485, "claude-3-opus": 1558 }, "last_updated": "2024-01-15T10:30:00Z" }

在库内部,EloSelector.GetLeaderboard(category)提供了等价的能力:按评分降序返回[]*ModelRating(可指定分类,空分类则返回全局榜单),每个ModelRating还携带Comparisons、Wins、Losses、Ties统计信息(elo.go)。持久化文件的 JSON 结构(StoredRatings)也包含version、last_updated、global_ratings与category_ratings字段(storage.go),与上述响应结构一致。

K 因子调优

K 因子决定单次反馈对评分的扰动幅度,是系统"学习速度"的总开关:

K-Factor行为特征适用场景
16缓慢、稳定的变化反馈一致的成熟系统
32均衡(默认值)大多数生产环境
64快速、剧烈变化快速实验、新部署期

从源码实现看,K 因子还影响单模型反馈的步长(K × 0.1),因此调整k_factor会同时缩放两种反馈模式的更新幅度。建议从默认值 32 开始,观察一段时间评分演化后再决定是否调整。

持久化存储

开启storage_path即可让评分跨重启持久化:

elo: storage_path: /data/elo-ratings.json auto_save_interval: 1m

原文档说明该文件具备原子写安全与备份轮转能力,这在 src/semantic-router/pkg/selection/storage.go 中有完整的实现佐证:

  • 原子写:saveToFile先写入elo-ratings.json.tmp临时文件,再通过os.Rename原子重命名替换正式文件,避免写一半导致损坏(storage.go);
  • 自动保存:StartAutoSave启动后台 goroutine,通过 ticker 周期性检查dirty标志(MarkDirty置位),仅在评分确实变化时才落盘;关闭时执行最后一次最终保存(storage.go);
  • 损坏备份:读取时若发现文件损坏,会把原文件备份为elo-ratings.json.corrupted后再报错,防止现场丢失(storage.go);
  • 数据清洗:sanitizeRatings会把异常评分钳制在 100-3000 区间、清除非负统计值,防御脏数据(storage.go);
  • 存储接口抽象:EloStorage接口(LoadRatings/SaveRatings/LoadAllRatings/SaveAllRatings/Close)支持文件与内存(测试用MemoryEloStorage)两种后端,便于替换(storage.go)。

启动时,NewEloSelector会通过loadFromStorage把文件中的全局评分与分类评分一并载入内存(elo.go)。

可观测性与测试验证

EloSelector通过 src/semantic-router/pkg/selection/metrics.go 中的 Prometheus 指标暴露演化过程,包括model_elo_rating(Gauge,当前评分)、model_feedback_total(Counter,反馈次数)以及选择相关的耗时/得分/置信度指标,便于监控"评分漂移"与模型支配情况。

仓库测试 src/semantic-router/pkg/selection/selector_elo_test.go 验证了算法的收敛行为:模拟 strong-model 分别击败 medium-model、weak-model,且 medium-model 击败 weak-model,共 10 轮(30 次成对反馈)后断言strong > medium > weak的评分次序成立,strong-model应累计 20 胜、weak-model应累计 20 负,且GetLeaderboard返回的榜单前三名顺序正确——这为"反馈越多,排名越可靠"提供了可执行的验证路径。相关生命周期与持久化行为还可参考 storage_test.go。

最佳实践

  1. 收集一致的反馈:确保反馈真实反映输出质量,避免噪声数据干扰评分;
  2. 从默认 K 因子起步:先观察行为再调整,不要一上来就用激进参数;
  3. 启用持久化:避免重启丢失已积累的评分(配置storage_path+auto_save_interval);
  4. 监控评分漂移:留意是否有模型被不公平地长期支配(可借助model_elo_rating等指标);
  5. 用先验引导冷启动:若已知模型的历史质量,可通过InitializeFromConfig的静态 ModelScores 映射或直接设置initial_rating初始化评分,让新模型从合理起点开始学习;
  6. 善用分类加权:请求类型差异明显的场景建议保持category_weighted: true,让每个决策维度拥有独立的评分体系;
  7. 配合成本因子:多模型成本差异较大时,可设置cost_scaling_factor让评分在质量与成本之间取得平衡。

总结

Elo 评分选择为 Semantic Router 提供了一条"反馈即学习、零训练成本"的在线路由路径:以 Bradley-Terry 成对比较为数学内核,以EloSelector为运行时载体,通过UpdateFeedback消费用户反馈并持续刷新全局与分类两套评分,最终在每次请求到来时把流量导向当前评分最高的模型。它区别于 RouteLLM 等静态预训练路由器的地方在于——路由策略本身随线上反馈实时演化。结合原子写持久化、自动保存、损坏备份、Prometheus 指标与收敛性测试,这套实现可以独立、安全地嵌入生产网关,作为多模型调度决策层的一个稳定算法选项。进一步阅读可关注 elo.go、selection_config.go 与 storage.go 三份核心文件。

  • 后端
  • API网关
  • 模型推理服务
  • AI Agent

【免费下载链接】semantic-router

An open, programmable decision layer for models and compute.

项目地址:https://gitcode.com/gh_mirrors/sem/semantic-router
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 22:28:41

高分一号遥感反演黑臭水体与水华:从数据预处理到专题图全流程

简介:本资源面向遥感与GIS方向的初学者及环境监测从业者,提供利用高分一号影像对重庆部分区域开展黑臭水体与水华反演的完整技术方案,涉及ArcGIS与ENVI两大工具,适合作为水体遥感提取的实操参考。压缩包内共1个docx文档&#xff0…

作者头像 李华
网站建设 2026/10/13 22:26:30

AI编程工具实战:选型、提示词与工作流嵌入,告别低效代码

你有没有过这种体验——AI编程工具每天都在用,但要么生成的东西没法直接跑,要么改来改去花费的时间比自己从头写还长,最后得出的结论是“AI编程不过如此”。我见过不少这样的团队,也在自己负责的几个项目里踩过类似的坑。说实话&a…

作者头像 李华
网站建设 2026/10/13 22:26:26

用Codex搭建自动化闭环:触发-执行-校验-反馈,让AI不乱改代码

最近这半年,我把日常能自动化的事情几乎都交给了 Codex 这个编程代理来跑:帮我看提交差异、补接口测试、整理周报数据、批量修文档格式、甚至把遗留脚本改造成模块化代码。刚开始是“要跑什么就临时开一个会话”,一天下来提示词写得又碎又长&…

作者头像 李华