- 后端
- API网关
- 模型推理服务
- AI Agent
【免费下载链接】semantic-router
An open, programmable decision layer for models and compute.
导读
header_mutation是 semantic-router 中一个路由局部(route-local)插件,它允许你在某条具体路由被命中后,对发送给下游模型/后端的请求头执行「添加、更新、删除」三种操作。本文基于 header-mutation.md(v0.3 中文版见 i18n 版本),结合仓库中的 YAML 片段、Go 源码与测试,讲解该插件的适用场景、完整配置写法、底层执行语义(append/set/remove 与 Envoy ext_proc 的对应关系)、DSL 表达、校验规则以及安全注意事项,读完即可在自己的路由决策中独立配置并验证该插件。
一、插件定位:路由局部的下游头策略
semantic-router 是一个可编程的「模型与算力决策层」,每条路由(decision)除了做语义/关键词/领域等分类决策外,还可以挂载一系列插件来改变最终发给后端的请求。header_mutation就是其中用于改写下游请求头的插件。
它的核心设计原则是**「局部化」**:
- 头策略只作用在被匹配的那条路由上,不影响路由器其他路由的流量;
- 三种操作(增、改、删)在同一个插件中完成,不需要拆分成多个过滤器;
- 变换是显式声明的,而不是藏在 Envoy 代理配置或业务应用代码里,便于审计与维护。
对应到仓库中的真实配置片段即 config/fragments/plugin/header-mutation/tenant-routing.yaml,它也是本教程的配套示例。
二、它解决什么问题、何时该用
在网关/路由器场景中,不同路由常常需要携带不同的下游头:例如付费租户走 premium 后端,需要在其请求头上盖一个X-Tenant-Tier: premium的戳记;调试环境希望只对选中流量追加溯源信息;下游服务可能期望收到路由专属的标识头。若没有这样的插件,你只能把这些逻辑散落在代理配置、服务网格规则或应用代码里,导致「某条路由到底改了哪些头」难以追踪。
header_mutation把「哪条路由 → 改哪些头」收敛成一个 YAML 声明块,紧贴路由定义,天然具备可读性和可追溯性。典型使用场景:
- 一条路由需要把租户或套餐元数据写入请求头(如
X-Tenant-Tier); - 下游服务期望路由专用头(如
X-Route-Source); - 只对选中流量添加调试或溯源(provenance)头,而全量流量不携带这些头。
三、配置详解:完整 YAML 与参数语义
在路由定义中,将插件挂在routing.decisions[].plugins下。以下为完整可用的最小示例(与原文档及仓库片段一致):
plugins: - type: header_mutation configuration: add: - name: X-Tenant-Tier value: premium update: - name: X-Route-Source value: semantic-router delete: - X-Debug-Trace参数说明
| 字段 | 类型 | 语义 | 默认/边界 |
|---|---|---|---|
type | string | 固定为header_mutation | 必填 |
configuration.add | HeaderPair[] | 追加下游请求头(若同名头已存在则保留原值,见下文 append 语义) | 可选,列表可空 |
configuration.update | HeaderPair[] | 设置/覆盖下游请求头(若同名头已存在则覆盖) | 可选,列表可空 |
configuration.delete | string[] | 删除指定名称的请求头 | 可选,列表可空 |
其中每个HeaderPair由name(头名称)与value(头值)两个字符串字段组成,对应源码结构 plugin_config.go 中的HeaderPair:
type HeaderPair struct { Name string `json:"name" yaml:"name"` Value string `json:"value" yaml:"value"` }整段插件的配置结构定义在 HeaderMutationPluginConfig:
type HeaderMutationPluginConfig struct { Add []HeaderPair `json:"add,omitempty" yaml:"add,omitempty"` Update []HeaderPair `json:"update,omitempty" yaml:"update,omitempty"` Delete []string `json:"delete,omitempty" yaml:"delete,omitempty"` }与主配置文件的对应
仓库主配置 config/config.yaml 中真实存在一个header_mutation用例(位于premium_route类决策下),内容与本教程示例完全一致,可直接作为「插件挂载位置」的参照:它被放在fallback策略块之后的plugins列表中,说明插件与模型选择、fallback 等决策属性平级。
四、底层执行语义:append、set 与 remove
「add」与「update」在语义上并不相同,这一点由运行时实现严格保证。核心执行逻辑在 req_filter_header_mutation.go 的 buildHeaderMutations:
for _, mutation := range pluginruntime.HeaderMutations(headerConfig) { if mutation.Operation == "remove" { removeHeaders = append(removeHeaders, mutation.Name) continue } header := &corev3.HeaderValueOption{Header: &corev3.HeaderValue{Key: mutation.Name, RawValue: []byte(mutation.Value)}} if mutation.Operation == "set" { header.AppendAction = corev3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD } setHeaders = append(setHeaders, header) }可见三种操作被翻译成两类 Envoy ext_proc 输出:
remove→ 进入removeHeaders列表,最终映射为 Envoy 的 header 删除指令;add/update→ 进入setHeaders列表,其中update会带上OVERWRITE_IF_EXISTS_OR_ADD的 AppendAction(已存在则覆盖,不存在则追加),而add不设置该标记,即纯追加语义。
操作顺序与类型映射由 pluginruntime/headers.go 的 HeaderMutations 生成,严格保持配置声明的顺序:先 add、再 update、最后 delete,且以显式的operation字段(append/set/remove)对外暴露:
for _, header := range policy.Add { result = append(result, HeaderMutation{Name: header.Name, Value: header.Value, Operation: "append"}) } for _, header := range policy.Update { result = append(result, HeaderMutation{Name: header.Name, Value: header.Value, Operation: "set"}) } for _, name := range policy.Delete { result = append(result, HeaderMutation{Name: name, Operation: "remove"}) }这解释了文档中「add、update、delete 在一个插件中统一支持」背后的真实差异:add是追加(保留可能存在的同名旧值),update是覆盖或新建,delete是移除。
五、校验规则:什么配置会被拒绝
配置不是随便写都能通过校验的。在 Kubernetes 配置转换路径中,k8s/converter.go 的 validateHeaderMutationPluginConfig 定义了以下约束:
- 至少声明一种操作:
add、update、delete三者全空时直接报错header_mutation plugin must specify at least one of: add, update, delete; - 头名称不能为空:
add与update中的每一项,name字段缺失或为空时,分别报header_mutation add/update: header name cannot be empty; - 配置解码使用
DisallowUnknownFields(),意味着未知字段会被拒绝,避免拼写错误悄悄失效。
此外,从源码结构看(decision_reliability.go),配置加载阶段还有一道「可靠性头保护」:如果某个决策块自己声明了 reliability 相关头(由该决策的 reliability 块独占写入),加载器会从header_mutation的 add/update/delete 中剔除并告警,防止插件与可靠性机制互相覆盖。这意味着不要把可靠性机制专用的头名放进插件配置。
六、DSL 表达:在 .dsl 路由描述中声明
除了 YAML,semantic-router 的 DSL 也能描述该插件。DSL 编译器在 compiler_plugin_configs.go 的 compileHeaderMutationPluginConfig 中把add、update、delete三个字段直接映射到配置结构:
func (c *Compiler) compileHeaderMutationPluginConfig(fields map[string]Value) config.HeaderMutationPluginConfig { return config.HeaderMutationPluginConfig{ Add: compileHeaderPairs(fields["add"]), Update: compileHeaderPairs(fields["update"]), Delete: stringArrayValue(fields["delete"]), } }其中add/update需要每个元素是包含name与value字符串字段的对象(见 compileHeaderPairs);反向生成 DSL 文本时,decompiler_plugin_emit.go 的 emitHeaderMutationPluginConfig 会按add: [{ name: "...", value: "..." }]、update: [...]、delete: [...]的格式输出。DSL 解析器还接受连字符别名header-mutation,统一规范化为header_mutation(见 dsl/parser.go)。
对应测试覆盖在 dsl_test.go 的 header_mutation 用例,可在本地运行go test ./pkg/dsl/...验证 DSL 编解码往返。
七、预览与调试:用 API 验证改动计划
如果你在使用 Dashboard/API Server 管理配置,可以直接预览该插件产生的有序 Envoy 头操作,无需真正把配置下发到代理:
- 端点:
POST /api/v1/plugins/header_mutation/preview(路由注册见 route_plugin_previews.go); - 请求体:提供一个候选
configuration(add/update/delete),或通过binding(recipe+decision)引用已激活配置; - 响应:返回
mutations数组——即 pluginruntime.HeaderMutation 的有序列表,每项含name、value、operation(append/set/remove)三要素,保证与 Envoy 实际收到的顺序与语义一致(见 headers.go 注释); - 权限:头值属于敏感数据,查看值需要
secret_view权限,无权限时预览响应会把非 remove 操作的值打码(values_redacted标记,实现见 handleHeaderMutationPreview)。
这样你在写正式配置前就能确认「哪条路由、按什么顺序、改哪些头」是否符合预期。
八、安全注意事项
原文档特别强调:头的值是静态配置,不是模板。也就是说:
- 头值不允许做请求元数据插值/模板展开,配置里写什么,下游就收到什么;
- 严禁用不受信任的请求元数据去制造身份或授权类头(例如凭客户端输入动态拼出
Authorization、X-Api-Key之类的凭据头),否则会引入头注入与越权风险; - 与这一点相呼应,仓库中
shadow_dispatch插件的forward_headers默认不允许转发自定义凭据头(见 plugin_config.go 中 ForwardHeaders 的注释),说明「头 = 信任边界」是整体设计共识。
九、实践建议小结
- 把「租户/套餐/路由来源」等元数据写入下游头时,优先用
header_mutation而不是改代理配置,保持路由逻辑自包含; - 明确区分
add(追加)与update(覆盖),按需选用,避免同名头叠加造成下游解析歧义; - 至少声明一种操作,且每个头名非空,否则配置会被校验器拒绝;
- 不要碰 reliability 机制独占的头名,也不要通过该插件注入动态凭据;
- 上线前用
/api/v1/plugins/header_mutation/preview预览有序变更计划,再做真实验证。
通过 config/fragments/plugin/header-mutation/tenant-routing.yaml 这一最小示例起步,配合 config/config.yaml 中的真实挂载位置、extproc 实现 与 k8s 校验逻辑,你就能在语义路由决策中精确、可控地管理下游请求头。
- 后端
- API网关
- 模型推理服务
- AI Agent
【免费下载链接】semantic-router
An open, programmable decision layer for models and compute.
相关推荐
semantic-router 路由级 Header 变更插件 header_mutation 实战指南:为下游添加、更新、删除请求头
semantic router 路由级 Header 变更插件 header_mutation 实战指南:为下游添加、更新、删除请求头 导读 header_mu
后端API网关模型推理服务AI Agentsemantic-router request_params 插件实战:为每条路由硬性约束 OpenAI 请求参数
semantic router request_params 插件实战:为每条路由硬性约束 OpenAI 请求参数 request_params 是 seman
后端API网关模型推理服务AI Agentsemantic-router 插件机制实战指南:在决策命中后为路由挂载可编程行为
semantic router 插件机制实战指南:在决策命中后为路由挂载可编程行为 本文围绕 semantic router 的 Plugins(插件)机制展开
后端API网关模型推理服务AI Agent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考