# intentquery **Repository Path**: dependmyse/intentquery ## Basic Information - **Project Name**: intentquery - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-11-03 - **Last Updated**: 2026-11-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # OntoQuery · 零售金融意图识别本体 把「用户问什么 → 落到哪个技能 → 还缺哪些槽位」这套判定知识,做成一份可维护、可评测、可生长的本体。 系统只做到**结构化契约**(技能 / 槽位 / 输出形态 / 置信度 / 依据),取数、口径计算、派生、图表都不在这一层。大模型不参与判定主链路。 ![知识图谱分层架构:接入、判定、知识、治理、数据五层,底部为语义缺口闭环](docs/images/kg-architecture.svg) ## 一、为什么需要本体 银行问数场景有两个绕不开的难点: 1. **一个指标有很多种说法。** 「金葵花客群 AUM」在真实问句里会出现「金葵」「金葵3」「金葵M3」「金葵花3客群」等形式,还有一层口语黑话;词表里为此登记了 1859 条说法。 2. **技能之间有大量易混边界。** 158 个技能里,「AUM随心问」与「AUM随心问(客户经理)」只差一个观测视角;「【NL2SQL】产品经营数据问答」与「全产品交易金额随心问」在指标词上高度重叠,前者的 25 条问句里实测有 12 条被后者接走。 常见做法是把技能清单塞进提示词交给大模型判断。问题不在单次准不准,而在:边界靠模型自觉、改口径要重调提示词、改完有没有变好无法证明。 本项目的做法是把这些知识显式建成三套数据——**本体骨架**(说法到概念)、**技能契约**(意图、正例、反向边界、槽位绑定)、**取值与用语**(受控取值、触发线索、指标口径、黑话),判定走确定性打分,并把每一次判错变成建模待办。 ## 二、三条设计边界 | 边界 | 具体含义 | |---|---| | 只出判定,不取数 | 产出是 `RoutingContract`(技能编码、槽位、形态、置信、依据)。SQL 与结果集不在判定链路,`sql/SafeQueryExecutor` 仅服务对照演示 | | 大模型只在旁路 | 用途限于候选措辞、实体释义、缺口分类建议、对照试验;不可用时降级为规则兜底,绝不生成不存在的编码 | | 结论必须可归因 | 每一次路由都能追到命中的词条、模板、边界、归一前后对照;每个缺口都能追到来源与热度 | ## 三、判定链路 判定层八步依次为:**标签归一 → 词典识别 → 候选生成 → 形态判定 → 定向抽槽 → 完备与前置 → 裁决 → 结构化契约**。 其中第三步「候选生成」由八路证据加权融合,权重为初值、按标注语料回归后确定: | 证据 | 权重 | 取值方式 | |---|---|---| | 问法正样本 | 0.45 | 最长命中说法 × 覆盖率 × IDF | | 触发模板 | 0.35 | 固定片段按序命中率,片段越短越低权 | | 归一化实体回连 | 0.32 | 口语碎片还原为标准概念后是否属本意图口径 | | 字面兜底 | 0.30 | 字符 bigram + IDF + 饱和收敛 + 文档长度归一 | | 指标词 | ≤0.18 | 每词 0.06,通用词按 IDF 压低 | | 意图名字面 | 0.15 | 剥离占位符后整名匹配 | | 主体视角 | 0.12 | 「我的 / 名下 / 管户」与意图观测主体是否一致 | | 领域词 | 0.05 | `domain_raw` 字面命中 | 八路相加后封顶 1.0,再乘两道折扣:同族视角不符 ×0.55 或 ×0.80,命中反向边界 ×0.35(改派)或 ×0.25(排除),最后排序取前五。**打分与评估是两件事**——打分是加权累加,MRR / Top-K 只是评测指标,不参与打分。 ## 四、本体规模(2026-10-27 实测) | 维度 | 规模 | |---|---| | 本体骨架 | 855 类 · 1238 实例 · 1859 说法 · 24 关系类型 · 4457 关系实例 | | 技能契约 | 158 意图 · 783 正例骨架 · 547 反向边界 · 169 前置条件 · 264 槽位绑定 · 455 易混对比 | | 取值与用语 | 78 槽位类型 · 663 受控取值 · 15 触发线索 · 500 指标口径 · 61 类黑话 | | 闭环账本 | 缺口 216 · 候选卡 25 · 上传候选对 906 · Badcase 117 · 变更集 51 | | 数据库 | 52 张表,其中本体表 36 张 | ## 五、页面 单页应用,15 个路由,菜单按三组排布: | 分组 | 页面 | 说明 | |---|---|---| | 智能问数 | 图谱Prompt `#/ontology-qa` | 问句经本体后如何组装成提示词,八步全可见 | | | 意图初筛 `#/traditional-qa` | 全量意图逐条打分的明细与依据 | | | 意图识别 `#/intent` | 技能目录、槽位绑定、边界与正例;识别即抽槽,多归属说法按绑定关系消歧 | | 本体中心 | 本体图谱 `#/graph` | 类树、实例、说法、关系;支持定位与搜索 | | | 槽位抽取 `#/slot-extract` | 无意图前提的全局抽取,对照本体能力面与契约缺口 | | | 本体运维 `#/ops` | 实例 / 同义词 / 映射编辑,热加载、变更历史、按批回退、覆盖扫描 | | | 规则库维护 `#/corpus` | 上传「意图 + 语料」,与现有规则库比对出五类建议卡,人工裁决后回写并重载 | | | 概念缺口 · 缺口池 `#/gap-pool` | 按说法与类型去重、按热度排序的建模待办与 AI 候选卡 | | | 概念缺口 · 离线挖掘 `#/gap-upload` | 上传问句语料,配对相似说法并确认入池 | | | 语义漂移 `#/drift` | 术语分叉与口径演进预警 | | 应用场景落地 | 归一化验证 `#/normalization` | 文本归一、术语改写、槽位抽取逐项对照 | | | 意图初筛应用 `#/comparison` | 跑批任务、双口径指标、分意图拆解、历史回看 | | | Badcase 归因 `#/badcase` | 判错逐条归因,产出建模建议 | 另有两个路由不在菜单里:对比问答 `#/compare`(首访与未知 hash 的落点,同一问句两条路线并排对照)、Prompt 工坊 `#/prompt`(手改提示词并在线比对,路由保留、菜单已收起)。 ## 六、技术栈 Java 21(源码级;本机 JDK 17 可用 `-Dmaven.compiler.release=17` 编译)+ Spring Boot 3.5.16 + MySQL 8 + 原生 ES module 前端(零构建)。 依赖只有四个:`spring-boot-starter-web`、`spring-boot-starter-jdbc`、`mysql-connector-j`、`icu4j`(汉字转写,与建模脚本的拼音口径一致)。不用 JPA、不用 Lombok、没有 Elasticsearch、没有向量库——判定全程只读内存中的本体模型。 ## 七、快速开始 ### 环境 - MySQL 8(默认连接 `root/123456@127.0.0.1:3306`,可用 `ONTOQUERY_DB_USERNAME` / `ONTOQUERY_DB_PASSWORD` 覆盖) - JDK 21 与 Maven 3.9+(JDK 17 需加 `-Dmaven.compiler.release=17`) - 大模型密钥(可选,只影响旁路能力;未配置时自动降级为规则兜底) ### 初始化数据库 仓库 `src/main/resources/db/` 下的脚本按序执行: ```bash M="mysql -uroot -p123456 --default-character-set=utf8mb4" $M < src/main/resources/db/ontoquery.sql # 基线:54 张表 + 零售金融全量数据(改造前的单租户快照) $M < src/main/resources/db/20_multi_tenant.sql # 多租户迁移:补 app_id、重建唯一键、建产品表 ont_app $M < src/main/resources/db/27_intent_pattern_source.sql # 问法与边界补 source 列(缺口采纳依赖它) $M < src/main/resources/db/26_95555_ontology.sql # 可选:95555 产品的意图树、槽位与示例问 $M < src/main/resources/db/28_corpus_rule.sql # 规则库维护:上传批次与建议卡两张表 $M < src/main/resources/db/32_dim_nodes.sql # 补建 DIM_* 维度节点(结构体检「关系边端点悬空」) ``` `32_dim_nodes.sql` 只补数据不建表:`REL_SLOTVALUE_OF_DIMENSION` 的客体侧指向 `DIM_*` 编码, 其中 56 个维度过去只有取值域类 `CLS_LEX_*`、没有 `DIM_*` 节点,体检会报 578 条悬空端点。 脚本按 `ont_slot_type` 幂等补齐,重跑无副作用;跑完调一次 `POST /api/ontology/admin/reload`。 整库快照 `ontoquery.sql` 里已经带上这 56 行,直接导快照的不需要再执行。 四点注意: - 基线 dump 是**改造前**的快照(没有任何 `app_id` 列),必须再跑一次迁移脚本才是多租户形态。两步都幂等:迁移用 `information_schema` 判列判索引,重复执行不会重复加列,可随时补跑。 - 若从更早版本重导本体(`06_intent_ontology.sql` 那套脚本已不在当前仓库,仅旧分支保留),会清空人工采纳的知识(页面采纳的说法、边界、候选卡随之丢失),且与医疗演示脚本 `02_ontology_seed.sql` 互斥。对外演示前先备份,重导后必须调用 `POST /api/ontology/admin/reload`。 - 迁移会把 `ont_entity_desc` 的主键由业务码 `code` 换成自增 `id`、唯一键改为 `(app_id, code)`——业务码不再天然唯一,这是「同一编码可被多个产品各自持有」的前提。 - `27_intent_pattern_source.sql` 给 `ont_intent_pattern` / `ont_intent_boundary` 补 `source` 列:缺口页采纳「意图正例 / 反例」时按来源精确删除撤销,缺这一列会在采纳时报 `Unknown column 'source'`。脚本用 `information_schema` 判列,幂等可重跑。 - `28_corpus_rule.sql` 建 `ont_corpus_batch` / `ont_corpus_card`:规则库维护页的批次摘要与建议卡。卡上的 `status`(PENDING / ACCEPTED / IGNORED)是裁决状态的唯一来源,采纳后写的是 `ont_synonym` 与 `ont_intent_pattern`,不在这里存规则副本。 ### 构建与启动 ```bash mvn -q package mvn spring-boot:run # 端口 8080 ``` 打开 ,从「对比问答」或「意图初筛」页开始: > 近一个月金葵花客群 AUM 大于 50 万的客户有多少人 8080 被占用时在 `local.yml` 覆盖 `server.port`。 ## 八、常用接口 | 域 | 方法 | 路径 | |---|---|---| | 判定 | POST | `/api/intent/resolve`、`/api/intent/route`、`/api/intent/contract/meta` | | | GET | `/api/intent/list`、`/{code}`、`/domains`、`/meta`、`/eval/run` | | Prompt 工坊 | POST | `/api/prompt/auto`、`/build`、`/normalize` | | | GET | `/api/prompt/normalization` | | 本体浏览 | GET | `/api/ontology/tree`、`/graph`、`/search`、`/facts`、`/entity/{code}` | | | POST | `/api/ontology/entity/{code}/desc` | | 本体运维 | GET | `/api/ontology/admin/instances`、`/classes`、`/entries`、`/changes`、`/coverage` | | | POST | `/api/ontology/admin/instance`、`/instance/update`、`/instance/delete`、`/synonym/add`、`/synonym/delete`、`/mapping/update`、`/reload`、`/revert` | | 缺口闭环 | GET | `/api/gap/board`、`/stats`、`/classify`、`/targets`、`/fake-closures`、`/proposals`、`/runs`、`/upload/batches`、`/upload/pairs`、`/badcase/runs`、`/badcase/items`、`/badcase/advice` | | | POST | `/api/gap/record`、`/dismiss`、`/undismiss`、`/adopt/class`、`/adopt/term`、`/adopt/positive`、`/adopt/negative`、`/revoke`、`/verify`、`/proposals/run`、`/proposals/adopt`、`/proposals/reject`、`/upload/analyze`、`/upload/confirm`、`/badcase/analyze`、`/badcase/advice/adopt`、`/badcase/advice/reject`、`/badcase/adopt` | | 语义漂移 | GET | `/api/drift/scan` | | 规则库维护 | POST | `/api/corpus/analyze`(multipart 上传)、`/api/corpus/decide` | | | GET | `/api/corpus/batches`、`/api/corpus/cards` | | 评测 | POST | `/api/batch/upload`、`/api/batch/builtin`、`/api/batch/sweep`、`/api/query/ablation`、`/api/benchmark/run` | | | GET | `/api/batch/tasks`、`/api/batch/task/{taskId}`、`/api/batch/builtin/available`、`/api/benchmark/latest` | | 系统 | GET | `/api/meta/tables`、`/api/meta/llm-status`;POST `/api/meta/llm-cache/clear` | | 产品(租户) | GET | `/api/apps` | | | POST | `/api/apps`、`/api/apps/update`、`/api/apps/delete` | 除 `/api/apps` 与静态资源外,所有接口都按请求头 `X-App-Id`(或查询参数 `appId`)分租户;缺省取默认产品 `qbot`。 字段级契约见 [docs/CONTRACT.md](docs/CONTRACT.md)。 ## 九、目录结构 ``` src/main/java/com/ontoquery/ ├── ontology/ 本体模型与加载、词典 NER、推理;ops/ 写操作与审计、覆盖扫描;trace/ 决策轨迹 ├── dst/ DST 判定:技能契约路由、槽位填充、契约对象、CQ 回归 ├── intent/ 候选初筛:八路证据打分(IntentRouter) ├── prompt/ 提示词工坊:归一化、术语改写、知识注入与组装 ├── gap/ 缺口闭环:留痕、候选生成、上传挖掘、相似度 ├── corpus/ 规则库维护:语料与现有规则比对、建议卡、采纳回写 ├── badcase/ 判错归因与建模建议 ├── drift/ 语义漂移:术语分叉与口径演进 ├── batch/ 跑批:任务、指标、融合排序、历史 ├── ablation/ 对照试验:只给技能名的字面基线 ├── tradnl/ 对照侧:BM25 与模式链接(不参与判定主链路) ├── compare/ 对比问答:双侧轨迹并行与逐段对照 ├── benchmark/ 基准跑批:固定集上的回归结果留存 ├── llm/ 大模型旁路:客户端、缓存、降级桩、SQL 抽取 ├── risk/ SQL 风险检查(对照侧演示用) ├── sql/ 唯一 SQL 出口与结果集 ├── web/ 控制器、请求对象、全局异常 ├── config/ 配置属性 └── support/ 归一化、置信度、证据、共享 DTO src/main/resources/ ├── db/ 01–28 号脚本(无 05):建表、意图本体、金融业务库、缺口闭环、评测、Badcase、规则库 ├── eval/ cq.json(20 条 CQ 回归集) ├── static/ 前端 SPA(16 页 + 2 个隐藏路由,原生 ES module,无构建) └── application.yml ``` ## 十、评测与回归 评测分两层,不能混用: | 层 | 样本 | 用途 | 入口 | |---|---|---|---| | CQ 回归 | 20 条,八类场景 | 门禁:改本体、改权重后必跑 | `GET /api/intent/eval/run` | | 跑批 | 557 条内置集,可上传 | 能力对比与归因 | `POST /api/batch/builtin` | CQ 基线:技能 Top1 0.90、Joint 0.90、槽位 F1 1.00、输出形态 0.93。拒识仅 0.50 而样本只有 2 条,不作能力结论。 跑批结果(557 条,`arm` 为本体路): | 对照口径 | 对照组可用的信息 | arm Top1 | ctrl Top1 | 差值 | |---|---|---|---|---| | `full` | 技能名 + 本体收录说法 | 69.7% | 68.4% | +1.3pp(p=0.61,不显著) | | `name` | 只有技能名 | 69.7% | 53.9% | +15.8pp [10.8, 20.8] | **对外引用必须声明口径。** Top1 的差值完全取决于对照组能否拿到词表:给对照组同样的说法,两者在排序上统计不可区分。本体更稳的证据在别处——`name` 口径下 MRR 0.933 对 0.565、置信度校准 ECE 0.106 对 0.355、零候选漏召 1 条对 59 条、结论稳定性 70.9% 对 63.0%。完整口径与算法见 [docs/本体价值度量指标.md](docs/本体价值度量指标.md)。 已知局限(均为本体缺口,不是链路缺陷): - CQ 集里域外拒识只有 2 条,0.50 的拒识准确率不具备统计意义,不足以下能力结论。 - 三处意图边界尚未登记,导致稳定误判:产品经营数据问答 ↔ 全产品交易金额随心问、厅堂新增零售有效客户数 ↔ 客群随心问、养老金缴存情况(边界只登记单向)。补齐边界后整体 Top1 约可再提 8 个百分点。 - 缺口池现存 216 条中有 160 条是全站闸门上线前积下的功能词噪声,对外举例只用闸门之后入池的记录。 复跑命令(需服务已在 8080 运行、Node 18+;服务在别的端口时用 `BASE=http://127.0.0.1:18084` 指定): ```bash node tools/eval-ontology-value.mjs # 双口径指标 + 漏召 + 稳定性 + ECE 分桶 node tools/estimate-lift.mjs # McNemar 配对检验 + 95% 置信区间 + 分意图拆解 ``` ## 十一、多租户与数据隔离 一套代码、一个库,按产品(`app_id`)分区。改造前的全部数据归默认产品 `qbot`,页面左上角 logo 下方是切换器。 - **请求带租户**:前端在 `api.js` 的 `request()` 统一注入 `X-App-Id`,页面代码不必逐处传参;选择写 `localStorage['ontoquery.appId']`,切换后整页重挂载。 - **后端判定**:`TenantFilter` 取值顺序为 `X-App-Id` 请求头 → `appId` 查询参数 → 默认产品。非法或已停用的标识**直接拒绝**(`A0400` / `A0403`),不静默回落——静默回落会让页面看起来「有数据」,实际已经串了产品。`/api/apps` 与静态资源放行,否则本地存的标识被停用后连切回去的入口都没有。 - **隔离范围**:37 张本体 / 意图 / 缺口 / 评测 / 缓存表加 `app_id`,唯一约束一律改为「租户内唯一」(`app_id` 放索引首列);本体模型按产品分别缓存,一个产品热加载不影响另一个。 - **刻意不隔离**:`dim_*` / `fact_*` 是外部业务库语义(组织、产品、客户、交易事实),属公共维度,不随产品分叉。 - **新增产品只登记一行**:不复制任何数据,本体从空开始;切换过去看到的是 0 个意图、0 个类,随缺口池与运维页逐步长出来。删除产品只删 `ont_app` 登记,业务数据保留。 ### 已灌入数据的产品 | 产品 | 类 | 意图 | 槽位类型 | 规范取值 | 说法 | 示例问 | 来源 | |---|---|---|---|---|---|---|---| | `qbot` | 855 | 158 | 78 | 663 | 1859 | 0 | 零售金融《意图维度标签》+ 业务枚举字典(`06_intent_ontology.sql`) | | `95555` | 195 | 485 | 49 | 128 | 1564 | 418 | 语音助手意图树 + 槽位专表 + 意图槽位绑定表(`26_95555_ontology.sql`) | 95555 是电话银行场景(用户致电 95555 咨询),因此「说法」分两层:**槽位取值说法**(2456 条源表说法,去重后 1564 条进 `ont_synonym`)与**意图示例问**(418 条按 13 个场景编写的电话口吻整句,进 `ont_intent_pattern`)。 三份源表放项目根目录,示例问放 `tools/data/95555_examples.csv`;改完重跑脚本即可,不要手改 SQL: ```bash python tools/build_95555_ontology.py # 生成 src/main/resources/db/26_95555_ontology.sql mysql -uroot -p123456 --default-character-set=utf8mb4 ontoquery < src/main/resources/db/26_95555_ontology.sql curl -X POST localhost:8080/api/ontology/admin/reload -H 'X-App-Id: 95555' ``` 槽位专表的关键是**一个取值有多个说法**:`公司客户 / 公司的 / 公司 / 企业` 都是「公司账户」。落库分三处—— `ont_slot_value`(说法进 `alt_labels`,线上提槽只认这张表)、类树(取值域 `CLS_LEX_*` → 取值类 `CLS_SV_*` → 说法词条 `TERM_*`)、`ont_synonym`(取值类的等价说法,供词典识别归一)。三处少一处,说法就抽不到或归不回规范值。 两条容易写错的口径: - **意图标准名不进 `ont_synonym`**。名字由 `DictionaryNer` 的实例名词条(1.0 分)与 `IntentRouter` 的 NAME 通道承担;再登记一份同义词,同一个字面会在 SYNONYM 与 NAME 两路各算一次,短名意图凭空多出约 0.18 分,「贷款还款」于是压过「贷款还款宽限期」。qbot 的同义词表同样不含实例名。 - **`ont_intent_binding.metric_terms` 是技能私有指标词表**,不是「关键要求」的容器。把关键要求塞进这一列会强加一条「必须命中 METRIC」的前置条件,满足度归零后输给不声明任何槽位的意图。关键要求写 `ont_instance.remark`。 ## 十二、文档 | 文档 | 说明 | |---|---| | [总体设计说明](docs/system-design.md) | 架构、数据模型、判定链路、增长闭环、运维、API 与规约 | | [数据库表说明](docs/db-tables.md) | 54 张表的含义与存储内容、五条主线的关系图、46 条逻辑关联、现状口径上的坑 | | [意图识别本体](docs/intent-ontology.md) | 类树结构、编码约定、06 号脚本的生成方式 | | [创新说明](docs/innovation.md) | 本体 / 负例 / 闭环三条主张 | | [概念缺口 · 本体增长回路](docs/concept-gap.md) | 采集、池化、AI 候选生成六步、人工裁决、闭环验证 | | [对照试验](docs/ablation.md) | 本体增强与字面基线的对称管线 | | [本体价值度量指标](docs/本体价值度量指标.md) | 双口径、MRR、ECE、漏召、稳定性与显著性判定 | | [初筛调参记录](docs/intent-routing-tuning.md) | 八路证据权重的回归依据 | | [融合排序](docs/fusion-ranking.md) | 本体路与字面路的融合实验 | | [语义漂移检测](docs/semantic-drift.md) | 术语分叉与口径演进 | | [API 契约](docs/CONTRACT.md) | 控制器与前端共守的字段级契约 | | [操作手册](docs/manual.md) | 环境搭建、演示路径、实测记录 | | [演示讲解词](docs/demo-script.md) | 演示流程讲稿与 FAQ | | [原型与页面说明](docs/prototype.md) | SPA 页面结构 | | [能力问题集](docs/competency-questions.md) | 20 条 CQ 的设计意图 | | [方案设计](docs/design.md) | 早期本体建模与双管线设计(历史文档) | ## 十三、开发规约 - 全站禁用 emoji,图标一律内联 SVG;前端文案用书面语。 - 遵守《Java 开发手册(嵩山版)》强制条目:命名、包装类型、日志、并发、建表规约、参数绑定。 - LLM 密钥不入仓库:`local.yml`(已忽略)或环境变量 `ONTOQUERY_LLM_API_KEY`。 - 所有 SQL 只经 `SafeQueryExecutor` 白名单执行(单条 SELECT、10 秒超时、500 行上限)。 - 本体写操作全部参数绑定并落 `ont_change_log`,支持按批回退;写库后必须 reload,否则下次判定仍读旧模型。 - 新增本体 / 意图 / 缺口 / 评测 / 缓存相关的表与查询,一律带 `app_id` 条件,新建唯一键以 `app_id` 为首列;只有 `dim_*` / `fact_*` 这类外部业务库语义的公共维度例外。漏写不会报错,只会静默跨产品串数据。 - 前端改动需同步推进 `js/app.js` 的 `ASSET_VERSION` 与 `index.html` 的 `?v=`,并同步到 `target/classes/static/`(IDEA 起服务时不再自动拷贝)。