ChatBI最佳实践

ChatBI是指通过NL2SQL技术支持企业通过自然语言查询数据生成报表。为了让您更好地了解ChatBI的功能和操作方法,我们将以“阿里香”餐饮管理系统为例,全面串联ChatBI的各个功能要点,帮助您快速上手并高效使用。

效果预览

ChatBI其核心流程包含两大阶段:首先基于NL2SQL技术将用户自然语言请求解析为SQL查询语句,随后使用解析的SQL语句生成可视化分析报表。

  1. 使用NL2SQL查询:

    /*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2sql, select '这一周的总收入有多少') WITH (basic_index_name='schema_index');

    SQL结果如下:

    SELECT SUM(actural_amount) AS total_income FROM bill_info WHERE pay_time >='2024-10-14' AND pay_time <= '2024-10-20';
  2. 使用上述解析的SQL语句,生成可视化分析报表:

    /*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_data2chart, 
    SELECT DATE_FORMAT(pay_time, '%Y-%m') AS m, SUM(actural_amount) AS income FROM 
    bill_info WHERE YEAR(pay_time) = 2024 GROUP BY m ORDER BY m) WITH 
    (basic_index_name='schema_index', setting_table_name='polar4ai_nl2sql_pattern', setting_id='3', result_type='IMAGE');

    SQL结果如下:

    +----------------------------------------------------------------------------------------------------------------------------------------------------------+
    | result                                                                                                                                                   |
    +----------------------------------------------------------------------------------------------------------------------------------------------------------+
    | http://db4ai-xxx.aliyuncs.com/xxx/xxx.png?security-token=xxx&OSSAccessKeyId=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|
    +----------------------------------------------------------------------------------------------------------------------------------------------------------+

数据准备

“阿里香”是虚构的一家餐饮公司,其账单管理系统中包含三张表,如下所示。您可点击下载样例数据

请根据您的表结构详细信息填写表和列的注释,以便大语言模型能够更好地识别和理解数据。这将有助于提升模型在数据处理和分析过程中的准确性和效率。
CREATE TABLE restaurant_info (
  id INT COMMENT '门店ID',
  position VARCHAR(128) COMMENT '门店地点',
  PRIMARY KEY (id)
) COMMENT='门店表';

CREATE TABLE menu_info (
  id INT COMMENT '菜品ID',
  name VARCHAR(64) COMMENT '菜品名称',
  type INT COMMENT '菜品类型',
  unit_price INT COMMENT '菜品单价',
  PRIMARY KEY (id)
) COMMENT='菜品表';

CREATE TABLE bill_info (
  id INT COMMENT '账单ID',
  items VARCHAR(512) COMMENT '下单菜品',
  actural_amount INT COMMENT '实际付费',
  restaurant_id INT COMMENT '就餐门店',
  waiter VARCHAR(16) COMMENT '服务员',
  diner_count INT COMMENT '就餐人数',
  pay_time DATE COMMENT '下单时间',
  PRIMARY KEY (id)
) COMMENT='账单表';

开通PolarDB for AI能力

  1. 增加AI节点,并设置AI节点的连接数据库账号:开启PolarDB for AI功能

    说明
    • 若您在购买集群时已添加AI节点,则可以直接为AI节点设置连接数据库的账号。详细说明,请参见创建普通账号

    • 该账号应具备读写权限,以便读取和写入目标数据表,从而顺利执行ChatBI转换过程中涉及的各类数据库操作。

  2. 使用集群地址连接PolarDB集群:登录PolarDB for AI

    说明
    • 使用命令行连接集群时,需增加-c选项。

    • DMS默认使用主地址连接集群,因此您需要手动修改为集群地址。修改后,请关闭原SQL窗口,并重新打开一个新的SQL窗口执行SQL。

使用ChatBI

接下来,您可以使用PolarDB for AINL2SQL模型来生成与用户问题相对应的SQL语句。有关详细信息请参见自然语言到SQL语言转义(基于大语言模型的NL2SQL)

创建表结构索引

通过以下SQL语句,您可以创建一个名为schema_index的表结构索引表,以便向大模型提供表结构信息。

/*polar4ai*/CREATE TABLE schema_index(id integer, table_name varchar, table_comment text_ik_max_word, table_ddl text_ik_max_word, column_names text_ik_max_word, column_comments text_ik_max_word, sample_values text_ik_max_word, vecs vector_768,ext text_ik_max_word, PRIMARY key (id));

