Higress AI 意图识别插件(ai-intent):让 LLM 为请求打标签,驱动下游模型与缓存选择
【免费下载链接】higress🤖 AI Gateway | AI Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/hi/higress
导读
ai-intent 是 Higress AI 网关中基于 LLM(大语言模型)的意图识别 Wasm 插件:它在请求转发前调用一次大模型,将用户问题归类到预设场景类别(如“金融|电商|法律”),并把识别结果写入intent_category属性,供 ai-proxy、ai-cache 等后续插件按意图选择不同大模型或缓存策略。读完本文,你将掌握该插件的运行机制、完整配置项与默认值、前置依赖(内部路由 + 固定地址服务),以及如何通过源码与测试验证它的行为,从而在自己的 AI 网关场景中落地“按意图分流”的能力。
插件功能概述
根据插件文档与源码注释,ai-intent 的核心职责是:智能判断用户请求与某个领域或 Agent 的功能契合度,从而提升不同模型的应用效果和用户体验。它在请求体阶段提取用户提问,构造提示词(prompt)并同步调用一次大模型,将返回结果与预设类别比对后,把命中的类别写入 proxy-wasm 的 Property(属性)intent_category中。
该插件位于plugins/wasm-go/extensions/ai-intent/目录,当前版本号为2.0.2(见 VERSION),插件名称为ai-intent,模块路径为github.com/alibaba/higress/plugins/wasm-go/extensions/ai-intent(见 go.mod)。
运行属性:执行阶段与优先级
| 属性 | 值 |
|---|---|
| 插件执行阶段 | 默认阶段(Default Phase) |
| 插件执行优先级 | 700(文档标注) |
需要说明的是,main.go 中的注解声明为@Phase AUTHN、@Priority 1000,文档标注的执行优先级为700,实际部署时的优先级以 WasmPlugin CRD 中配置的priority字段为准。无论取哪个值,关键约束是:ai-intent 的优先级必须高于 ai-proxy 等后续消费意图的插件,这样它才能在请求转发给上游大模型之前完成意图识别并写入 Property。
前置准备:四条关键前提
在启用 ai-intent 之前,需要按文档说明完成以下四项前置配置(这是文档中反复强调、最容易踩坑的部分):
- 优先级高于后续插件:ai-intent 的优先级高于 ai-proxy 等“后续使用意图”的插件。后续插件可以通过
proxywasm.GetProperty([]string{"intent_category"})获取意图类别,并据此为不同缓存库或不同大模型做选择。 - 新建一条供插件访问大模型的路由:例如路由以
/intent作为前缀,服务选择大模型服务,并为该路由开启 ai-proxy 插件。 - 新建一个固定地址服务(如
intent-service):服务指向127.0.0.1:80(即网关自身实例 + 端口),ai-intent 内部需要通过该服务发起调用以访问上述新增路由,服务名对应配置项llm.proxyServiceName;也可以新建 DNS 类型服务,使插件直接访问其他大模型服务。 - 访问白名单:如果使用固定地址服务调用网关自身,需要把
127.0.0.1加入网关的访问白名单,否则内部回环调用会被拦截。
这一设计使得插件通过“自己调用自己的大模型路由”完成一次额外的 LLM 分类请求,对用户请求透明,不影响最终的业务转发。
配置参数详解
配置采用 YAML 结构,分为scene(场景)与llm(大模型代理)两个区块。以下参数表完整覆盖了文档定义,并补充了从 main.go 的parseConfig中确认的默认值与解析行为:
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|---|---|---|---|---|
scene.category | string | 必填 | - | 预设场景类别,以\|分割,如金融\|电商\|法律\|Higress;为空时插件启动报错 |
scene.prompt | string | 非必填 | 见下方默认提示词 | LLM 请求的 prompt 模板,包含%s占位符 |
llm.proxyServiceName | string | 必填 | - | 新建的 Higress 服务,指向大模型(取 Higress 中的 FQDN 值);为空时插件启动报错 |
llm.proxyUrl | string | 必填 | - | 大模型路由请求地址全路径,可以是网关自身地址或任意 OpenAI 协议大模型地址,例如http://127.0.0.1:80/intent/compatible-mode/v1/chat/completions;为空时插件启动报错 |
llm.proxyDomain | string | 非必填 | 从proxyUrl解析获取 | 大模型服务的域名 |
llm.proxyPort | string/number | 非必填 | 从proxyUrl解析获取 | 大模型服务端口号 |
llm.proxyApiKey | string | 非必填 | - | 使用外部大模型服务时需配置对应大模型的 API_KEY |
llm.proxyModel | string | 非必填 | qwen-long | 大模型类型 |
llm.proxyTimeout | number | 非必填 | 10000 | 调用大模型的超时时间,单位 ms |
默认提示词模板(中文,源码常量DefaultPrompt,见 main.go):
你是一个智能类别识别助手,负责根据用户提出的问题和预设的类别,确定问题属于哪个预设的类别,并给出相应的类别。用户提出的问题为:'%s',预设的类别为'%s',直接返回一种具体类别,如果没有找到就返回'NotFound'。
从源码可以确认的默认值细节:
proxyDomain与proxyPort的自动解析:当未配置llm.proxyDomain时,从proxyUrl中解析Hostname();当未配置llm.proxyPort(或解析结果<= 0)时,从proxyUrl解析端口;若 URL 未显式携带端口,则按协议取默认值——HTTP 为80、HTTPS 为443。proxyTimeout默认 10000ms:配置值<= 0时回退到defaultTimeout = 10 * 1000(ms)。proxyModel默认qwen-long:未配置时使用该模型名。keyFrom(内部默认值):插件从请求 Body 提取用户问题时默认使用 GJSON Pathmessages.@reverse.0.content(即 messages 数组中最后一条 user 消息的 content);解析 LLM 响应时默认使用choices.0.message.content。
完整配置示例
以下示例来自文档并可直接套用(注意proxyTimeout在文档示例中写为字符串"10000",源码按数字解析,两者均可被兼容处理):
scene: category: "金融|电商|法律|Higress" prompt: "你是一个智能类别识别助手,负责根据用户提出的问题和预设的类别,确定问题属于哪个预设的类别,并给出相应的类别。用户提出的问题为:'%s',预设的类别为'%s',直接返回一种具体类别,如果没有找到就返回'NotFound'。" llm: proxyServiceName: "intent-service.static" proxyUrl: "http://127.0.0.1:80/intent/compatible-mode/v1/chat/completions" proxyDomain: "127.0.0.1" proxyPort: "80" proxyModel: "qwen-long" proxyApiKey: "" proxyTimeout: "10000"对应的 WasmPlugin 资源声明可参照仓库中的示例结构(如 samples/wasmplugin/default-config.yaml),将上述内容放入spec.defaultConfig,并通过url指定 ai-intent 插件的 OCI 镜像地址(如oci://higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/ai-intent:2.0.2),通过matchRules或matchAll声明生效范围。
工作原理:源码级解析
1. 配置解析阶段(parseConfig)
main.go 中的parseConfig负责初始化插件:
- scene 初始化:
scene.category为空直接返回scene.category must not by empty错误;随后用strings.Split(category, "|")将类别拆分为数组CategoryArr,用于后续比对。 - prompt 初始化:
scene.prompt为空时使用内置DefaultPrompt。 - llm 代理初始化:
llm.proxyServiceName与llm.proxyUrl为必填,缺失直接报错;通过url.Parse解析出请求路径ProxyPath,并完成proxyDomain/proxyPort的兜底逻辑(如上文所述),最终用wrapper.NewClusterClient(wrapper.FQDNCluster{FQDN: ProxyServiceName, Port: ProxyPort, Host: ProxyDomain})构建基于 FQDN 集群的 HTTP 客户端。
一个值得注意的安全细节:测试 main_test.go 中的TestParseConfigDoesNotLogProxyAPIKey专门验证了parseConfig不会把proxyApiKey、自定义 prompt 以及 URL 中内嵌的用户名密码(user:password@)写入日志,防止敏感信息泄漏。
2. 请求体处理阶段(onHttpRequestBody)
意图识别的核心逻辑发生在 onHttpRequestBody:
- 提取用户问题:用 GJSON 按
keyFrom.requestBody(默认messages.@reverse.0.content)从请求 Body 中提取原始问题,并经zhToUnicode处理 Unicode 转义。 - 拼接 prompt:用
fmt.Sprintf(prompt, 问题, 预设类别)完成两个%s占位符替换,即“用户问题”和“预设类别集合”。 - 构造 LLM 请求:
generateProxyRequest组装 OpenAI 协议请求体{"model": ..., "messages": [{"role": "user", "content": prompt}]},并携带Content-Type: application/json与Authorization: Bearer <proxyApiKey>头(见 main.go)。 - 同步等待识别结果:通过
ProxyClient.Post异步发起调用,超时时间取proxyTimeout;期间请求处理返回types.ActionPause暂停请求,调用结束后通过proxywasm.ResumeHttpRequest()恢复。 - 类别校验与写入 Property:仅当 LLM 返回
statusCode == 200且解析出choices[0].message.content时,遍历CategoryArr进行比对。判定条件有两条:返回的 category 与预设类别完全一致,或返回的 category包含该预设类别(同时跳过空白类别)。命中后调用proxywasm.SetProperty([]string{"intent_category"}, ...)写入意图类别(见 main.go)。 - 未命中处理:若 LLM 返回
NotFound或响应异常(非 200、无 choices),则不会写入任何 Property,请求照常放行——这也意味着后续插件拿不到intent_category时会走默认分支。
3. 与其他阶段的关系
- 请求头阶段
onHttpRequestHeaders仅执行ctx.DisableReroute()并返回HeaderStopIteration,用于禁用重路由,避免与后续路由逻辑冲突。 - 响应头 / 响应体 / 流式响应阶段均为透传(
ActionContinue/ 原样返回 chunk),意图识别只影响请求阶段,不修改业务响应。
行为验证:测试用例解读
main_test.go 覆盖了配置解析、请求处理、配置校验与边界场景四类测试,可作为理解插件行为的最佳参考:
- 配置解析:验证“基本配置、自定义 prompt、最小配置(仅 category + proxyServiceName + proxyUrl)、HTTPS 配置”四类配置均能正常启动(
OnPluginStartStatusOK)。 - 请求体处理:构造“今天股市怎么样?”(应识别为“金融”)与“这个商品什么时候发货?”(应识别为“电商”)的请求,模拟 LLM 返回对应类别后,断言
host.GetProperty([]string{"intent_category"})的值分别为金融、电商;而“今天天气怎么样?”模拟返回NotFound时,断言GetProperty返回错误(即未设置 Property)。这三个用例直观验证了识别与校验逻辑。 - 配置校验:分别缺少
scene.category、llm.proxyServiceName、llm.proxyUrl时,插件启动状态均非 OK,与parseConfig中的必填校验一一对应。 - 边界情况:无效 JSON 请求体(返回
NotFound时不写 Property)、LLM 服务返回 503 错误(非 200 不写 Property),验证了插件在异常路径下不会误写意图。
与下游插件联动:基于意图的路由与缓存选择
ai-intent 的价值最终体现在联动上。文档明确说明:后续插件(如 ai-proxy、ai-cache 等)可以通过proxywasm.GetProperty([]string{"intent_category"})读取意图类别,据此为不同缓存库或不同大模型做选择。
实现层面,intent_category由proxywasm.SetProperty写入(见 main.go),任何运行在同一 Wasm 过滤器链、优先级更低的插件都可以通过proxywasm.GetProperty读取该键。仓库中 wasm-rust/example/ai-intent/src/lib.rs 也展示了 Rust 侧对intent_category属性的消费示例,说明这一属性协议是跨语言 SDK 统一的约定。
典型的联动场景是:意图识别 → 意图分发。例如“金融”类问题路由到金融领域专用模型并启用对应缓存,“电商”类问题走电商 Agent,未命中类别(无intent_category)则回落到通用模型。这样既提升了分类准确率,也避免了所有流量都打到昂贵的大模型上,这正是文档所称“提升不同模型的应用效果和用户体验”的落地方式。
总结
ai-intent 通过“一次额外的 LLM 调用 + Property 写入”实现了细粒度的请求意图分类,是 Higress AI 网关中构建多模型路由、意图感知缓存等高级能力的基础插件。实践要点可归纳为:正确配置前置路由与固定地址服务(含 127.0.0.1 白名单)、保证插件优先级高于下游消费插件、按需定制scene.prompt与scene.category。如需深入实现细节,可继续阅读 main.go 与 main_test.go,或参考 Rust 版实现 plugins/wasm-rust/example/ai-intent/src/lib.rs。
【免费下载链接】higress🤖 AI Gateway | AI Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/hi/higress
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考