WorkBuddy 加持,一天上线八表关联智能问数

前言

本文完整记录了一次真实的智能问数(润乾 NLQ)上线全过程——从环境准备到最终验证通过,实际耗时约一个工作日。我们使用 WorkBuddy 作为“执行 Agent”,直接操作本机润乾 DQL/NLQ 引擎与 HSQLDB 中的 TPCH 数据集,从元数据生成、汉语词典构建、LLM 提示词编写、数据源配置到最终 Web 查询页面,一路打通。最终,像“各地区的订单金额总和”“1996 年每月的订单数”这类自然语言问题,均成功返回真实查询结果。

与常见的单表 Demo 不同,TPCH 的多表外键穿越(订单→客户→国家→地区,四层关联)是智能问数应用中的难点。本文会剖析这类跨表关联在语义层中的建模方法,并展示润乾 NLQ 在处理复杂 JOIN 上的天然优势。

当前 Demo 尚未接入用户角色与数据权限,但润乾 DQL 内核层已内置行 / 列级权限控制能力,可通过关联表作为条件进行细粒度过滤,后续配上角色即可平滑扩展。本文重在展示核心链路的搭建方法,权限部分将在后续文章中补充。..

..

..

最省事的读法:直接跳到第九节,复制一段总提示词给 WorkBuddy,就能复现整个过程。

项目

内容

目标读者

熟悉 SQL 、想要快速落地 NLQ 智能问数 Demo 的后端 / 数据 / BI 工程师

前置知识

SQL 基础;对外键、主子表、维表与事实表有基本概念

技术栈版本

润乾 DQL/NLQ (授权)、 HSQLDB 2.7.3 、 TPCH 标准数据集

预计阅读时间

约 15 分钟

一、为什么是润乾 NLQ,又为什么用 WorkBuddy

"智能问数"的目标是让业务人员用大白话提问,系统自动查出数据。市面上的方案大多非常繁重,不仅需要专业的 AI 团队,做模型训练、语义理解等工作常常要花费数周到数月的时间,完全达不到快速验证智能问数的目标。

润乾走的是另一条路——两段式 NLQ

1. 第一段,大模型(LLM)把自然语言翻译成一种规范查询文本

2. 第二段,润乾引擎解析规范文本、执行查询并返回结果。规范文本是结构化、受限的,SQL 是引擎按元数据自动生成的,所以既跑得通,又能保证 100% 准确率。

WorkBuddy 在这里扮演什么角色?相较于靠人机对话一步步操作的对话式 AI 只能"出主意",不能"动手"WorkBuddy Agent 形态能直接读本机文件、连接数据库读表结构、写配置文件、编译运行 Java 验证程序、把跑通的产物回填,让用户快速搭建智能问数应用,一天搞定。

二、方案架构与成果物全景

一次完整的润乾 NLQ 上线,产出物可以归纳为 6 类文件,它们的分工如下表:

产物

作用

wb_tpch.lmd

DQL 元数据:物理表结构 + 外键关系 + 维表定义

语义层

wb_tpch.nlq

汉语词典:词表 + 字段视图 + 维 / 常数词 + 聚合词

语义层

wb_tpch_nlq_nspl.md

LLM 提示词:自然语言 → 规范查询文本的翻译规则

大模型层

raqsoftConfig.xml

注册 DQL / NLQ 两个数据源(驱动 + URL )

配置层

nlqConfig.xml

把 NLQ 实例 wb_tpch 绑定到数据源与元数据

配置层

wb_tpch.jsp

Web 查询页面(业务人员直接提问的入口)

应用层

再加一个 VerifyNLQ.java,它是真连真查的验证程序——不是"看起来对",而是真的走 JDBC 驱动把 DQL NLQ 都跑一遍、打印真实返回。整个链路是:

用户自然语言 → LLM( 提示词 .md → 规范查询文本
→ NLQ 引擎 (com.nlq.datalogic.jdbc.NLQDriver) → MQL 语义查询
→ DQL 引擎 (com.datalogic.jdbc.LogicDriver + 元数据 .lmd) → SQL
→ HSQLDB(TPCH 8 表) → 真实结果

三、TPCH 数据集:多表关联场景

本文选了 TPCH,它是 TPC 基准测试的标准决策支持数据集,典型的星型/雪花关联结构,8 张表:

中文

行数(本机实测)

角色

REGION

地区

5

维度(顶层)

NATION

国家

25

维度

SUPPLIER

供应商

10000

维度

CUSTOMER

客户

11572

维度

PART

零件

10

维度

PARTSUPP

零件供应

159750

事实(子)

ORDERS

订单

12380

事实(主)

LINEITEM

订单明细

59206

事实(子)

它们之间的外键关系构成一个典型的多层关联网络:

· 订单客户国家地区(四层穿越

· 订单明细订单、零件、供应商

· 供应(PARTSUPP) → 零件、供应商

· 供应商国家地区

多表关联给 NLQ 带来三个比单表难得多的问题:

1. 外键穿越:业务问"各地区订单金额",数据实际在订单表,但"地区"要沿 订单. 客户编码 → 客户. 国家编码 → 国家. 地区编码 → 地区. 地区名称 连续跳 4 层才能拿到。词典必须把这条"路径"精确描述清楚,少一跳、跳错一步就查不出。

2. 主子表:订单(主)和订单明细(子)是一对多。问"各客户订购金额"要落到明细表聚合,问"各客户订单数"又要落到订单表聚合——同一个"客户"维度,不同指标对应不同事实表,词典要能区分。

3. 跨表聚合:多个事实表都要"按同一维对齐汇总"(例如按地区同时看订单金额与供应成本),这是润乾 NLQ 里专门的查询范式之一。

四、环境准备

说明

润乾报表

运行实例 D:/soft/raqsoft( Tomcat 6868 + HSQLDB 9001 )

授权文件

润乾报表开发授权.xml

数据库

HSQLDB , jdbc:hsqldb:hsql://127.0.0.1/tpch ,用户 sa 空密码, db.type=13

驱动 jar

D:/soft/common/jdbc/hsqldb-2.7.3-jdk8.jar

产品 lib

…/demo/WEB-INF/lib/ ( datalogic.jar 、 nlq.jar 等)

五、实施步骤(6 步)

下面是完整实施路径,每一步的产出都真实落盘。为了让读者能复现,我把每一步的具体要求做成了提示词(见第九节),WorkBuddy 会按序执行。

1. 生成元数据 wb_tpch.lmd:连库读 8 张表真实结构,生成 DQL 元数据 JSON(顶层 {tableList, namedDimList})。物理表 type:0,含主键与 fkList;日期型字段建"日期"虚拟维,字符串枚举字段(订单状态、订单优先级、市场细分、制造商、品牌、零件类型、包装、退货标记、明细状态、运输说明、运输方式)建枚举虚拟维。

2. 生成汉语词典 wb_tpch.nlq:基于 lmd 生成 NLQ 词典,含全局词表 + dimConfigList + tableViewList8 张表的字段视图、字段簇、实体)。这是最考验细节的一步,详见第六节。

3. 配置数据源(两个 xml)raqsoftConfig.xml 注册 DQL4wbTPCHLogicDriver + lmd 路径)和 NLQ4wbTPCHNLQDriver + nlq 名)两个 DBnlqConfig.xml 增加 <NLQ name="wb_tpch"> 绑定。

4. 生成 LLM 提示词 wb_tpch_nlq_nspl.md:把词典里的表、字段、维词、常数词、聚合词,以及"自然语言规范文本"的翻译规则写成提示词,喂给任意大模型。

5. Web 上线 wb_tpch.jsp:复制自带 nlq.jsp,改 dataSource="NLQ4wbTPCH" 和标题,并修复 <dd_qq> 占位符还原问题。

6. 真连真查验证:写 JDBC 程序,分别用 DQL 驱动验证多表深路径聚合、用 NLQ 驱动验证汉语查询,打印真实结果。

六、深度解析:多表关联在词典里到底怎么写

相较于单表 Demo 抄一遍就能跑,多表关联则必须把下面几件事做对,任何一件错了都是"查不出""查错"

(1)元数据里的外键关系(fkList)是穿越的基石

.lmd 里每个物理表要声明外键,例如订单表 ORDERS 声明 fkList 指向 CUSTOMER(通过 O_CUSTKEY),客户表 CUSTOMER 再声明指向 NATION(通过 C_NATIONKEY),国家表 NATION 声明指向 REGION(通过 N_REGIONKEY)。引擎就是靠这一串 fkList "地区"这个顶层维度沿外键链自动连回订单表的。

(2)词典字段视图的 expStr 必须终止于 "外键引用字段(FK key)"

这是最容易踩的坑。.nlq tableViewList 里,每个字段视图用 expStr 表达取值路径,规则是 表名. 字段名 或 fkN. 字段名. 字段名…。路径必须终止于外键引用字段本身,而不是被引用表的名称字段。举两个实例:

· 国家名称:写 fk1.N_NATIONKEY(终止于外键 key),不能写成 fk1.N_NAME

· 客户地区:写 fk1.C_NATIONKEY.N_REGIONKEY,一路穿越到地区的外键 key

为什么?因为外键 key 才是引擎能按 fkList 继续往下穿越的锚点,直接写名称字段会把穿越链打断。

(3)dataType 两套编码,别混

.lmd 里字段类型用 java.sql.Types 编码(INTEGER=4VARCHAR=12DOUBLE=8DATE=91);而 .nlq 字段视图的 dataType 用的是另一套(数值=1、字符串=11、日期=8)。两处值不一样,照抄错误会直接导致类型解析失败。

(4)金额字段挂 unitName,字段簇只放字符串维度

给订单金额、订购金额等数值字段加 unitName:"元",这样"订单金额 大于 10万元"这类带单位的过滤才能被识别;fieldCluster 字段簇只放字符串维度字段,数值字段不进簇,否则簇词会错误指向含数值的簇。

(5)dimConfigList.dimName 必须等于 lmd 里的维表名

实体维(REGION/NATION 等)用物理表名,日期/枚举维用虚拟表名(如"日期""订单状态"),词典里的 dimName 要和 .lmd namedDimList 完全对齐,否则"按维分组"会匹配失败。

把这些做对之后,DQL 侧的深路径就能写出下面这种四层穿越查询:

SELECT ORDERS.O_CUSTKEY.C_NATIONKEY.N_REGIONKEY.R_NAME,
ORDERS.sum(O_TOTALPRICE)
FROM ORDERS
BY ORDERS.O_CUSTKEY.C_NATIONKEY.N_REGIONKEY.R_NAME

路径 O_CUSTKEY → C_NATIONKEY → N_REGIONKEY → R_NAME 从订单一路跳到地区,一次聚合就拿到"各地区订单金额"

七、成果物清单

文件

说明

位置

wb_tpch.lmd

DQL 元数据( 8 表 + 外键 + 日期 / 枚举虚拟维)

artifacts/

wb_tpch.nlq

汉语词典(词表 + 字段视图 + 维 / 常数词 + 聚合词)

artifacts/

wb_tpch_nlq_nspl.md

LLM 提示词(自然语言 → 规范文本)

artifacts/

raqsoftConfig.xml

DQL/NLQ 数据源配置( DQL4wbTPCH / NLQ4wbTPCH )

artifacts/

nlqConfig.xml

NLQ 实例配置( wb_tpch → 数据源 + 元数据)

artifacts/

wb_tpch.jsp

Web 查询页( dataSource=NLQ4wbTPCH )

artifacts/

VerifyNLQ.java

JDBC 真连真查验证程序

artifacts/

00-总提示词.md

一条搞定的总提示词(最小代价方案)

prompts/

01-05-分阶段提示词.md

分阶段提示词(备选)

prompts/

八、验证结果(真连真查,非纸上谈兵)

DQL 侧——多表深路径与日期分层:

查询

结果

订单全表 count(*) / sum(O_TOTALPRICE)

12380 单

各地区订单金额( 4 层穿越)

见下方 5 行

订单明细按订单日期 "年月" 分层聚合

正确分层

四层穿越返回的五个地区金额(真实查询结果):

地区 R_NAME

订单金额总和

AFRICA

3.7866×10⁸

EUROPE

3.8184×10⁸

AMERICA

3.7368×10⁸

ASIA

3.7534×10⁸

MIDDLE EAST

3.6470×10⁸

NLQ 侧——汉语查询全部命中并生成 MQL:

自然语言

生成的核心 MQL

各地区的订单金额总和

… ON REGION AS 地区 FROM ORDERS … BY 客户地区

1996 年每月的订单数

… ON FROM ORDERS WHERE 订单日期#=1996 BY 订单日期#

各品牌的订购金额总和

… ON 品牌 FROM LINEITEM … BY 品牌

各运输方式的订购金额总和

… ON 运输方式 FROM LINEITEM … BY 运输方式

注意这几条查询的"层次感":前两条都做了外键穿越("地区"从订单穿越三层、""来自订单明细穿越到订单的日期维),后两条则是明细表(LINEITEM)自身的枚举维(品牌、运输方式)。这说明词典既能处理跨表穿越,也能处理单表枚举维,多表关联的三种难点都验证通过了。

九、最小代价路径:一条提示词搞定