这张表不会直接显示在数据库中。如果您需要查看相关信息,请使用以下SQL语句进行查询。

/*polar4ai*/SHOW TABLES;

接下来,您可以使用以下SQL语句将数据表结构导入到索引表schema_index中。

/*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_text2vec, SELECT '') WITH (mode='async', resource='schema') INTO schema_index;

执行时,PolarDB for AI会默认对当前库下的所有表进行向量化操作,并对列值进行采样。

执行该语句后,您将获得后台任务的task_id,例如:bce632ea-97e9-11ee-bdd2-492f4dfe0918。您可以使用以下SQL查询当前任务的状态。当返回的taskStatusfinish时,表示索引构建已完成。

/*polar4ai*/SHOW TASK `bce632ea-97e9-11ee-bdd2-492f4dfe0918`;

使用NL2SQL模型回答问题

您可以执行以下SQL语句在线使用基于LLMNL2SQL。在以下示例中,用户提出的问题是这一周的总收入有多少,使用的表结构索引schema_index

/*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2sql, select '这一周的总收入有多少') WITH (basic_index_name='schema_index');

数据库在处理请求时需要等待一段时间以获取大模型的回复,预期的返回结果如下:

image

根据上述示例,我们还可以提出一些典型的问题。这些问题涵盖了多种情况,包括GROUP BY、多表JOINORDER BY以及公式等。

问题序号

用户问题

NL2SQL返回值

1

每个门店收入情况排序

SELECT restaurant_id, SUM(actural_amount) AS total_income FROM bill_info GROUP BY restaurant_id ORDER BY total_income DESC;

2

在上海哪家门店收入最高

SELECT b.restaurant_id FROM restaurant_info r JOIN bill_info b ON r.id = b.restaurant_id WHERE r.position LIKE '%上海%' GROUP BY r.position ORDER BY SUM(b.actural_amount) DESC LIMIT 1;

3

上海平均每个人消费多少

SELECT AVG(bill_info.actural_amount / bill_info.diner_count) AS average_consumption_per_person FROM bill_info WHERE bill_info.restaurant_id IN (SELECT id FROM restaurant_info WHERE position = '上海');

4

这个月菜品下单量前十有哪些

SELECT mi.name, SUM(bi.items) as total_orders FROM bill_info bi JOIN menu_info mi ON bi.items = mi.id WHERE bi.pay_time >= '2024-10-01' AND bi.pay_time < '2024-11-01' GROUP BY mi.name ORDER BY total_orders DESC LIMIT 10;

5

这个月比上个月收入的环比增长百分比多少

SELECT (SUM(CASE WHEN MONTH(pay_time) = 10 AND YEAR(pay_time) = 2024 THEN actural_amount ELSE 0 END) - SUM(CASE WHEN MONTH(pay_time) = 9 AND YEAR(pay_time) = 2024 THEN actural_amount ELSE 0 END)) / SUM(CASE WHEN MONTH(pay_time) = 9 AND YEAR(pay_time) = 2024 THEN actural_amount ELSE 0 END) * 100 AS growth_percentage FROM bill_info;

6

上海的哪家门店人流量最高

SELECT r.position, COUNT(b.id) AS customer_flow FROM restaurant_info r JOIN bill_info b ON r.id = b.restaurant_id WHERE r.position LIKE '%上海%' GROUP BY r.id ORDER BY customer_flow DESC LIMIT 1;

可以看到,基于LLMNL2SQL模型能够较好地回答用户的问题,但有些问题的回复并未达到预期效果。例如,在第二个问题中,用户希望返回门店名称。如果重新表述为在上海哪家门店收入最高,请返回店名,模型将返回以下 SQL 语句:SELECT r.name FROM bill_info b JOIN restaurant_info r ON b.restaurant_id = r.id WHERE r.position = '上海' ORDER BY b.actural_amount DESC LIMIT 1;。当然,我们也可以通过对模型进行精细调优来提高其准确率,这些问题将在下文中逐一解决。

精调模型

配置问题模板

您可以使用一些通用的问题模板,通过引入特定知识来指导模型,从而使其能够根据特定的知识生成SQL语句。

  1. 执行以下SQL,创建问题模板表polar4ai_nl2sql_pattern

    CREATE TABLE `polar4ai_nl2sql_pattern` (
      `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
      `pattern_question` text COMMENT '模板问题',
      `pattern_description` text COMMENT '模板描述',
      `pattern_sql` text COMMENT '模板SQL',
      `pattern_params` text COMMENT '模板参数',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

    其中,表名必须以polar4ai_nl2sql_pattern开头,表结构中必须包含上述建表语句中的五个列。

  2. 接下来,创建问题模板的索引表pattern_index

    /*polar4ai*/CREATE TABLE pattern_index(id integer, pattern_question 
    text_ik_max_word, pattern_description text_ik_max_word, pattern_sql 
    text_ik_max_word, pattern_params text_ik_max_word, pattern_tables 
    text_ik_max_word, vecs vector_768, PRIMARY key (id));

    我们为第二个问题配置模板,以进行精调,目的是返回店面的地址。

    请执行以下SQL语句以添加一个新的模式(pattern):

    INSERT INTO polar4ai_nl2sql_pattern (id, pattern_question, pattern_description, pattern_sql, pattern_params) VALUES (
      1, 
      "在#{position}哪家门店收入最高", 
      "在【地点】哪家门店收入最高", 
      "SELECT r.position FROM bill_info b JOIN restaurant_info r ON b.restaurant_id = r.id WHERE r.position LIKE '%#{position}%' ORDER BY b.actural_amount DESC LIMIT 1;", 
      '[{"table_name":"restaurant_info"},{"table_name":"bill_info"}]'
    );

    在模式(pattern)中采用了槽位,以便匹配多个地点。在pattern_sql列中填写正确的SQL语句,并在槽位置用#{}进行标记。pattern_params列用于表信息的额外后置处理补充,但在此可以忽略。

  3. 接下来,将问题模板的信息导入索引表。

    /*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_text2vec, SELECT '') WITH (mode='async', resource='pattern') INTO pattern_index;

    schema_index构建索引表的过程类似,这里也会返回一个任务ID。您可以通过执行/*polar4ai*/show task 'xxx-xxx-xxx'来查看当前任务的状态。

    说明

    如果polar4ai_nl2sql_pattern表中的数据发生更新,则需要重新创建pattern_index并进行导入操作。可以使用以下 SQL 语句删除旧表:

    /*polar4ai*/DROP TABLE pattern_index;

    让我们重新执行生成问题的SQL,并附上pattern_index提示。

    /*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2sql, select '在上海哪家门店收入最高') WITH 
    (basic_index_name='schema_index',pattern_index_name='pattern_index');

    5eecdaf48460cde5ad9d83d5444ce71cd140b5a7e9b23e1258e70b814913bc360a414d3de9277d871abf3af1cbd75249c0734e6846e794467f339bd1442daeacb6461bc7ea938f09d4a6c8551d30df66760f3fe7627db0a6fc653b69905bac42

构建配置表

若您希望对问题进行前置处理,或者对最终生成的SQL进行后置处理时,可以使用配置表进行配置。

词汇含义提示

在第六个问题中,由于“大模型”无法准确理解“人流量”这一词汇的含义,因此可以通过配置polar4ai_nl2sql_llm_config表进行预处理。

CREATE TABLE `polar4ai_nl2sql_llm_config` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `is_functional` int(11) NOT NULL DEFAULT '1' COMMENT '是否生效',
  `text_condition` text COMMENT '文本条件',
  `query_function` text COMMENT '查询处理',
  `formula_function` text COMMENT '公式信息',
  `sql_condition` text COMMENT 'SQL条件',
  `sql_function` text COMMENT 'SQL处理',
  PRIMARY KEY (`id`)
);

插入相关的配置项,告知大模型将“人流量”或“客流量”统计为“就餐人数”。

INSERT INTO polar4ai_nl2sql_llm_config (id, is_functional, text_condition, query_function, formula_function, sql_condition, sql_function) VALUES (
  1, 
  1, 
  "人流量||客流量", 
  "", 
  "人流量或客流量使用就餐人数总和进行统计", 
  "", 
  ""
);

其中,is_functional1表示该配置项有效。字段text_condition的值为“人流量||客流量”,用于在问题中匹配含有“人流量”或者“客流量”的情况。formula_function中的内容则是通过文字(或公式)向大模型解释专业词汇的含义。

在此情况下,无需构建索引表和进行向量化处理,可以直接执行SQL生成,结果如下。

image

模糊匹配提示

在第3个问题中,地名匹配使用=操作符进行检索将导致失败。因此,我们需要提示在进行地名匹配时应使用模糊搜索,可以添加以下配置项。

INSERT INTO polar4ai_nl2sql_llm_config (id, is_functional, text_condition, query_function, formula_function, sql_condition, sql_function) VALUES (
  2, 
  1, 
  "", 
  "", 
  "门店地点position的匹配需要使用模糊搜索", 
  "", 
  ""
);

其中,text_condition为空,表示该配置项将全局生效(请谨慎使用)。

结果如下图所示。可以看到,地点的匹配成功地使用了模糊搜索。

image

同理,在第5个问题中,“环比”和“同比”的计算公式也可以录入到polar4ai_nl2sql_llm_config配置表中,以提高生成SQL的精确度。您可以自行进行挑战尝试。

图表输出

在使用NL2SQL生成SQL语句后,通常希望获得该SQL查询的结果,并同时展示一些更直观的视觉效果,如柱状图、折线图和饼图等。PolarDBNL2Chart方案可以根据您的问题和SQL语句执行并最终返回相应的报表(支持柱状图、饼状图、折线图)。

  1. 假设您在NL2SQL中的语句如下:

    /*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2sql, select '商户类型统计') WITH (basic_index_name='schema_index',pattern_index_name='pattern_index');

    并生成了相应SQL语句(请检查SQL语句运行结果有意义且不是空值):

    SELECT merchtype AS 商户类型,COUNT(*) AS 商品数量 FROM hkrt_merchant_info GROUP BY merchtype;
  2. 使用NL2Chart:

    语法说明

    /*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2chart, <SQL语句>) WITH (usr_query = <usr_query>, result_type = <result_type>);

    参数说明

    参数名称

    参数说明

    示例值

    usr_query

    用户输入的问题描述,用于明确统计图表的生成需求。

    "2023年各季度销售额统计"

    result_type

    指定返回结果的类型,当前仅支持 'IMAGE'

    'IMAGE'

    SQL语句

    由 NL2SQL 模块生成的 SQL 查询语句,用于获取数据。

    SELECT quarter, sales FROM sales_data WHERE year = 2023

    示例:将输出的SQL语句的查询结果转化为图表

    /*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2chart, SELECT merchtype AS 商户类型, COUNT(*) AS 商户数量 FROM hkrt_merchant_info GROUP BY merchtype) WITH (usr_query = '商户类型统计', result_type='IMAGE');

    返回结果如下:

    说明

    返回的链接为图片地址,有效时间为90分钟,过时将失效。

    http://db4ai-xxx-xx-xxxx-xxx-xxxx.aliyuncs.com/pc-bpze47ma2c515087l6/OSSAccessKeyId=xxxxxxx&Expires=1716130199&Signature=KvPFzfMebIEmqxPIXURurwwbsXM%3D

    image.png

  3. (可选)图表类型选择与强制选择

    模型会基于对用户问题和数据的理解选择合适的图表,推荐选择用户问题引导模型画图。

    以下是问题类型与图表类型对应参考表:

    问题类型

    图表类型

    用户问题示例

    说明

    数量统计

    柱状图

    "请统计各城市销售额"

    展示不同类别之间的数值对比,如数量、总量、频次等。

    趋势变化

    折线图

    "请展示过去一年的用户增长趋势"

    展示数据随时间或有序类别变化的趋势,强调连续性。

    占比分布

    饼状图

    "请展示各产品线的销售占比"

    适用于展示整体中各部分的比例关系,需数据为分类且总和明确。

    通过修改usr_query参数强制指定图表类型,在usr_query参数末尾加上补充命令即可:

    -- 将输出的sql输入 nl2chart 画折线图
    /*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2chart,
    SELECT merchtype AS 商户类型, COUNT(*) AS 商户数量 FROM hkrt_merchant_info GROUP BY merchtype
    ) WITH (usr_query = '商户类型统计,画折线图', result_type='IMAGE');

    image.png

    -- 将输出的sql输入 nl2chart 画饼状图
    /*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2chart,
    SELECT merchtype AS 商户类型, COUNT(*) AS 商户数量 FROM hkrt_merchant_info GROUP BY merchtype
    ) WITH (usr_query = '商户类型统计,画饼状图', result_type='IMAGE');

    image.png

更多详细信息请参考自然语言生成智能图表NL2Chart

常见问题

DMS平台上执行SQL语法时出现错误。

DMS默认使用主地址连接集群,因此您需要手动修改为集群地址。修改后,请关闭原SQL窗口,并重新打开一个新的SQL窗口执行SQL。详细说明,请参见登录PolarDB for AI

错误信息:2003 - Execute sql failed in ai db执行失败。

  1. 检查您使用的数据库账号是否为AI节点的数据库账号,并且具备读写权限。详细说明,请参见开启PolarDB for AI功能

  2. 若您使用的是DMS,修改为集群地址连接集群后,请关闭原SQL窗口,并重新打开一个新的SQL窗口执行SQL。详细说明,请参见登录PolarDB for AI

  3. 检查SQL语句中是否存在特殊字符(尽量去掉注释、换行和缩进符号)。

错误信息:9050 - Empty data 'polar4ai_nl2sql_pattern'为空。

polar4ai_nl2sql_pattern表中没有数据。如果没有可用的模式(pattern),则无需进行向量化导入。

执行data2chart时出现错误:1149 - You have an error in your SQL syntax;

造成这个错误的原因有很多,请按照以下步骤依次排查:

  1. 检查列名:确认SQL语句中是否使用了关键字或函数名作为列名。

  2. SQL语句作为字符串插入:将SQL语句作为字符串插入到数据库中。

  3. 通过sql_fetching参数获取SQL:通过sql_fetching参数获取该条SQL进行生成。

重新微调训练模型

若模型无法满足您的业务需求,您可以重新训练模型,并对模型内部参数进行微调,以达到更佳的效果。

限制条件

  • 仅支持AI节点规格为16125 GB+ 一张GU100 polar.mysql.x8.2xlarge.gpu的集群使用该功能。

  • 当前一次只能训练一个模型。

  • 目前只能部署一个模型。

使用说明

训练模型

/*polar4ai*/CREATE MODEL udf_qwen14b WITH (model_class='qwen-turbo', model_parameter=(basic_index_name='schema_index', pattern_index_name='pattern_index',training_type='efficient_sft')) as (SELECT '')

