ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Semantic Router 中的 Thompson Sampling 模型选择:贝叶斯探索-利用平衡的实战指南

Semantic Router 中的 Thompson Sampling 模型选择:贝叶斯探索-利用平衡的实战指南
  • 后端
  • API网关
  • 模型推理服务
  • AI Agent

【免费下载链接】semantic-router

An open, programmable decision layer for models and compute.

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

导读

Thompson Sampling 是 vLLM Semantic Router 提供的多臂老虎机(Multi-Armed Bandit)式模型选择算法,用于在多个 LLM 候选后端之间自动做出路由决策。它以贝叶斯方式自然平衡"尝试新模型(探索)"与"复用已验证的好模型(利用)"这对矛盾,并在用户反馈驱动下持续更新每个模型的胜率后验。读完本文,你将掌握该算法的数学原理、Go 语言核心实现、配置文件写法、关键参数调优方法,以及如何通过反馈 API 让路由器逐步学会个性化路由。

1. Thompson Sampling 在模型选择中的定位

在 Semantic Router 的模型选择体系中,算法被划分为两个阵营:核心算法(Static、Elo、RouterDC、AutoMix、Hybrid)与 RL 驱动算法(Thompson Sampling、GMTRouter、Router-R1)。其中 Thompson Sampling 属于后者,选择算法总览 将其定位为"面向探索/利用权衡的贝叶斯自适应学习"方案,适用于已经积累了一定反馈数据、希望路由器既能持续学习又能稳定利用已知优势模型的场景。

与 ELO(基于评分对比)和 RouterDC(基于查询-模型相似度)不同,Thompson Sampling 的核心思想是:为每个候选模型维护一个描述其真实胜率不确定性的 Beta 分布,每次请求时从各分布的"后验采样"中选出采样值最高的模型。采样的随机性天然带来了探索:当某模型观测数据少、分布宽(方差大)时,它更容易被"碰运气"选中;而当数据充分、分布收窄后,模型被选中的概率主要由其真实胜率决定。这种"概率匹配"(Probability Matching)机制在理论上保证了遗憾(Regret)的上界可控。

2. 算法流程:从用户请求到路由闭环

文档给出的算法流程可概括为一次请求的完整闭环:

四个关键步骤:

  1. 每个模型维护一个代表其成功概率的 Beta 分布;
  2. 对每次请求,从每个模型的分布中采样一个值;
  3. 选择采样值最高的模型作为路由目标;
  4. 根据用户反馈(成功/失败)更新该模型的分布参数。

在 Semantic Router 的 RL 驱动选择器实现中,这一流程被扩展为更细粒度的"全局/用户/类别/会话"多级偏好结构,并在采样之外叠加了成本、会话上下文等修正项(详见第 5 节),但"采样-取最大-反馈更新"的骨架与文档完全一致。

3. 数学基础:Beta 分布、后验采样与贝叶斯更新

3.1 Beta 分布

每个模型维护一个 Beta 分布,用来表示其成功概率 θ 的不确定性:

P(θ | α, β) = Beta(α, β) where: α = prior_alpha + successes β = prior_beta + failures θ = true success probability (unknown)

这里 α 与 β 可直观理解为"成功次数 + 先验成功数"与"失败次数 + 先验失败数"。先验(prior_alpha, prior_beta)决定了模型在没有任何观测数据时的初始行为。

3.2 采样过程

每次选择时,从每个模型的后验分布中独立采样:

θ_i ~ Beta(α_i, β_i) for each model i Select model: argmax_i(θ_i)

值得注意的是,选择依据是采样值而非均值——这正是探索的来源。均值相同但方差不同的两个模型,在采样时会因方差不同而获得不同的被选概率:方差大(不确定性高)的模型更容易偶尔采样出极端高分而被选中。

3.3 贝叶斯更新

收到反馈后,仅更新被选模型的分布:

If success: α' = α + 1 If failure: β' = β + 1

由于 Beta 分布与伯努利似然共轭,这个更新保持了后验仍为 Beta 分布的形式,无需复杂的数值积分即可在线迭代。

3.4 期望与方差