如果你已经有一套润乾 NLQ 环境,最省事的方式不是照着本文一步步敲,而是把 00- 总提示词.md 整段复制给 WorkBuddy(Agent 模式)。那段提示词里写好了:

· 完整的环境信息(安装目录、数据库 URL、驱动 jar、授权文件路径、8 张表、实例名 wb_tpch);

· 6 项按序执行的任务(lmd → nlq → xml 配置 → LLM 提示词 → jsp → 真连真查验证);

· 全部关键规则(外键关系、expStr 终止于 FK key、两套 dataTypeunitName、字段簇等,见第六节);

· 约束(加载 raqsoft-dql-lmd 经验、中文输出、每步产出文件路径 + 验证结果)。

WorkBuddy 会直接连库读表结构、生成元数据与词典、写配置、编译运行验证程序,把每一步的产出路径和真实验证结果报给你确认。你唯一要做的,是每步结束后简单确认一下。这就是"一天上线、最小代价"的含义——把重复的、容易错的细节固化进提示词,让 Agent 去执行,人只做判断

十、踩坑与经验总结

1. expStr 必须终止于 FK key:多表穿越最核心的坑,写错成名称字段会静默查不出,报错信息还不明显。务必写成 fk1.N_NATIONKEY 而非 fk1.N_NAME

2. 两套 dataType 编码lmd java.sql.Typesnlq 1/11/8,别照抄。

3. NLQ 需要 raqsoftConfig.xml:独立跑 NLQDriver 时会报"需要配置raqsoftConfig.xml",要在 nlqConfig.xml <NLQ> 节点里显式指定 <RaqsoftConfig> 指向正确的 raqsoftConfig.xml

4. jsp 的 <dd_qq> 占位符:从自带 nlq.jsp 复制改造成 wb_tpch.jsp 后,占位符还原需要三处 .replaceAll("<dd_qq>","\""),否则页面显示会带多余引号。

5. 验证要 "真连真查":不要只看配置对不对,要写 JDBC 程序走驱动真正查一遍、打印结果,才算上线。

总结

润乾 NLQ 的两段式设计,让"智能问数""大模型直接吐 SQL"的高风险路径,变成了"大模型翻译规范文本 + 引擎受控执行"的可控路径。智能问数的主要实施难点在语义建模——外键穿越、主子表、跨表聚合,这三件事的答案都在 .lmd fkList .nlq 的字段视图路径里。

借助 WorkBuddy 这种能"动手" Agent,可以把这一整套繁琐、易错的配置过程,收敛成一段可复用的提示词。人负责定义规则和做判断,Agent 负责连接、生成、验证——这才是"一天上线"背后真正省下的成本。如果你想在现有润乾环境上快速复现,就从 00- 总提示词.md 开始;如果你想理解为什么这样配,请回到第六节逐条对照 .lmd .nlq

附录:总提示词

# 总提示词(一条搞定,最小代价)

> 把下面整段话复制给 WorkBuddyAgent 模式)。WorkBuddy 会直接操作本机:连接数据库、读写文件、生成配置、跑通验证。你只需在每步产出后简单确认即可。

---

帮我基于润乾NLQ 上线一个智能问数 DEMO,数据集用 TPCHHSQLDB),是多表关联场景(比单表复杂,涉及外键穿越、主子表、跨表聚合)。

## 环境信息

- 安装根目录:`D:/soft/raqsoft`(运行实例),文件名/服务名统一。

- 数据库:HSQLDB`url=jdbc:hsqldb:hsql://127.0.0.1/tpch``driver=org.hsqldb.jdbcDriver``user=sa``password=`(空),`db.type=13`

- 底层驱动 jar`D:/soft/common/jdbc/hsqldb-2.7.3-jdk8.jar`

- 产品 lib`D:/soft/raqsoft/report/web/webapps/demo/WEB-INF/lib/`datalogic.jar / nlq.jar 等)。

- 授权文件:`D:\tools\professional\润乾报表研发人员内部专用授权20261231nlr.xml`

- 8 TPCH 表:REGIONNATIONSUPPLIERCUSTOMERPARTPARTSUPPORDERSLINEITEMDEMO schema)。

- 实例命名:`wb_tpch`lmd/nlq/jsp 统一用这个名)。

## 任务(按顺序全部完成)

