# Dify 搭建数据查询 AI 应用

# 简介

在大数据飞速发展的时代,现在企业基本都会向 AI 靠近,传统的系统和用户交互都是让用户在系统上自己操作,要查询什么内容通过对话的方式就能获取到结果,而不需要点这个菜单又去点哪个菜单,还要选条件在点查询,本篇文章就实现,怎么将自然语言转成 SQl 在去查询实际的数据。dify 官网

# 开始

首先我们需要 dify 环境,这里安装就不再赘述了,下面开始

# 知识库

需要将我们的业务 表结构详细的字段说明 还有一些 sql的示例 上传到知识库,当用户提问提就会检索出来,让大模型理解在给出进行生成 sql 语句,如图创建两个知识库:

数据结构内容:

表结构描述:
表名称: `ht_land` 资产表
字段列表: 
`title`,资产标题,字符串类型
`distNo`,地区编号,字符串类型。这个字段可以和地区表关联,以实现是哪个地区的资产。
`distName`,地区名称,字符串类型
`landNo`,资产编号,字符串类型
`landName`,资产名称,字符串类型
`area`,资产面积,小数类型
`unit`,资产面积单位,字符串类型
表结构描述:
表名称: `dist` 地区表或者是村集体表
字段列表: 
`id`,主键,整型
`distno`,地区编号,字符串类型。注意地区编号,1位长度表示省级,2位长度表示市级,4位长度表示区县级,6位长度表示镇街级,9位长度表示村级,
`distname`,地区名称,字符串类型
`parentDistNo`,上级地区编号,字符串类型。可以和本身关联,关联上一级的地区。
`parentDistName`,上级地区名称,字符串类型
表结构描述:
表名称: `Rep_01` 收益及收益分配表
字段列表: 
`id`,主键,整型
`zth`,地区编号,字符串类型。这个字段可以和地区表关联,以实现是哪个地区的资产。
`YEARS`,年份,字符串类型
`MONTHS`,月份,字符串类型
`C1`,项目名称,字符串类型。比如有“一、经营收入”, “二、经营收益” 等项目
`C2`,行次,字符串类型。相当于排序号
`C3`,本月数,小数类型。单位是元,也叫本月发生数
`C1`,本年数,小数类型。单位是元,也叫本年发生数
表结构描述:
表名称: `proj` 交易表
字段列表: 
`id`,主键,整型
`title`,交易项目名称,字符串类型
`prj_no`,交易编号,字符串类型
`distno`,地区编号,字符串类型。这个字段可以和地区表关联,以实现是哪个地区的资产。
`distname`,地区名称,字符串类型。
`landno`,资产编号,字符串类型。这个字段可以和资产表关联,以实现是哪个资产的交易情况。
`landname`,资产名称,字符串类型。单位是元
`jyjffs`,交易方式,字符串类型。列如:电子暗投,电子竞投,公开暗投,公开明投,协商谈判,续约,综合评审
`totalAmount`,交易金额,小数类型。单位是元
这些表的设计是相互关联的,例如,资产表中的`distNo`字段将地区表或者是村集体表表中的特定地区关联起来。

sql 示列:

查询的资产?, select * from ht_land where distno like (select distno from dist where distname like '%白云区%') + '%';
村集体总资产数, select count(*) from ht_land;
村集体交易总额, select sum(totalAmount) from proj;
村集体总收入, select sum(*) from zcjy_hpq..Rep_01 where c1 like '%六、年末未分配收益%';
村集体2024年1月份总收入, select sum(*) from zcjy_hpq..Rep_01 where years = 2024 and months = 1 and c1 like '%六、年末未分配收益%';

测试检索效果如图,可以看的出来正确的检索出来了:

# 工作流

知识库创建好了,也能看到结合大模型能够实现生成 sql 语句,接下来我们开始创建工作流,从生成 sql 到查询到返回结果

大致流程:开始 -> 知识检索 -> 大模型解析返回 sql -> 问题分类(对业务不相关问题的处理) -> 数据库查询 -> json 解析获取数据 -> 大模型结合问题和答案回答 -> 回复用户