E[θ] = α / (α + β) # Expected success rate Var[θ] = αβ / ((α+β)²(α+β+1)) # Uncertainty
  • 高方差→ 更多探索(模型表现不确定,需要更多数据);
  • 低方差→ 更多利用(对模型表现有把握,倾向稳定选择)。

这一公式直接对应源码中的实现:BetaDistribution 的Mean()与Variance()方法,且单元测试 rl_driven_test.go 通过 10000 次采样的均值近似验证了Sample()的统计性质(均匀先验 (1,1) 均值约为 0.5,强偏好 (10,1) 均值约为 10/11)。

4. 核心算法(Go 实现)与源码对应

文档给出了ThompsonSelector的伪代码式实现。在 Semantic Router 仓库中,Thompson Sampling 的实际实现位于 RL 驱动的RLDrivenSelector中(当UseThompsonSampling为 true 时启用),其选择与反馈更新逻辑与文档高度吻合:

// Select 使用 Thompson Sampling 选择模型 func (r *RLDrivenSelector) Select(ctx context.Context, selCtx *SelectionContext) (*SelectionResult, error) { // ... 从偏好中读取每个候选的 Beta 分布 for _, pref := range preferences { sample := pref.Distribution.Sample() // 可选的成本修正:偏好更便宜的模型 if r.config.CostAwareness && r.config.CostWeight > 0 { sample = r.applyCostBonus(pref.Model, sample) } // 可选的会话上下文修正 if selCtx.SessionID != "" && r.config.SessionContextWeight > 0 { sample = r.applySessionContext(selCtx.SessionID, pref.Model, sample) } sampledValues[pref.Model] = sample } // 选择采样值最高的模型 // ... }

对应实现位于 rl_driven.go,关键子实现包括:

  • BetaDistribution.Sample():通过 Gamma 分布比值法X ~ Beta(a,b) = Gamma(a,1) / (Gamma(a,1) + Gamma(b,1))生成采样,底层采用 Marsaglia-Tsang 算法(gammaVariate),并对 α≤0、β≤0 的边界情况做了兜底(重置为 1.0);
  • UpdateFeedback():按加权方式更新赢家(α 增加)与输家(β 增加)的分布,支持平局(Tie 时 α、β 各加 0.5);
  • 偏好混合(blendPreferences):实现见 rl_driven.go,将全局偏好与用户偏好按PersonalizationBlend加权混合,再用 0.3 比例混入类别偏好。

此外,RLDrivenSelector还提供了SelectMultiRound(基于采样分数选出多个模型并行查询并聚合,对应 Router-R1 的多轮路由)与GetLeaderboard/GetUserLeaderboard(按分布均值排序输出模型排行榜)等扩展能力,均可在 rl_driven.go 中查阅。

在 extproc 层的"路由学习自适应"(Router Learning)中还有一处独立的 Thompson 风格采样应用:router_learning_adaptation.go 的sampleBeta使用同样的 Gamma 比值法从经验质量先验中采样,用于在 base 选择器结果之上决定是否切换到其他候选模型,并受到切换边距(routingSamplingMargin)与成本惩罚(costPenalty)约束。

5. 配置:启用 Thompson Sampling 与候选模型

5.1 文档配置示例

在决策(decision)的 algorithm 块中配置type: thompson,并设置贝叶斯先验与学习参数:

decision: algorithm: type: thompson thompson: prior_alpha: 1.0 # Prior successes (optimistic: higher) prior_beta: 1.0 # Prior failures (pessimistic: higher) per_user: true # Per-user personalization decay_factor: 0.1 # Decay old observations min_samples: 10 # Minimum samples before exploitation models: - name: gpt-4 backend: openai - name: gpt-3.5-turbo backend: openai - name: claude-3-opus backend: anthropic

5.2 仓库中的对应配置结构

在当前仓库中,Thompson Sampling 的配置承载在 RL 驱动的配置结构RLDrivenSelectionConfig(selection_config.go 中对应 selection_config.go)中,并以RLDrivenConfig(rl_driven.go)的形式被选择器解析。仓库版本的关键字段如下(默认值来自DefaultRLDrivenConfig(),见 rl_driven.go):