1. **生成元数据 `wb_tpch.lmd`**:连接数据库读 8 张表的真实结构,生成 DQL 元数据(JSONUTF-8,顶层 `{tableList, namedDimList}`)。物理表 `type:0`,含主键与 `fkList`(外键关系:NATIONREGIONSUPPLIERNATIONCUSTOMERNATIONPARTSUPPPART/SUPPLIERORDERSCUSTOMERLINEITEMORDERS/PART/SUPPLIER)。日期型字段建「日期」虚拟维(`type:2`, dimType 5levelList 含年/季度///年月/月日/年季度/星期//年周),字符串枚举字段建枚举虚拟维(订单状态、订单优先级、市场细分、制造商、品牌、零件类型、包装、退货标记、明细状态、运输说明、运输方式)。字段 desc 用中文。输出到 `WEB-INF/files/dql/wb_tpch.lmd`

2. **生成汉语词典 `wb_tpch.nlq`**:基于上面的 lmd,生成 NLQ 词典(JSON)。要点(务必遵守,否则查不出):

- 全局词表(无效词/量纲/存在词/非法词/宏词/聚合词/连词/比较词)参考 demo 自带 `orders.nlq` 直接复用。

- `dimConfigList` `dimName` 必须等于 lmd 中维表名(实体维=物理表名如 REGION/NATION,日期/枚举维=虚拟表名如 日期/订单状态)。

- `tableViewList` 的字段视图 `expStr` 规则:`表名.字段名` `fkN.字段名.字段名…`**路径必须终止于外键引用字段(FK key**,例如国家名称=`fk1.N_NATIONKEY`(不是 `fk1.N_NAME`)、客户地区=`fk1.C_NATIONKEY.N_REGIONKEY`。原生主键字段不要加 `isFKCluster:true`

- 字段簇 `fieldCluster` 只放字符串维度字段,**数值字段不进簇**;簇词不指向含数值的簇。

- 字段视图 `dataType`:数值=1、字符串=11、日期=8(与 lmd 不同)。金额字段加 `unitName:""`

- 每张表配 `entityList` 实体(订单/订单明细/客户/供应商/零件/供应/国家/地区),多表外键字段用中文命名(如「客户国家」「客户地区」)。

- 输出到 `WEB-INF/files/dql/wb_tpch.nlq`

3. **配置数据源(xml**

- `WEB-INF/raqsoftConfig.xml` `DBList` 加两个 DB

- `DQL4wbTPCH``url=jdbc:datalogic://?lmd=<wb_tpch.lmd绝对路径>&db.url=jdbc:hsqldb:hsql://127.0.0.1/tpch&db.driver=org.hsqldb.jdbcDriver&db.user=sa&db.password=&db.type=13``driver=com.datalogic.jdbc.LogicDriver`type=0

- `NLQ4wbTPCH``url=jdbc:datalogic:nlq://?nlq=wb_tpch``driver=com.nlq.datalogic.jdbc.NLQDriver`type=0

- `WEB-INF/classes/nlqConfig.xml` `<NLQ name="wb_tpch"><DB>DQL4wbTPCH</DB><MetaData><wb_tpch.nlq绝对路径></MetaData><RaqsoftConfig></RaqsoftConfig></NLQ>`

4. **生成 LLM 提示词 `wb_tpch_nlq_nspl.md`**:基于 nlq 的词典,参考 `WEB-INF/classes/tpch_nlq_nspl.md` 结构,生成「自然语言→规范文本(nlq+nspl)」的提示词,词典部分与 nlq 完全一致(表/字段/维词/常数词/聚合词)。

5. **Web 上线**:复制 `raqsoft/dql/jsp/nlq.jsp` `raqsoft/dql/jsp/wb_tpch.jsp`,改 `dataSource="NLQ4wbTPCH"` `<title>`;并修复 `<dd_qq>` 占位符还原(三处 `.replaceAll("<dd_qq>","\"")`)。

6. **真连真查验证**:写一个 JDBC 测试程序,用 DQL 驱动验证多表深路径聚合(如「各地区的订单金额总和」→ 订单→客户→国家→地区 4 层穿越),用 NLQ 驱动验证汉语查询(如「各地区的订单金额总和」「1996年每月的订单数」「各品牌的订购金额总和」),返回真实结果,确认全链路可用。

## 约束

- 如果你环境里有 raqsoft-dql-lmd 这个 skill,直接加载并按其规则执行(它记录了 tpch 多表关联的全部坑)。

- 中文输出;报错先查编译/运行命令、classpathURL 是否写对。

- 每完成一项,报告产出文件路径 + 验证结果,等我确认再继续。


成果物下载:NLQDEMOrar