跳转到内容

语义理解(ruoyi-aihuman-nlu) ​

模块:ruoyi-aihuman-nlu

职责:把自然语言解析为结构化意图(如 module=hazard##type=dialog##status=当前隐患),驱动大屏弹框 / 地图动作。

入口接口:POST /digital-human/nlu/recognize → NluController → SiliconFlowNluService。


一、开发思路(为什么这么做) ​

NLU 不依赖「大模型直接理解一切」,而是**「关键词路由 + 小模型补全 + 代码层确定性兜底」三层架构,核心目标是让免费小模型也能稳定输出固定格式**,避免把弱模型的幻觉带到前端。

  1. 模块化路由,缩短 prompt

    • 先用关键词把用户文本路由到具体模块(隐患 / 设备 / 施工 / 巡检 / 管线 / 管廊 / 漫游)。
    • 命中模块时,只把 common 公共头部 + 该模块专有提示词发给模型;未命中才用 common + 全部模块(兜底全量)。
    • 效果:单模块 prompt 短,小模型的格式准确率显著提升;命中模块但格式异常时,自动用全量 prompt 重试一次。
  2. 把确定性规则从 prompt 搬到代码

    • 弱模型对「声明性排除规则 / 优先级」遵循能力很差(例如把「巡检人员数量」误判为巡检状态统计)。
    • 因此把高频、易错的固定问法(等级 / 分类 / 作业票 / 详情 / 排除 / 精确匹配)下沉为接口层确定性拦截:命中即直接返回模板,连模型都不调用,从根上消除误判。
  3. 输出再经代码层强校正

    • 模型返回后,还要做分隔符修复、模板强制校正、枚举值合法性校验(编辑距离纠正),确保落到前端的字段值与提示词逐字一致。

二、处理流程(接口层 recognize 顺序) ​

SiliconFlowNluService.recognize 按以下顺序短路返回,越靠前越确定、越不走模型:

顺序拦截 / 处理说明
1空文本检查文本为空 → 报错
2模块路由 moduleRouter.route(text)关键词命中模块名,供后续拼接短 prompt
3排除规则 matchesExclude命中模块排除词(如「巡检人员数量」)→ 直接返回 null,不走模型
4gallery 纯管廊判定仅提「管廊」且无讲解意图 → 直接 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 做三道校正,不依赖模型自觉:

  1. normalizeOutput:把误用的非法分隔符(、=、①、| 等)统一修回 ##;category= 修回 category1=;补全漏写的 module= 前缀(用模板查找表反查)。
  2. enforceTemplate:若输出结构化字段能匹配提示词里的模板,直接替换为模板的精确输出,确保字段完整且与提示词逐字一致。
  3. sanitizeEnumValues:某字段值不在合法枚举集合时,按 Levenshtein 编辑距离纠正到最接近的合法值(阈值 = 非法值长度一半),解决弱模型生造非法值(如「未投停」)。

六、配置项 ​

顶层属性(aihuman.nlu,NluProperties):

配置项默认值说明
aihuman.nlu.providerzhipuNLU 服务商:zhipu(智谱 GLM)或 siliconflow(硅基流动)
aihuman.nlu.view-verb-gatetrue是否启用「查看动词前置」确定性拦截;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/recognizePOSTNLU 语义识别,返回结构化意图 → 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硅基流动 / 智谱平台配置
NluPropertiesprovider / viewVerbGate / viewVerbs 等总开关
aihuman-nlu-prompt.yml提示词(common + modules)

十、官方文档 ​

数字人「小慧」项目文档