跳到主要内容

Label & Selector

为什么单独有这篇文档

AIDY 当前把“候选 upstream / model 的收紧规则”统一建模为 label selector。

这套语义会同时影响:

  • route.upstreams[] 之后还能剩下哪些 upstream
  • 请求携带 model 时,哪些 upstream_models 还能继续参与匹配
  • /v1/models 最终能返回哪些模型名

因此,Label & Selector 不是某一个资源的局部字段说明,而是一套贯穿 route、consumer、api key、upstream、upstream model 的通用约束。

哪些地方有 labels

当前只有两类资源可被打 label:

  • upstreams.labels
  • upstream_models.labels

其中:

  • upstreams.labels 用于给 upstream 打业务标签,例如 tier=default
  • upstream_models.labels 用于给某条模型映射打标签,例如 family=chat

route 本身没有 labels 字段。

哪些地方可以写 selector

当前可配置 selector 的地方有:

  • routes.upstream_selector
  • routes.model_selector
  • consumers.upstream_selector
  • consumers.model_selector
  • consumer_api_keys.upstream_selector
  • consumer_api_keys.model_selector

语义上:

  • upstream_selector 作用于 upstream labels
  • model_selector 作用于 upstream model labels
  • 多层 selector 叠加时,始终是逐层收紧,必须全部命中

另外,route.upstreams[] 永远是基础集合。

也就是说:

selector 只能在 route 已绑定的 upstream 集合上继续过滤,不能绕过 route.upstreams[] 凭空选出一个未绑定 upstream。

如果 route.upstreams[] 为空,则当前 route 没有任何可用 upstream。

Selector 结构

当前 selector 尽量对齐 Kubernetes LabelSelector,只支持:

  • match_labels
  • match_expressions

示例:

upstream_selector:
match_labels:
tier: default
match_expressions:
- key: region
operator: In
values: ["us-east", "us-west"]

语义:

  • match_labels 内部是 AND
  • match_expressions 内部是 AND
  • match_labelsmatch_expressions 之间也是 AND
  • In / NotInvalues 列表内部是 OR
  • 空 selector 表示“不额外限制”

当前支持的 operator 只有四种:

  • In
  • NotIn
  • Exists
  • DoesNotExist

Label key / value 规则

label key / value 语法参考 Kubernetes labels。

key

label key 由两部分组成:

  • 可选 prefix
  • 必选 name

格式是:

[prefix/]<name>

约束:

  • name 必须非空
  • name 最长 63 字符
  • name 必须以字母或数字开头和结尾
  • 中间可包含字母、数字、-_.
  • prefix 可选;如果存在,必须是合法 DNS subdomain
  • prefixname 之间最多只能有一个 /

合法示例:

  • tier
  • region
  • example.com/tier
  • ml.example.com/family

非法示例:

  • /tier
  • example.com/
  • Example.com/tier
  • example.com/-tier

value

label value 可以为空字符串。

如果非空,则:

  • 最长 63 字符
  • 必须以字母或数字开头和结尾
  • 中间可包含字母、数字、-_.

内部保留前缀

下面两类前缀是 AIDY 内部预留的,用户不能写入自定义 labels:

  • aidy/*
  • aidy.*/*

也就是说,这些 key 可以出现在 selector 里做匹配,但不能出现在:

  • upstreams.labels
  • upstream_models.labels

这样做是为了给运行时保留系统内置 label 空间,避免与用户自定义标签冲突。

当前内置系统 labels

运行时当前会额外注入三个内置 label:

  • aidy/upstream-id
  • aidy/model-name
  • aidy/protocol

含义:

  • aidy/upstream-id:当前候选 upstream 的资源 ID
  • aidy/model-name:当前命中的客户端模型名,也就是 upstream_models.model
  • aidy/protocol:当前候选 upstream 的运行时协议,例如 chat-completions / open-responses / claude-messages

因此,如果你想按“某个固定 upstream”“某个固定客户端模型名”或“某个 upstream 协议”做 selector,可以直接匹配这些系统 key。

例如:

upstream_selector:
match_labels:
aidy/upstream-id: ups_primary

model_selector:
match_labels:
aidy/model-name: gpt-4o-mini

运行时如何应用

一条带 model 的请求在运行时大致按这个顺序应用:

  1. 先取 route.upstreams[] 作为基础 upstream 集合
  2. 对 upstream 依次应用 route / consumer / api key 的 upstream_selector
  3. 再在 upstream_models 中按请求模型名查候选
  4. 对 upstream model 依次应用 route / consumer / api key 的 model_selector
  5. 过滤 provider / upstream 不可用、协议不兼容、凭证无效等候选
  6. 最后再进入 priority / lb_weight 排序与 fallback

如果请求不带 model,则不会走 model_selector 那层,只继续在 route 已绑定的 upstream 集合里应用 upstream selector。

常见用法

用业务标签做灰度

upstream_selector:
match_labels:
tier: beta

只允许聊天模型

model_selector:
match_labels:
family: chat

排除某个区域

upstream_selector:
match_expressions:
- key: region
operator: NotIn
values: ["cn-north"]

精确锁定某个 upstream

upstream_selector:
match_labels:
aidy/upstream-id: ups_01ARZ3NDEKTSV4RRFFQ69G5FB3

相关阅读