流程图如下:

  1. 新建一个 工作流

  2. 添加 知识库 节点,添加好 数据结构描述sql示例 知识库

  3. 添加大模型解析知识库内容生成 sql,为了更好的生成对应业务的 sql,需要添加提示词(可以根据自己的要求更改):

    # 角色
    你是一个SQL专家或者数据库分析师, 根据检索到{{#context#}}以及用户输入的{{#sys.query#}}内容,生成对应的查询sql,sql语句必须经过严格的校验:
    
    # 硬性要求
    1.严格使用检索到的表字段
    2.确保SQLSERVER语法兼容
    3.仅输出最终结果sql语句的txt文本,不要加任何信息
    4.禁止中间过程输出
    5.返回纯SQL文本内容, 不要代码块
    6.表名称要去掉特殊符号
    
    
    #关键步骤:
    1、对用户输入的内容进行识别和判断,如果内容涉及政治、时事、社会问题以及违背道德和法律法规的情形,一律输出:”您提出的问题超出我应当回答的范围,请询问与公司业务相关的问题,否则我无法作出回答“
    2、根据用户输入的内容和上下文信息,形成内容分类,根据内容分类按照以下规则从知识库 “黄埔数据结构描述” 中检索数据表结构信息:
    -内容分类与资产相关,则检索“资产信息”
    -内容分类与财务相关,则检索“财务信息”
    -内容分类与地区相关,则检索“地区信息”
    -内容分类与交易相关,则检索“交易信息”
    注意:务必严格按照上述分类获得对应的检索关键词,不得生成新的检索关键词。如果你认为用户的提问无法匹配到合适的分类,请输出提示:为确保查询获得准确信息,请再把你的需求描述细致一些
    3、根据用户输入的内容和上下文信息,形成一个符合用户意图的完整问题,以此作为输入在知识库“黄埔sql示例”中检索SQL语句参考示例
    4、基于对上下文和对用户提问的理解,按照检索到的数据表结构信息,以及SQL参考示例,编写SQL查询语句。注意,若内容分类与参考示例中的分类不符时,则忽略这个示例。另外,不是所有情况下都有示例参考,没有示例时请按照自己的理解和掌握的知识编写SQL语句
    5、去除SQL语句中多余的注释、换行符等无用信息,输出一个纯净的、可直接执行的SQL语句
    6、如果用户选择开场白的选项直接从知识库“黄埔sql示例”中检索SQL语句
    7、用户提到的“村集体”就是地区,如果没有说哪个村集体,那么就是所有地区
    
    
    #编写SQL时的注意事项:
    1.务必根据上下文提供的数据表结构描述来编写SQL语句,确保仅使用数据表结构描述中提到的表名和字段名,并参考对字段的解
    2.确保SQL兼容SQL Server 2014
    3.只用简体中文
    4.只输出一个完整SQL语句,无注释,确保可直接执行并获得预期的结果
    5.对于字符串和长文本类型的字段,除非用户有特别说明,否则都用LKE操作,而不是等于操作,例如:WHERE distno LIKEN'%关键词%',而不是WHERE distno='关键词'
    6.除法处理:参考以下模板以避免错误:
    CASE WHEN 「除数] = 0 THEN 0
    ELSE CAST([被除数] AS FLOAT)/[除数]
    END AS[结果列名]
    7.当用户询问涉及地区层级的查询时(如“查询[XX地区]下所有资产”),按以下逻辑生成SQL:
    定位目标地区:
    从地区表查询用户输入名称对应的distno(确保名称完全匹配,如“白云”而非“白云区”)。
    SELECT distno FROM 地区表 WHERE distname = '用户输入名称';
    获取所有子地区编号:
    前缀匹配):若地区编号为层级结构(如0111的下级为011101),用LIKE匹配前缀:
    SELECT distno FROM 地区表 
    WHERE distno LIKE (SELECT distno || '%' FROM 地区表 WHERE distname = '用户输入名称');
    关联业务表查询:
    将子地区编号与业务表(如资产信息)关联,返回结果。
    SELECT b.* FROM 业务表 b
    JOIN (上述子地区查询) s ON b.distno = s.distno;
    8.适用场景: 生成查询SQL时需遵循上述规则
    9.输出规范: 纯SQL文本无附加说明
    10.技术限制: 不支持图像处理类的请求
    11.返回纯SQL文本内容
    
  4. 代码执行器 这里加这个主要是对 sql 进行加密传输,其实也可以不加

  5. 问题分类器 主要是对用户提问的问题进行分类,如果是不相关的问题,则做其他处理,如图:

  6. http 请求节点 这一步主要是传入 sql 调 api 接口进行数据查询

  7. json 解析节点 这一步主要是对查询出来的数据进行解析,获取原始的数据

  8. 添加大模型节点 这里的节点是为了对用户的问题,和结果做回答,这里可以用私有的模型,保证数据的隐私安全。

    添加一些提示词让大模型更自然的回答如下:

    通过{{#context#}}的结果内容回答用户提问的{{#sys.query#}}的问题
    
    注意事项:
    #数据呈现、解读和分析要求:
    1.所有数据已符合用户问题中的条件(如地区名称、日期范围)
    2.直接使用提供的数据分析,不质疑数据是否符合条件
    3.不需再次筛选或确认数据类别/时间范围
    4.数据为[]或空时,回复"没有查询到相关数据",不得编造数据
    5.列出详细数据,优先以表格方式列出数据,如果数据超过10时,除非用户问题中包含明确要求列出全部数据的情形外,你只需要随机列出5条有代表性的的记录,否则需要列出全部记录
    6.当有记录被省略时,必须作出说明
    7.对数据进行概览和总结,必须包括原始数据的总记录数
    8.识别趋势、异常,并提供分析和建议
    
    #其他注意事项
    1、严格按照上下文内容回答
    
  9. 直接返回 最后直接返回

# 推荐数据集

可以去 数据集网站 上获取一些其他人预定好的 sql 例子或者其他数据

# 自定义工具

dify 是一个很强大的工具,我们可以自己定义工具,比如自己定义一个 查询天气的ai助手 ,或者 搜索引擎 ,在或者 图表展示 等 助手,然后把我的工具发布出去,供其他程序调用

# 扩展

dify 的知识库系统并不是很强大,如果我们做业务复杂,生成那种多表关联的 sql 语句,那么 dify 就显的不是那么的好了,所以我们需要专门的 自然语言转SQL 的工具来实现,下面推荐两种

  1. RAGFlow 可以看我其他文章 如何使用RAGFlow并接入到Dify知识库中
  2. VannaAI