decision: algorithm: type: rl_driven rl_driven: use_thompson_sampling: true # 使用 Thompson Sampling(默认 true) exploration_rate: 0.3 # ε-greedy 模式的初始探索率(默认 0.3) exploration_decay: 0.99 # 每 100 次选择衰减 1%(默认 0.99) min_exploration: 0.05 # 最低探索率(默认 0.05) enable_personalization: true # 按用户维护独立偏好(默认 true) personalization_blend: 0.7 # 用户偏好与全局偏好混合比例(默认 0.7) session_context_weight: 0.3 # 会话内反馈权重(默认 0.3) implicit_feedback_weight: 0.5 # 隐式反馈权重(默认 0.5) cost_awareness: true # 探索时偏好更便宜模型(默认 true) cost_weight: 0.2 # 成本影响强度(默认 0.2) storage_path: /var/lib/vsr/rl_state.json # 状态持久化路径(可选) auto_save_interval: 30s # 自动保存间隔(默认 30s)

需要注意两点:

  • 当UseThompsonSampling为 false 时,选择器退化为ε-greedy策略:以exploration_rate概率随机选择(成本感知模式下偏向低价模型),否则选择均值最高的模型;
  • 仓库的决策级学习配置结构由 router_learning_adaptation.go 的buildModelSelectionConfig组装,测试用例 router_selection_config_test.go 展示了完整的rl_driven决策配置写法(含storage_path: /var/lib/vsr/rl_state.json、auto_save_interval: 45s等字段)。

5.3 关键参数表

参数默认值说明
prior_alpha1.0先验成功数;越高越乐观
prior_beta1.0先验失败数;越高越悲观
per_user/enable_personalizationfalse / true是否按用户维护独立分布
decay_factor0.0旧观测衰减率(0 = 不衰减)
min_samples10进入完全利用前的最小样本数
exploration_rate0.3ε-greedy 分支的探索率
exploration_decay0.99探索率随选择次数衰减
personalization_blend0.7用户偏好与全局偏好的混合比例
cost_weight0.2成本感知探索的强度

6. 先验设置(Prior Settings)与初始行为

先验 (alpha, beta) 直接塑造模型的初始行为:

设置行为
(1, 1)均匀先验——各模型平等探索
(2, 1)乐观——假设模型表现良好
(1, 2)悲观——假设模型需要证明自己
(10, 10)强置信先验——改变缓慢

在仓库实现中,初始化逻辑将模型配置里的静态评分转换为 Beta 先验参数:alpha = 1.0 + Score*10.0、beta = 1.0 + (1.0-Score)*10.0(见 rl_driven.go 的InitializeFromConfig),即评分越高,初始 α 越大,与文档"先验越高越乐观"的语义一致。同时,InitializeFromConfig会为所有候选模型以 (1,1) 均匀先验初始化全局偏好,并把模型定价(Pricing.PromptPer1M)注册为成本信号供成本感知探索使用。

7. 按用户个性化(Per-User Personalization)

开启per_user: true(仓库中为enable_personalization: true)后,系统为每个用户维护独立的模型分布:

thompson: per_user: true

这允许系统学习"用户 A 偏好 GPT-4,而用户 B 偏好 Claude"这样的个体差异。仓库实现通过四级偏好结构实现这一目标(rl_driven.go):

  • 全局偏好(globalPreferences):所有用户共享的基线;
  • 用户偏好(userPreferences):user_id -> model -> preference的映射,每个用户独立;
  • 类别偏好(categoryPreferences):按决策/类别区分的偏好;
  • 会话上下文(sessionContext):记录会话内各模型的选中次数与成功次数。

实际选择时,通过getPreference将全局与用户偏好按personalization_blend加权混合(rl_driven.go),因此即使用户新用户没有历史数据,也能退回到全局偏好;而老用户则越来越依赖个人偏好。仓库还提供了GetUserLeaderboard(userID)(rl_driven.go)查看每个用户的模型偏好排名。

8. 反馈集成:驱动模型学习的闭环

Thompson Sampling 通过反馈 API 驱动分布更新。文档给出的方式是直接提交评分:

