Label & Selector
为什么单独有这篇文档
AIDY 当前把“route 访问控制”与“候选 upstream / model 的收紧规则”统一建模为 label selector。
这套语义会同时影响:
- 当前 consumer / api key 能否访问已经命中的 route
route.upstreams[]之后还能剩下哪些 upstream- 请求携带
model时,哪些upstream_models还能继续参与匹配 /v1/models最终能返回哪些模型名
因此,Label & Selector 不是某一个资源的局部字段说明,而是一套贯穿 route、consumer、api key、upstream、upstream model 的通用约束。
哪些地方有 labels
当前有三类资源可被打 label:
routes.labelsupstreams.labelsupstream_models.labels
其中:
routes.labels用于给 route 打访问控制标签,例如access=adminupstreams.labels用于给 upstream 打业务标签,例如tier=defaultupstream_models.labels用于给某条模型映射打标签,例如family=chat
哪些地方可以写 selector
当前可配置 selector 的地方有:
routes.upstream_selectorroutes.model_selectorconsumers.route_selectorconsumers.upstream_selectorconsumers.model_selectorconsumer_api_keys.route_selectorconsumer_api_keys.upstream_selectorconsumer_api_keys.model_selector
语义上:
route_selector作用于 route labelsupstream_selector作用于 upstream labelsmodel_selector作用于 upstream model labels- 多层 selector 叠加时,始终是逐层收紧,必须全部命中
另外,route.upstreams[] 永远是基础集合。
也就是说:
selector 只能在 route 已绑定的 upstream 集合上继续过滤,不能绕过
route.upstreams[]凭空选出一个未绑定 upstream。
如果 route.upstreams[] 为空,则当前 route 没有任何可用 upstream。
Selector 结构
AIDY selector 使用 LabelSelector 表达,并尽量对齐 Kubernetes LabelSelector 语义,当前只支持:
match_labelsmatch_expressions
示例:
upstream_selector:
match_labels:
tier: default
match_expressions:
- key: region
operator: In
values: ["us-east", "us-west"]
语义:
match_labels内部是 ANDmatch_expressions内部是 ANDmatch_labels与match_expressions之间也是 ANDIn/NotIn的values列表内部是 OR- 空 selector 表示“不额外限制”
当前支持的 operator 只有四种:
InNotInExistsDoesNotExist
Label key / value 规则
label key 语法参考 Kubernetes labels;label value 在 Kubernetes value 规则基础上额外允许 /,用于匹配 anthropic/glm-5.1 这类模型名。
key
label key 由两部分组成:
- 可选 prefix
- 必选 name
格式是:
[prefix/]<name>
约束:
name必须非空name最长 63 字符name必须以字母或数字开头和结尾- 中间可包含字母、数字、
-、_、. prefix可选;如果存在,必须是合法 DNS subdomainprefix与name之间最多只能有一个/
合法示例:
tierregionexample.com/tierml.example.com/family
非法示例:
/tierexample.com/Example.com/tierexample.com/-tier
value
label value 可以为空字符串。
如果非空,则:
- 最长 63 字符
- 必须以字母或数字开头和结尾
- 中间可包含字母、数字、
-、_、.、/
内部保留前缀
下面两类前缀是 AIDY 内部预留的,用户不能写入自定义 labels:
aidy/*aidy.*/*
也就是说,这些 key 可以出现在 selector 里做匹配,但不能出现在:
upstreams.labelsupstream_models.labelsroutes.labels
这样做是为了给运行时保留系统内置 label 空间,避免与用户自定义标签冲突。
当前内置系统 labels
运行时当前会额外注入四个内置 label:
aidy/route-idaidy/upstream-idaidy/model-nameaidy/protocol
含义:
aidy/route-id:当前命中的 route 资源 ID,仅用于route_selectoraidy/upstream-id:当前候选 upstream 的资源 IDaidy/model-name:当前命中的客户端模型名,也就是upstream_models.modelaidy/protocol:当前候选 upstream 的运行时协议,例如chat-completions/open-responses/claude-messages
因此,如果你想按“某个固定 route”“某个固定 upstream”“某个固定客户端模型名”或“某个 upstream 协议”做 selector,可以直接匹配这些系统 key。
例如:
route_selector:
match_labels:
aidy/route-id: rt_admin
upstream_selector:
match_labels:
aidy/upstream-id: ups_primary
model_selector:
match_labels:
aidy/model-name: gpt-4o-mini
运行时如何应用
一条带 model 的请求在运行时大致按这个顺序应用:
- 先按 path 命中 route
- 认证出 consumer / api key 后,对 route 依次应用 consumer / api key 的
route_selector - 再取
route.upstreams[]作为基础 upstream 集合 - 对 upstream 依次应用 route / consumer / api key 的
upstream_selector - 再在
upstream_models中按请求模型名查候选 - 对 upstream model 依次应用 route / consumer / api key 的
model_selector - 过滤 provider / upstream 不可用、协议不兼容、凭证无效等候选
- 最后再进入 priority / lb_weight 排序与 fallback
如果请求不带 model,则不会走 model_selector 那层,只继续在 route 已绑定的 upstream 集合里应用 upstream selector。
常见用法
限定 consumer 可访问的 route
routes:
- labels:
access: admin
consumers:
- route_selector:
match_labels:
access: admin
用业务标签做灰度
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