参数说明

参数名

说明

默认值

可选值/范围

model_class

模型类型,当前支持{'qwen-14b-chat', 'qwen-turbo'}。

{'qwen-14b-chat', 'qwen-turbo'}

model_parameter

模型参数配置,包括必需和可选参数。

basic_index_name

训练数据中的数据库信息采自的索引表名称,此处需为数据库索引表。

pattern_index_name

训练数据中的问题模板信息采自的索引表名称,此处需为问题模板索引表。

training_type

训练类型,允许值{'efficient_sft', 'sft'}。'efficient_sft'表示高效训练,一般为LoRa方式;'sft'表示全参数训练。

{'efficient_sft', 'sft'}

n_epochs

循环次数,模型训练过程中学习数据集的次数。建议范围为1-3遍,可依据需求进行调整。

3

[1, 200]

learning_rate

学习率,表示每次更新数据的增量参数权重。学习率越大,则参数变化越大,对模型影响越大。

'3e-4'

batch_size

批次大小,代表模型更新参数的数据步长。建议的批次大小为1632。

16

{8, 16, 32}

lr_scheduler_type

学习率策略,动态改变训练过程中更新权重时采用的学习率大小。

'linear'

{'linear', 'cosine', 'cosine_with_restarts', 'polynomial', 'constant', 'constant_with_warmup', 'inverse_sqrt', 'reduce_lr_on_plateau'}