# Positive feedback (success) curl -X POST http://localhost:8080/api/v1/feedback \ -d '{"request_id": "req-123", "model": "gpt-4", "rating": 1}' # Negative feedback (failure) curl -X POST http://localhost:8080/api/v1/feedback \ -d '{"request_id": "req-456", "model": "gpt-4", "rating": -1}'

在仓库的 Feedback 结构 中,反馈被建模为"赢家-输家"对比:WinnerModel与LoserModel分别对应被偏好与未被偏好的模型;Tie表示两者打平;UserID/SessionID分别用于用户个性化与会话上下文学习;FeedbackType表示隐式反馈类型(取值如satisfied、wrong_answer、need_clarification、want_different),Confidence用于对不确定的自动检测信号降权。

UpdateFeedback的更新规则(rl_driven.go):

  • 赢家:Alpha += weight(成功);输家:Beta += weight(失败);
  • 平局:两者Alpha += 0.5*weight、Beta += 0.5*weight;
  • 隐式反馈按implicit_feedback_weight(默认 0.5)降权,进一步乘以其Confidence;
  • 全局、用户、类别、会话四级偏好会同时被更新。

9. 最佳实践与调优建议

文档给出四条核心最佳实践,结合仓库实现可进一步细化:

  1. 从均匀先验开始:默认 (1, 1),除非你对模型质量有先验知识;若想快速淘汰表现差的模型,可适当提高prior_beta;
  2. 开启 per-user 实现个性化:per_user: true让不同用户各自收敛到自己的偏好,但冷启动阶段建议保留全局偏好混合(personalization_blend不要设为 1.0);
  3. 非平稳环境下使用衰减:当模型质量随时间变化(例如频繁上线新模型或压测环境),应调大decay_factor让旧观测逐渐失效,仓库中exploration_decay参数同样用于控制探索率随选择次数的衰减;
  4. 合理设置 min_samples:min_samples过低会导致过早利用(模型尚未被充分验证),过高则探索时间过长;建议结合请求量按小时级数据量估算。

9.1 额外工程建议

  • 持久化状态:配置storage_path将全局/用户/类别偏好保存到 JSON 文件,并通过auto_save_interval控制落盘频率,避免重启后学习成果丢失(见 rl_driven.go 的存储初始化);
  • 成本感知:保持cost_awareness: true,让探索阶段优先尝试更便宜的模型,降低试错成本(applyCostBonus将成本归一化后对采样值加乘数,见 rl_driven.go);
  • 会话级平滑:session_context_weight让会话内最近的成败记录影响路由,适合多轮对话场景。

10. 验证与测试:如何确认算法生效

仓库为 Thompson Sampling 提供了完整的测试覆盖,可作为验证算法行为的参考:

  • 分布统计测试rl_driven_test.go:对 (1,1)、(10,1)、(1,10)、(5,5) 四种分布各采样 10000 次,验证均值与理论值偏差在 0.1 以内;
  • 并发安全测试:TestBetaDistribution_ConcurrentSample验证并发采样下分布状态的一致性;
  • 反馈更新测试:验证赢家 α 增加、输家 β 增加的贝叶斯更新逻辑;
  • 端到端测试core_selection.go:testThompsonSamplingSelection通过真实/v1/chat/completions请求验证"探索-利用"行为,期望早期请求中出现模型选择的多样性(探索),随后向最优模型收敛。

11. 参考与延伸

  • 本文档的姊妹篇:选择算法总览、如何选择算法、Router-R1(LLM 路由);
  • Thompson Sampling 的理论基础参见 Russo 等人的A Tutorial on Thompson Sampling(arXiv:1707.02038),本文档将其作为贝叶斯探索-利用方法的核心参考文献;
  • 源码入口:RL 驱动选择器、选择配置结构、路由学习自适应(extproc 层采样)。

小结:Thompson Sampling 为多模型路由提供了一个优雅的贝叶斯答案——用采样概率匹配真实胜率,用贝叶斯更新消化反馈,用先验与混合机制控制个性化与冷启动。结合 Semantic Router 的配置结构、持久化与成本感知扩展,你可以用极少的工程投入,把一个静态路由升级为会持续学习、自动探索、随用户反馈进化的自适应路由决策层。

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

【免费下载链接】semantic-router

An open, programmable decision layer for models and compute.

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

相关推荐

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进