- 后端
- API网关
- 模型推理服务
- AI Agent
【免费下载链接】semantic-router
An open, programmable decision layer for models and compute.
导读
preference是 Semantic Router(vSR)中的一类学习型信号(learned signal):它从一组人工编写的示例(examples)与分类器设置中推断出用户的回答风格偏好(如“想要简洁回答”或“想要详尽推演”),并把推断结果作为具名路由输入暴露给决策引擎。本文以 v0.3 Preference Signal 文档 为骨架,完整讲解它的配置方式、use_contrastive默认行为、prototype_scoring参数矩阵,并结合 preference_classifier.go、contrastive_preference_classifier.go、prototype_bank.go 等源码揭示对比学习匹配的完整链路。读完本文,你将能够:在 v0.3 配置中声明偏好信号、用示例锚定风格类别、理解对比模式与外部 LLM 模式的取舍,并把偏好信号接入路由决策条件。
什么是 Preference Signal
解决的问题:同一话题,不同风格
即使用户询问的是相同主题,不同人也往往想要截然不同的回答方式:有人希望“一句话讲完”,有人要求“逐步推演、不要跳过任何步骤”。如果这种风格偏好只在生成链路的下游处理(例如由模型提示词临时体现),那么路由层就无法据此选择更合适的模型或插件栈——因为路由发生在模型调用之前。
preference信号解决的就是这个问题:把推断出的风格偏好作为命名路由输入(named routing input)暴露出来,让决策引擎在模型选择(model choice)与插件选择(plugin choice)两个层面都看到“这位用户想要什么风格”的事实。
关键优势
- 不硬编码用户状态:偏好检测结果独立于决策产出,无需在路由逻辑中手工维护“用户 A 喜欢简洁”之类的状态机;
- 检测与路由解耦:偏好检测只负责产出信号,不关心最终路由结果,二者可以独立演进;
- 示例驱动的风格检测:支持“简洁 vs 详尽”这类语义风格判断,而不是简单的关键词匹配;
- 策略复用:一条偏好策略可以被多个决策(decision)同时引用,一处定义、多处生效。
从源码结构看,这一设计体现在 classifier_signal_preference_support.go 中:信号计算函数只负责产出preference:<名称>这一命名信号及其置信度/边界值,写入SignalResults后,路由决策层再按规则消费(见recordPreferenceSignalValues)。
何时使用
- 一部分用户偏好极简回答,另一部分用户需要高细节输出;
- 路由行为需要适配稳定的风格偏好(而非单次请求的临时倾向);
- 希望偏好检测在多个决策之间保持可复用;
- 风格信号需要同时影响模型选择与插件选择。
配置总览
1. 在 routing.signals.preferences 声明偏好规则
源片段族位于 config/fragments/signal/preference/,仓库自带的可复用示例 power-user.yaml 内容如下:
routing: signals: preferences: - name: terse_answers description: Users who prefer short, direct responses. examples: - keep it concise - bullet points only - answer in one paragraph threshold: 0.7字段说明(对应 signal_config.go 中的PreferenceRule结构):
| 字段 | 类型 | 说明 |
|---|---|---|
name | string | 规则名称,也是信号名称,路由条件里用type: preference, name: <name>引用 |
description | string | 规则语义描述;在对比模式下会连同 examples 一起参与嵌入 |
examples | []string | 训练锚点(training anchors),非字面关键词规则 |
threshold | float32 | 该规则的相似度阈值;匹配得分低于阈值时判定为不匹配 |
⚠️重要语义:把examples视作偏好检测器的训练锚点,而不是字面关键词规则。例如keep it concise这类例子在嵌入空间里“语义靠近”的请求(如 “Please keep your reply short and to the point”)才会命中,而不是要求用户请求里出现这几个单词。
threshold的默认值语义可参考源码:在 contrastive_preference_classifier.go 中,若threshold > 0且BestScore < threshold,分类器返回ErrPreferenceBelowThreshold(“preference below threshold”),信号视为不匹配;当threshold未设置(0)时,该检查被跳过。在 e2e/profiles/preference-routing/values.yaml 的实战配置中,terse_answers与detailed_answers都使用了threshold: 0.6,并用margin_threshold: 0.05追加一道“歧义否决”门槛。
2. 可选:在 global.model_catalog 覆盖偏好分类器行为
global: model_catalog: modules: classifier: preference: use_contrastive: false # optional override; default is true prototype_scoring: enabled: true cluster_similarity_threshold: 0.9 max_prototypes: 8 best_weight: 0.75 top_m: 2 margin_threshold: 0.05对应源码类型为 model_config_types.go 中的PreferenceModelConfig(UseContrastive、EmbeddingModel、PrototypeScoring)以及 prototype_scoring_config.go 中的PrototypeScoringConfig。
3. use_contrastive 默认值:v0.3 的新行为
文档明确说明:如果global.model_catalog.modules.classifier.preference.use_contrastive被省略,vSR 默认将其视为true。这意味着一个像 config/recipes/balance/config.yaml 这样的 profile,只要声明了偏好信号,就无需额外追加全局 classifier 块——除非你想显式禁用对比模式。
源码层面的执行契约选择逻辑在 preference_execution.go 中:
PreferenceUsesPrototypes():规则带 examples、显式指定了embedding_model、或显式use_contrastive: true时返回 true(走向量原型对比路径);PreferenceUsesDecisionTask():description-only 规则默认走原生 decision-task 判定;显式外部分类器保留自己的执行契约;- 优先级关系见 preference_execution_test.go 的测试矩阵:
"explicit prototypes override external"、"native binding overrides external"等用例保证了多种配置组合下的契约选择一致性。
初始化链路在 classifier_preference_lifecycle.go:先判断是否走 decision task,否则读取PreferenceModel.WithDefaults(),把UseContrastive指向PreferenceUsesPrototypes()的判定结果,再调用NewPreferenceClassifierWithProvider实例化对应实现(对比模式默认嵌入模型为mmbert,可远程嵌入后端)。
对比模式(Contrastive)工作原理
在对比模式下,路由器的处理流程是:嵌入 → 原型压缩 → 相似度打分 → 阈值/边界裁决。
第一步:预加载规则嵌入
contrastive_preference_embeddings.go 中的preloadRuleEmbeddings会收集每条规则的 description + 全部 examples(见 contrastive_preference_scoring.go 的collectExamples),然后并发地(worker 数为runtime.NumCPU() * 2)把它们嵌入为向量,日志输出[Preference Contrastive] preloaded N/M example embeddings using model=...。
第二步:原型库(prototype bank)压缩
嵌入完成后调用rebuildRuleBanks(),对每条规则构建一个prototypeBank。构建逻辑在 prototype_bank.go:
- 对嵌入后的例子做去重(
dedupePrototypeExamples); - 计算例子之间的余弦相似度矩阵(prototype_clustering.go);
- 以
cluster_similarity_threshold(默认 0.9)为阈值做贪心聚类,把语义冗余的例子合并; - 每个簇选出medoid(最代表该簇的例子)作为原型,并按
ClusterSize、AvgSimilarity降序、文本序兜底排序; - 若原型数超过
max_prototypes(默认 8),截断保留前 N 个。
若prototype_scoring.enabled: false,则跳过聚类压缩,把每个例子原样作为原型(uncompressedPrototypeRepresentatives)。
第三步:打分公式
查询请求被嵌入后,与每条规则的每个原型计算余弦相似度,打分逻辑在 prototype_scoring.go:
score = best_weight × best + (1 - best_weight) × supportbest:与规则原型库的最高相似度;support:Top-M 个最高相似度的平均值(top_m默认 2);best_weight:默认 0.75,权重越大越依赖“最强单原型”,越小越依赖“整体支持度”(取值会被夹在 [0, 1])。
最终规则得分即PreferenceRuleScore.Score,排序取最高者为BestRule,次高者为RunnerUp,Margin = BestScore - RunnerUpScore(contrastive_preference_scoring.go)。
第四步:阈值与 margin 双重裁决
contrastive_preference_classifier.go 的Classify依次检查:
- 最佳规则得分低于其
threshold→ 拒绝(ErrPreferenceBelowThreshold); margin_threshold > 0且Margin < margin_threshold→ 拒绝“勉强获胜”的模糊结果。
文档强调的margin_threshold作用正在于此:拒绝模糊赢家,而不是硬凑一个弱匹配。在 e2e/profiles/preference-routing/values.yaml 中注释给出了实战理由:“Off by default; set explicitly so an ambiguous query that narrowly favors one rule over the other still counts as 'no match' rather than a weak, coincidental win.”
信号产出与观测
命中后,classifier_signal_preference_support.go 会把结果写进SignalResults:
preference:<name>:匹配规则名(同时进入MatchedPreferenceRules);SignalValues["preference:<name>"]:置信度;SignalValues["preference:<name>:margin"]:胜出边界;- 以及每条规则的
:best、:support、:prototype_count明细,便于调试与观测。
信号提取延迟与置信度会写入results.Metrics.Preference,供监控与调参使用。
路由决策中的使用方式
偏好信号在routing.decisions[].rules.conditions中以type: preference引用。e2e 配置 e2e/profiles/preference-routing/values.yaml 给出了两个完整的对照决策:
routing: decisions: - name: terse_preference_routing description: Route requests whose preference signal matches terse_answers. priority: 100 rules: operator: AND conditions: - type: preference name: terse_answers modelRefs: - model: general-expert use_reasoning: false - name: detailed_preference_routing description: Route requests whose preference signal matches detailed_answers. priority: 90 rules: operator: AND conditions: - type: preference name: detailed_answers modelRefs: - model: general-expert use_reasoning: false - name: default-route description: Fallback route for requests matching no preference rule. priority: 10 rules: operator: AND conditions: [] modelRefs: - model: general-expert use_reasoning: false注意两点:
- 决策按
priority从高到低匹配,未命中任何偏好规则的请求会落到default-route(conditions 为空)这样的兜底决策; - 在真实部署中,每个偏好决策还可以挂不同的
modelRefs(如简洁偏好走低成本快速模型、详尽偏好走高成本强推理模型)以及不同的插件栈,这正是文档所说“风格信号影响模型选择与插件选择”的落地方式。
e2e 验证用例
preference_routing.go 注册了名为preference-routing的端到端用例,数据来自 preference_routing_cases.json,三个用例完整覆盖了三种结果:
| 用例 | 查询 | 期望信号 | 是否命中 |
|---|---|---|---|
| Terse | “Please keep your reply short and to the point, no need to explain further.” | terse_answers→terse_preference_routing | 命中 |
| Detailed | “Please explain your full reasoning and go through this step by step, don't leave anything out.” | detailed_answers→detailed_preference_routing | 命中 |
| No match | “What's the capital of France?” | 无(不应命中任何规则) | 不命中 |
用例通过响应头x-vsr-matched-preference断言实际命中的信号名(见 signal_routing_helpers.go),用“纯事实性查询没有风格提示词、不应命中任何偏好规则”验证了阈值与 margin 双重门槛确实生效。
三种执行模式与选择建议
结合 classifier_preference_lifecycle.go 与 preference_execution_test.go,偏好信号共有三类执行契约:
| 模式 | 触发条件 | 行为 |
|---|---|---|
| 对比原型(contrastive / prototypes) | 规则带 examples、显式embedding_model、或use_contrastive: true | 嵌入 + 原型聚类 + 相似度打分,本地或远程嵌入后端均可 |
| 原生决策任务(decision task) | 仅有 description(description-only),且未绑定外部分类器 | 由决策模型以原生 judgment 判定,概率式打分 |
| 外部 LLM 分类器(external) | 配置了model_role: preference的外部模型 | 由外部 LLM 按 system/user prompt 判定(classifyExternal) |
选择建议:
- 追求可解释、可离线验证、低延迟:默认的对比原型模式最合适,examples 就是可审计的锚点;
- 只有描述、不想维护 examples:可以只写 description,让其自动走决策任务模式;
- 需要强语义判定或跨语言泛化:可显式绑定外部 LLM 分类器,但注意其延迟与成本;
- 明确禁用对比:显式写
use_contrastive: false;省略即默认true,这是 v0.3 的关键行为变更。
调参实战指南
prototype_scoring的默认值在 prototype_scoring_config.go 的WithDefaults()中固化,可直接作为调参起点:
| 参数 | 默认值 | 调参含义 |
|---|---|---|
enabled | true | 是否启用原型聚类压缩;设为false则每个例子独立参与打分 |
cluster_similarity_threshold | 0.9 | 聚类合并阈值,越高越严格、保留更多原型;越低压缩越激进 |
max_prototypes | 8 | 每个规则保留的最大原型数,超出截断 |
best_weight | 0.75 | 最强原型在最终得分中的权重(0~1);越接近 1 越偏向单点最强匹配 |
top_m | 2 | 计算 support 时取前 M 个最高相似度 |
margin_threshold | 0(<0 时归零) | 最佳与次佳的得分差下限;>0 时拒绝歧义匹配 |
典型调参路径:
- 先用默认参数跑通,观察
prototype_count(每个规则实际保留的原型数); - 若 examples 语义高度重合、
prototype_count偏多,可下调cluster_similarity_threshold或上调max_prototypes限制; - 若出现“弱命中”(得分刚好过 threshold 但语义牵强),调大
margin_threshold(如 0.05)拒绝歧义; - 若发现某类请求被误判为其他偏好,检查该类别的 examples 是否过少,适当补充语义多样的锚点例子;
- 每条规则独立的
threshold用于控制该规则的严格程度——整体偏好检测过松/过紧时优先调整它,而不是全局参数。
完整的多规则实战样例可参考 e2e/profiles/preference-routing/values.yaml(双规则 + margin 门槛 + 兜底路由),单规则可复用片段见 power-user.yaml,参考基线配置见 config/config.yaml。
总结
preference信号把“用户的回答风格偏好”变成了一个可路由、可观测、可复用的命名输入:声明routing.signals.preferences下的规则,用 description + examples 作为训练锚点;v0.3 默认启用对比模式,把每个规则压缩成原型库,用余弦相似度 + threshold + margin 双重门槛做裁决;决策层通过type: preference条件选择最合适的模型与插件组合。从 Preference Signal 文档 到 classifier_preference_lifecycle.go、contrastive_preference_classifier.go 与 e2e 用例,整条链路既保留了示例驱动的可解释性,又提供了工程化的调参与验证手段——这是 v0.3 让“同一话题、不同风格”的差异化路由变得可行、可运维的关键能力。
- 后端
- API网关
- 模型推理服务
- AI Agent
【免费下载链接】semantic-router
An open, programmable decision layer for models and compute.
相关推荐
semantic-router Preference Signal 指南:用示例与原型打分实现响应风格偏好路由
semantic router Preference Signal 指南:用示例与原型打分实现响应风格偏好路由 导读 本指南讲解 semantic router
后端API网关模型推理服务AI AgentSemantic-Router 偏好路由实战:用 LLM 偏好匹配实现精细化意图路由(Preference Routing)
Semantic Router 偏好路由实战:用 LLM 偏好匹配实现精细化意图路由(Preference Routing) 本文以 semantic rout
后端API网关模型推理服务AI Agentsemantic-router 偏好信号路由(Preference Signal Routing)实战指南:用 LLM 与 Embedding 实现细粒度请求路由
semantic router 偏好信号路由(Preference Signal Routing)实战指南:用 LLM 与 Embedding 实现细粒度请求路
后端API网关模型推理服务AI Agent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考