外观
语义理解(ruoyi-aihuman-nlu)
模块:
ruoyi-aihuman-nlu职责:把自然语言解析为结构化意图(如
module=hazard##type=dialog##status=当前隐患),驱动大屏弹框 / 地图动作。入口接口:
POST /digital-human/nlu/recognize→NluController→SiliconFlowNluService。
一、开发思路(为什么这么做)
NLU 不依赖「大模型直接理解一切」,而是**「关键词路由 + 小模型补全 + 代码层确定性兜底」三层架构,核心目标是让免费小模型也能稳定输出固定格式**,避免把弱模型的幻觉带到前端。
模块化路由,缩短 prompt
- 先用关键词把用户文本路由到具体模块(隐患 / 设备 / 施工 / 巡检 / 管线 / 管廊 / 漫游)。
- 命中模块时,只把
common公共头部 + 该模块专有提示词发给模型;未命中才用common+ 全部模块(兜底全量)。 - 效果:单模块 prompt 短,小模型的格式准确率显著提升;命中模块但格式异常时,自动用全量 prompt 重试一次。
把确定性规则从 prompt 搬到代码
- 弱模型对「声明性排除规则 / 优先级」遵循能力很差(例如把「巡检人员数量」误判为巡检状态统计)。
- 因此把高频、易错的固定问法(等级 / 分类 / 作业票 / 详情 / 排除 / 精确匹配)下沉为接口层确定性拦截:命中即直接返回模板,连模型都不调用,从根上消除误判。
输出再经代码层强校正
- 模型返回后,还要做分隔符修复、模板强制校正、枚举值合法性校验(编辑距离纠正),确保落到前端的字段值与提示词逐字一致。
二、处理流程(接口层 recognize 顺序)
SiliconFlowNluService.recognize 按以下顺序短路返回,越靠前越确定、越不走模型:
| 顺序 | 拦截 / 处理 | 说明 |
|---|---|---|
| 1 | 空文本检查 | 文本为空 → 报错 |
| 2 | 模块路由 moduleRouter.route(text) | 关键词命中模块名,供后续拼接短 prompt |
| 3 | 排除规则 matchesExclude | 命中模块排除词(如「巡检人员数量」)→ 直接返回 null,不走模型 |
| 4 | gallery 纯管廊判定 | 仅提「管廊」且无讲解意图 → 直接 module=gallery |
| 5 | 查看动词前置门禁 isViewVerbMissing | 不含「查看/查询」类动词、也不含 gallery/roam 豁免词 → 直接 null(总开关,viewVerbGate) |
| 6 | 详情问法拦截 | 含「详情」类词 + 编号/名称参数 → 直接返回 detail 弹窗模板(hazard→pipeLine→device→construct→inspection 依次仲裁) |
| 7 | 等级 / 分类 / 作业票 / 设备分类拦截 | 固定词命中即返回对应地图模板(不经过模型) |
| 8 | 精确匹配快路径 | 已知输入 → 直接返回模板,跳过模型 |
| 9 | 调用大模型 + 输出校正 | 以上均未命中才调模型,再做 normalizeOutput → enforceTemplate → sanitizeEnumValues |
| 10 | 兜底重试 | 命中了模块但返回格式异常(缺 module=)→ 用全量 prompt 重试一次 |
设计取舍:确定性优先于模型。能用规则兜住的,绝不交给模型,既快又稳。
三、模块路由(按关键词匹配模块)
ModuleRouter 按 aihuman-nlu-prompt.yml 里 modules 的定义顺序(LinkedHashMap 保证)依次匹配,首个命中即返回,因此模块顺序即优先级:
hazard(最高)> device > construct > inspection > pipeLine > gallery > roam(最低)每个模块在 yml 中配 keywords 关键词列表,文本 contains 任一关键词即命中该模块。例如:
| 模块 | 关键词(示例) |
|---|---|
hazard | 隐患、缺陷 |
device | 设备、在线、离线、报警、视频、监控、气体、位移、枪机、球机、云台、可燃、有毒、沉降、倾斜、震动 |
construct | 作业票、作业单位、高处、动土、吊装、动火、射线、断路、临时用电、盲板、施工 |
inspection | 巡检、巡查 |
pipeLine | 管线、管道 |
gallery | 管廊(简介/介绍/讲解类触发) |
roam | 漫游、三维场景(漫游类触发) |
四、提示词约束(common 公共头部铁律)
提示词在 aihuman-nlu-prompt.yml,结构为 common(公共头部)+ modules(各模块关键词与专有提示词)。common 规定的最高考级约束:
- 输出格式:仅输出单行
key=value##key=value,字段间用##分隔,结尾无额外符号;禁止用-、|、=、逗号、括号等替代##。 - 固定 key:
module、type、value、mapType、category1、category2、categoryName、levelName、status、jobName、searchCode、searchName;每个 key 只允许出现一次,禁止把值当 key。 - 无匹配输出
null:不输出answer自然语言、不换行 / 注释 / JSON / 闲聊。 - 首字段恒为
module=,type只能取map(地图)或dialog(弹框);module与type不可混淆。 - 值字段是预定义枚举:
status / categoryName / levelName / jobName / category1 / category2 / mapType必须从各模块固定枚举中选取,严禁把用户原话当作值输出(这是语义意图匹配,不是关键词字面匹配)。 - 查看动词前置:除
gallery、roam外,所有模块的弹框 / 地图都只有用户输入显式含「查看/查询」类动词才触发;动词不得写入任何值字段。 - 全局忽略词:数量词(多少 / 几个 / 总数…)、企业名称(xxx公司/集团)、时间词一律忽略,不写入任何字段。
- 人员统计类一律 null:「巡检人员 / 巡查人员 / 巡检员」等 →
null,严禁映射到任何status。 - 能力询问一律 null:「你能干什么 / 你是谁」等 →
null,不归入 gallery intro。
五、输出后处理(代码层强校正)
模型返回后,SiliconFlowNluService 做三道校正,不依赖模型自觉:
normalizeOutput:把误用的非法分隔符(、=、①、|等)统一修回##;category=修回category1=;补全漏写的module=前缀(用模板查找表反查)。enforceTemplate:若输出结构化字段能匹配提示词里的模板,直接替换为模板的精确输出,确保字段完整且与提示词逐字一致。sanitizeEnumValues:某字段值不在合法枚举集合时,按 Levenshtein 编辑距离纠正到最接近的合法值(阈值 = 非法值长度一半),解决弱模型生造非法值(如「未投停」)。
六、配置项
顶层属性(aihuman.nlu,NluProperties):
| 配置项 | 默认值 | 说明 |
|---|---|---|
aihuman.nlu.provider | zhipu | NLU 服务商:zhipu(智谱 GLM)或 siliconflow(硅基流动) |
aihuman.nlu.view-verb-gate | true | 是否启用「查看动词前置」确定性拦截;false 回退为旧版仅按关键词匹配 |
aihuman.nlu.view-verbs | 空 | 额外的查看类动词,与代码内置列表合并去重 |
服务商配置(NluConfig / ZhipuNluConfig):
| 配置项 | 说明 |
|---|---|
aihuman.nlu.siliconflow.api-key | 硅基流动 API Key(与 ASR 可共用) |
aihuman.nlu.siliconflow.api-url / model / max-tokens / temperature | 接口地址、模型名、生成上限、温度 |
aihuman.nlu.zhipu.api-key / api-url / model | 智谱配置(默认模型 glm-4-flash-250414) |
aihuman.nlu.prompt-config-location | 提示词 YAML 位置(默认 classpath:aihuman-nlu-prompt.yml) |
七、提示词文件结构(aihuman-nlu-prompt.yml)
yaml
common: | # 公共头部:格式规则、查看动词前置、忽略词、排除、自检清单
【格式·最高优先级】仅输出单行 key=value##key=value ...
...
modules: # 模块定义顺序即路由优先级
hazard:
keywords: [隐患, 缺陷]
prompt: | # 该模块专有提示词 + 模板行("输入 -> module=...##...")
device:
keywords: [设备, 在线, 离线, 报警, 视频, ...]
prompt: |
...新增模块只需在此追加
模块名: keywords + prompt,并在SiliconFlowNluService的MODULE_NAMES、确定性拦截处按需扩展即可;新增「详情」类问法复用matchDetailTemplate委托。
八、接口与模型
| 接口 | 方法 | 说明 |
|---|---|---|
/digital-human/nlu/recognize | POST | NLU 语义识别,返回结构化意图 → NluController |
| 平台 | 模型 | 费用 | 配置 |
|---|---|---|---|
| 智谱 GLM(默认) | glm-4-flash-250414 | 免费 | aihuman.nlu.zhipu.api-key |
| 硅基流动(备选) | Qwen/Qwen2.5-7B-Instruct(默认);可换 deepseek-ai/DeepSeek-R1-0528-Qwen3-8B | 免费 | aihuman.nlu.siliconflow.api-key |
九、关键类
| 类 | 作用 |
|---|---|
NluController | 接收 /nlu/recognize 请求 |
SiliconFlowNluService | 识别主流程:路由 → 确定性拦截 → 调模型 → 输出校正 |
ModuleRouter | 按关键词 + 优先级路由到模块 |
NluConfig / ZhipuNluConfig | 硅基流动 / 智谱平台配置 |
NluProperties | provider / viewVerbGate / viewVerbs 等总开关 |
aihuman-nlu-prompt.yml | 提示词(common + modules) |
十、官方文档
- 智谱开放平台(API Key 获取):https://open.bigmodel.cn/
- 硅基流动(模型与 API):https://siliconflow.cn/