- 后端
- API网关
- 模型推理服务
- AI Agent
【免费下载链接】semantic-router
An open, programmable decision layer for models and compute.
本文围绕 semantic-router 的 Elo Rating 模型选择机制展开:它如何用 Bradley-Terry 模型驱动的运行时评分系统,根据真实用户反馈持续调整各候选模型的权重并选出最优模型。读完本文后,你将理解该算法的数学原理与选路/反馈闭环、能够正确配置elo参数(含各字段默认值与源码依据),并掌握反馈提交、评分查看与持久化存储的完整操作与实现细节。
一、运行时评分体系:机制总览与流程
Elo Rating 选择机制使用一个**运行时评分系统(runtime rating system)**对模型进行排序:获得正向反馈的模型加分,获得负向反馈的模型扣分。随着时间推移,表现更好的模型评分升高、被选中得更频繁。该方法基于 Bradley-Terry 模型(成对比较框架),通过在线学习持续改进模型选择(详见 elo 教程)。
关于 RouteLLM 的说明:RouteLLM 论文(Ong et al.)训练的是静态路由模型——在偏好数据上离线训练后用于路由决策。semantic-router 的实现采取了不同路径:一个运行时 Elo 评分系统,依据线上实时反馈动态更新评分,而非依赖预训练的静态路由策略。
算法整体流程如下(继承自原文档):
工作流程可以概括为四步:
- 所有模型从初始评分开始(默认:1500);
- 用户在获得响应后提供反馈(点赞/点踩);
- 评分根据反馈与“预期结果”的偏差进行调整;
- 评分更高的模型被选中得更频繁。
在源码中,这一机制由 EloSelector 实现,它实现了统一的 Selector 接口(Select/Method/UpdateFeedback等方法),并由 工厂 Create() 在method: elo时实例化、在CreateAll()中随其余选择器一起注册到全局 Registry。
二、数学基础
2.1 期望得分(Bradley-Terry 模型)
模型 A 战胜模型 B 的期望概率:
E_A = 1 / (1 + 10^((R_B - R_A) / 400))其中:
R_A= 模型 A 的评分R_B= 模型 B 的评分E_A= 期望得分(A 获胜的概率)
2.2 评分更新
反馈之后,评分按下式更新:
R'_A = R_A + K × (S_A - E_A)其中:
K= K-factor(控制波动幅度,默认:32)S_A= 实际结果(1 = 胜,0.5 = 平,0 = 负)E_A= 期望得分
2.3 示例计算
Model A: Rating 1500, Model B: Rating 1400 Expected score for A: 1 / (1 + 10^((1400-1500)/400)) = 0.64 If A wins (S_A = 1): New rating = 1500 + 32 × (1 - 0.64) = 1500 + 11.5 = 1511.5 If A loses (S_A = 0): New rating = 1500 + 32 × (0 - 0.64) = 1500 - 20.5 = 1479.5可以看到:高评分模型赢下“被预期会赢”的对局时,只获得小幅加分(+11.5);而爆冷输掉时扣分更多(-20.5)——这正是 Bradley-Terry 框架“按意外程度修正”的体现。
三、核心算法与源码实现剖析
3.1 选择流程:Select
原文档给出的核心算法示意(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 }当前源码中的 Select 实现在此基础上更完整,实际执行逻辑为:
- 取候选评分:
getRatingsForCandidates优先取该决策(DecisionName)类别下的评分,取不到再回退到全局评分,都缺失则用initial_rating构造默认评分(源码 L622-L651); - Bradley-Terry 概率化得分:每个候选模型按标准 Elo 概率尺度
10^(rating/400)归一化为得分占比,总分异常为 0 时退化为均匀分布; - 成本调整(可选):当
cost_scaling_factor > 0且上下文带有CostWeight时,applyCostAdjustment会把成本归一化到 0–1,让更便宜的模型获得乘数加成(源码 L687-L723); - 置信度:
calculateConfidence用 Sigmoid 函数(陡度 k=0.2、阈值为min_comparisons)把“比较次数”映射为 0.5–1 之间的置信度——比较样本越少,结果越不可信(源码 L725-L738); - 返回
SelectionResult,其中Reasoning形如Selected based on Elo rating 1511.5 (win rate: 12/20),并对全部候选记录RecordEloRating指标。
3.2 反馈更新:UpdateFeedback 的三种模式
原文档给出的反馈更新示意(Go):
// 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)) }源码中的 UpdateFeedback 支持三种反馈模式,入口在 updateRatingLocked:
| 模式 | 输入条件 | 行为 |
|---|---|---|
| 成对比较 | WinnerModel与LoserModel都非空 | 标准 Elo 更新(applyPairwiseFeedbackLocked),双方期望得分由 Bradley-Terry 公式计算,Tie时双方按 0.5 计 |
| 正向自反馈 | 仅WinnerModel非空 | 该模型获得K × 0.1的小幅加分,计入 Wins(源码 L554-L565) |
| 负向自反馈 | 仅LoserModel非空 | 该模型被扣K × 0.1,计入 Losses(源码 L567-L578) |
成对模式的核心更新逻辑(applyPairwiseFeedbackLocked)与上文数学公式完全一致:
expectedWinner := 1.0 / (1.0 + math.Pow(10, (loserRating.Rating-winnerRating.Rating)/400.0)) expectedLoser := 1.0 - expectedWinner winnerRating.Rating += e.config.KFactor * (actualWinner - expectedWinner) loserRating.Rating += e.config.KFactor * (actualLoser - expectedLoser)同时每次更新都会累加Comparisons/Wins/Losses/Ties统计,并通过RecordFeedbackMetrics记录新旧评分的监控指标。更新完成后调用persistRatings()将存储标记为 dirty(见第七节)。
3.3 全局评分 + 分类别评分双层结构
一个容易忽视但很重要的设计:EloSelector内部维护两套评分(源码 L100-L106):
globalRatings:与类别无关的全局评分;categoryRatings:按决策名(decision name / 类别)维度的评分。
当category_weighted开启(默认开启)且反馈/选择携带DecisionName时,反馈会同时更新全局与该类别两份评分(updateCategoryRatings);选择时则优先使用类别评分、缺失再回退全局。这意味着同一模型在“代码”类别和“客服”类别下可以拥有不同的 Elo 表现,避免单一全能分掩盖领域差异。
冷启动播种:InitializeFromConfig会把配置中的类别级ModelScores(0–1 静态分)线性映射为 1000–2000 区间的初始 Elo(rating = 1000 + score × 1000,常量见 源码 L37-L41),对应 源码 L254-L297。这为“用已知先验质量做 bootstrap”提供了实现支撑(见第九节最佳实践第 5 条)。
3.4 反馈契约与校验
反馈输入的统一结构是 Feedback,关键字段包括:Query、WinnerModel、LoserModel、Tie、DecisionName、UserID、SessionID、ConversationID、FeedbackType(取值如satisfied、need_clarification、wrong_answer、want_different)、Confidence(0.0–1.0)。
进入选择器前,NormalizeFeedback / ValidateFeedback 会做契约校验:
winner_model与loser_model至少一个非空;- 两者都非空时不得是同一模型(禁止自我比较);
confidence必须在 0.0–1.0 之间且不能为 NaN/Inf。
四、配置
教程给出的基础配置示例(v0.2):
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结合源码,elo参数块的完整字段与默认值如下。参数定义见 EloConfig(选择器侧)与 EloSelectionConfig(配置解析侧),默认值来自 DefaultEloConfig:
| 参数 | YAML 键 | 默认值 | 说明 |
|---|---|---|---|
| 初始评分 | initial_rating | 1500 | 新模型的起始 Elo |
| K 因子 | k_factor | 32 | 控制每次比较的评分变化幅度,越大越敏感、越易波动 |
| 分类别评分 | category_weighted | true | 按决策类别维护独立评分,未命中时回退全局评分 |
| 时间衰减 | decay_factor | 0.0 | 对陈旧比较施加时间衰减(0–1,0 表示不衰减) |
| 稳定比较数 | min_comparisons | 5 | 达到该比较次数后评分才被认为“稳定”,同时是置信度 Sigmoid 的阈值 |
| 成本缩放 | cost_scaling_factor | 0.0 | 0 表示忽略成本;大于 0 时对更便宜模型加成 |
| 存储路径 | storage_path | 空(不持久化) | 设置后启动时加载、反馈后落盘,例如/var/lib/vsr/elo_ratings.json |
| 自动保存间隔 | auto_save_interval | 30s | 仅在设置storage_path时生效;配置非法值会回退 30 秒并告警 |
补充两点源码级事实:
auto_save_interval的解析由 resolveAutoSaveInterval 完成:任何缺失或非正值都会回退到 30 秒的defaultAutoSaveInterval并输出告警,确保坏配置既不会让time.NewTickerpanic,也不会静默禁用周期落盘;method取值elo时,工厂会创建EloSelector并调用InitializeFromConfig注入模型配置与类别配置(factory.go L170-L175)。
五、提交反馈
按教程文档,使用反馈 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 }'从源码结构看,无论 HTTP 层如何映射,最终都会落到选择器的UpdateFeedback入口,其输入契约即第三节 3.4 所述的Feedback结构。因此对接反馈时需要注意:
- 正向/负向单模型反馈对应“自反馈”模式,仅调整该模型
±K×0.1(K=32 时为 ±3.2 分),波动远小于成对比较,适合点赞/点踩这类只有单一模型上下文的场景; - 若同时给出
winner_model与loser_model,则走完整的成对 Elo 更新,并可附带tie: true表示平局; - 携带
decision_name时,会在category_weighted开启下同步更新该类别的评分; confidence字段用于隐式反馈(检测可能不确定)场景,取值必须落在 0.0–1.0。
六、查看当前评分
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" }在源码侧,评分的完整快照可以通过 GetLeaderboard 按评分降序获取(支持传类别名过滤),每个条目是 ModelRating:model / rating / comparisons / wins / losses / ties。落盘的 JSON 结构则定义在 StoredRatings:
type StoredRatings struct { Version int // 版本,便于未来格式迁移 LastUpdated time.Time // 最近更新时间 GlobalRatings map[string]*ModelRating // 全局评分 CategoryRatings map[string]map[string]*ModelRating // 分类别评分 }七、K-Factor 调优
| K-Factor | Behavior | Use Case |
|---|---|---|
| 16 | Slow, stable changes | 反馈稳定的成熟系统 |
| 32 | Balanced (default) | 大多数生产场景 |
| 64 | Fast, volatile changes | 快速实验、新部署 |
从源码看,k_factor唯一作用于两处:成对更新中的R' = R + K × (actual - expected),以及自反馈中的±K × 0.1。因此调大 K 会同时放大两类反馈的步长;调优时建议结合min_comparisons控制的置信度一起观察——样本不足时即使评分领先,Select返回的Confidence也会偏低(Sigmoid 尚未越过阈值)。
八、持久化存储
启用storage_path即可在重启之间保留评分:
elo: storage_path: /data/elo-ratings.json auto_save_interval: 1m教程说明该文件是原子写安全的并带有备份轮转。结合 storage.go 的实现,可以进一步确认这些保障的具体机制:
- 原子写:saveToFile 先把 JSON 写入
*.tmp临时文件,再os.Rename覆盖目标文件,避免写到一半崩溃导致文件损坏; - 损坏文件备份:若读取到的文件无法解析,loadFromFile 会先把原内容备份为
*.corrupted再报错,保留现场便于排查; - 数值消毒:sanitizeRatings 会把越界的评分钳制到 100–3000 区间、非负的胜负平计数归零;
- 脏标记 + 周期落盘:反馈更新只调用 MarkDirty 置脏,后台 goroutine(StartAutoSave)按
auto_save_interval周期检查脏标记并整体落盘;关闭时(Close)会再做一次最终保存,减少进程退出时的数据丢失窗口; - 启动加载:
NewEloSelector在配置了storage_path时先加载既有评分(_global键映射到全局、其余键映射到类别评分),失败仅告警不阻断启动(源码 L128-L156)。
此外,实现还预留了 EloStorage 接口(Load/Save/Close),当前提供文件与内存两种后端,测试可用MemoryEloStorage注入。
九、测试与行为验证
仓库为 Elo 选择器提供了可直接运行的行为测试 selector_elo_test.go:它模拟 10 轮三模型(strong / medium / weak)循环成对反馈,然后断言:
- 排名演化正确:
strong > medium > weak,且 strong 恰好 20 胜、weak 恰好 20 负; GetLeaderboard("")返回的全局榜顺序为strong-model → medium-model → weak-model。
func assertEloRankingEvolution(...) { if strongRating.Rating <= mediumRating.Rating { t.Errorf("strong (%f) should beat medium (%f)", ...) } // ... 验证榜单顺序 if leaderboard[0].Model != "strong-model" { ... } }这套测试同时覆盖了第 3.1 节所述的评分驱动选路与第 6 节所述榜单排序逻辑,是本地验证 Elo 行为是否符合预期的最轻量手段。另有 storage_test.go、feedback_test.go、auto_save_interval_test.go 等对持久化、反馈校验与自动保存间隔解析做了契约级测试。
十、最佳实践
- 收集一致的反馈:确保反馈真实反映响应质量,否则评分会被系统性带偏;
- 从默认 K-factor 开始:先使用
k_factor: 32观察一段时间,再依据实际行为调整; - 启用持久化:配置
storage_path,避免重启丢失已积累的评分; - 监控评分漂移:关注某个模型是否不公平地占据榜首(可结合
RecordEloRating指标与GetLeaderboard快照); - 用先验做冷启动:在有已知质量信息时,通过类别
ModelScores提供静态分(0–1 映射到 1000–2000 初始 Elo),让新系统不必从“全员 1500”的盲区起步。
参考文件索引
| 内容 | 路径 |
|---|---|
| Elo 教程(本文主体文档) | website/versioned_docs/version-v0.2/tutorials/intelligent-route/model-selection/elo.md |
| Elo 选择器核心实现 | src/semantic-router/pkg/selection/elo.go |
| 反馈结构与 Selector 接口 | src/semantic-router/pkg/selection/selector.go |
| 反馈契约校验 | src/semantic-router/pkg/selection/feedback.go |
| 文件/内存存储后端 | src/semantic-router/pkg/selection/storage.go |
| 自动保存间隔解析 | src/semantic-router/pkg/selection/auto_save_interval.go |
| 选择器工厂与注册 | src/semantic-router/pkg/selection/factory.go |
| 配置参数解析定义 | src/semantic-router/pkg/config/selection_config.go |
| Elo 行为测试 | src/semantic-router/pkg/selection/selector_elo_test.go |
- 后端
- API网关
- 模型推理服务
- AI Agent
【免费下载链接】semantic-router
An open, programmable decision layer for models and compute.
相关推荐
semantic-router 的 Elo 选择算法:基于反馈评分驱动的模型路由实践指南
semantic router 的 Elo 选择算法:基于反馈评分驱动的模型路由实践指南 导读 本文围绕 semantic router 提供的 elo 模型选
后端API网关模型推理服务AI AgentSemantic Router Adaptation 在线模型选择学习机制详解:配置、评分算法与可观测性
Semantic Router Adaptation 在线模型选择学习机制详解:配置、评分算法与可观测性 导读 Adaptation(自适应)是 semanti
后端API网关模型推理服务AI Agentsemantic-router GMT Router:基于异构图学习的个性化模型路由选择算法实战指南
semantic router GMT Router:基于异构图学习的个性化模型路由选择算法实战指南 导读 本文围绕 semantic router(vLLM
后端API网关模型推理服务AI Agent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考