eval_steps

模型验证的间隔步长,用于阶段性评估训练准确率及损失。

50

[1, 2147483647]

sequence_length

训练数据的序列长度,单个样本的最大长度,超出长度将自动截断。

2048

[500, 2048]

lr_warmup_ratio

warmup占总训练步数的比例。

0.05

(0, 1)

weight_decay

L2正则化,帮助减少过拟合问题。

0.01

(0, 0.2)

gradient_checkpointing

开启或关闭gradient checkpointing,用于节省显存。

'True'

{'True', 'False'}

use_flash_attn

是否使用Flash Attention。

'True'

{'True', 'False'}

lora_rank

LoRa训练中的秩大小,影响训练中数据对模型的作用程度。

8

{2, 4, 8, 16, 32, 64}

lora_alpha

LoRa训练中的缩放系数,用于调整初始化训练权重。

32

{8, 16, 32, 64}

lora_dropout

训练过程中随机丢弃神经元的比率,防止过拟合,提高模型泛化能力。

0.1

(0, 0.2)

lora_target_modules

选择模型的特定模块进行微调优化。

'ALL'

{'ALL', 'AUTO'}

查看模型

/*polar4ai*/SHOW model udf_qwen14b

删除模型

/*polar4ai*/DROP model udf_qwen14b

查看所有模型

/*polar4ai*/SHOW models

模型部署

训练完成的模型只有在经过部署后,才能在NL2SQL中使用。

/*polar4ai*/deploy model udf_qwen14b

查看部署

/*polar4ai*/SHOW deployment udf_qwen14b

删除部署

/*polar4ai*/DROP deployment udf_qwen14b

查看所有部署

/*polar4ai*/SHOW deployments

使用部署的模型进行自然语言转SQL

/*polar4ai*/SELECT * FROM PREDICT (MODEL _polar4ai_nl2sql, SELECT 'id为1的内容是什么?') WITH (basic_index_name='schema_index', llm_model='udf_qwen14b')
参数说明

参数

描述

basic_index_name

不可为空,需填写当前问题关联的数据库信息索引表。

llm_model

可为空。如果不填写,将调用未经微调的模型进行自然语言转SQL;如果填写,请确保填写已部署且状态为“serving”的部署名称。未完成部署的模型无法在此使用。