自从两年前OpenAI公司发布ChatGPT后,大模型(Large Language Model,简称LLM)相关技术在国内外可谓百家争鸣,遍地开花,在传统数据挖掘、机器学习和深度学习的基础上,正式宣告进入快速发展的人工智能(Artificial Intelligence,简称AI)2.0时代。
人工智能的本质上是基于海量数据(包括结构化数据,文本数据,图片数据,音频数据,视频数据等)不断学习和推理,去模仿人类思考、认知、决策和行动。
我在项目实施工作中,跟数据打交道很多,主要是传统的数据建模和分析,基本都是基于商业产品,比如SAP,Qlik,帆软,永洪等;或者是基于公司的自研产品,大体功能类似但二次开发比较容易。由于这种壁垒,对机器学习和大模型了解甚少,基本停留在了解一些基本概念和关注一些技术新闻方面。最近几年的项目上重复遇到过一些困扰我好久的问题,项目范围内的数据模型搭完后,对数据比较依赖的部门比如财务,审计等会不断涌现出新的数据需求,如何让业务部门的用户快速获取和分析数据为日常工作提供决策支持是一个痛点,以往的大宽表模型和自助式BI分析解决方案不太理想,而学习数据建模由于太过技术性往往让用户望而却步。
最近通过在B站学习了一些优秀up主的大模型讲解视频,以及阅读了一些技术大拿的博文,受益匪浅。通过了解Text2SQL,帮我开拓了思路,似乎让我找到了解决以上痛点问题的契机。
Text2SQL也叫NL2SQL(Natural Language To SQL),是一种自然语言生成技术(文字->代码->结果),通过将人类用自然语言提出的数据问题(Text),转化为结构化查询语言(Structured Query Language,简称SQL)代码,然后自动执行生成的代码实现人类和数据的直接对话,持续提升数据分析效率。
Text2SQL实现的技术路线有很多,本篇主要讲述了基于阿里巴巴开源大模型Qwen,开源框架Vanna,以及Ollama,Mysql,Python3等技术栈的组合来部署实践。由于考虑到企业级数据的安全性,所有组件都是本地化部署,内网可以直接使用。虽然还没延伸考虑数据权限、SQL准确性、复杂场景实现等面临的挑战,但先迈出第一步等于成功了一半,不断学习不断进步,像LLM一样:)
以下是部署实践的步骤:
1. 准备一个linux虚拟机作为服务器。
我电脑是win11+vmware+centos7.6+Xshell,这里的安装略过,可自行网上搜索教程,安装后的效果:
如果装完的OS是图形化启动,可以通过如下命令设置为命令行启动,节省硬件资源:
systemctl set-default multi-user.target
再通过ifconfig查看ip:
获取到ip后就可以通过Xshell远程连系统了:
2. 安装Python3.11环境。
开源框架Vanna是基于Python的,我们通过Miniconda来安装Python3.11。
下载conda安装脚本:
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
安装:bash Miniconda3-latest-Linux-x86_64.sh
这里输入yes:
使环境变量生效后查看conda版本验证是否安装OK:
source ~/.bashrc
conda --version
安装python3.11
conda create -n py311 python=3.11
激活Python3环境,并查看版本:
conda activate py311
python -V
3. 安装Ollama。
Ollama是开源大模型的管理运行工具,支持很多国内外开源的大模型,包括我们这次要用的qwen2.5.
安装命令:
curl -fsSL https://ollama.com/install.sh | sh
4. 安装大模型。
安装好后通过ollama -v看下版本,考虑到笔记本性能,选择本地安装qwen2.5:3b版本的开源大模型,3b表示3billon也即30亿个模型参数。
qwen2.5:3b
ollama run qwen2.5:3b
安装完后,就可以直接run模型,和模型对话:
5. 安装mysql并模拟准备企业数据。
Mysql的安装此处略过,可以网上搜索教程。我安装的是mysql8.0+客户端工具Dbeaver:
创建一个vanna的数据库并准备三张表:
DDL和写数脚本如下:
CREATE DATABASE `vanna` /*!40100 DEFAULT CHARACTER SET utf16 */ /*!80016 DEFAULT ENCRYPTION='N' */;
-- vanna.DEPT definition
CREATE TABLE `DEPT` (
`DEPTNO` int NOT NULL COMMENT '部门编号',
`DNAME` varchar(14) DEFAULT NULL COMMENT '部门名称',
`LOC` varchar(13) DEFAULT NULL COMMENT '部门地址',
PRIMARY KEY (`DEPTNO`)
) ENGINE=InnoDB DEFAULT CHARSET=utf16 COMMENT='部门';
-- vanna.SALGRADE definition
CREATE TABLE `SALGRADE` (
`GRADE` int DEFAULT NULL COMMENT '工资等级',
`LOSAL` double DEFAULT NULL COMMENT '最低工资',
`HISAL` double DEFAULT NULL COMMENT '最高工资'
) ENGINE=InnoDB DEFAULT CHARSET=utf16 COMMENT='工资等级';
-- vanna.EMP definition
CREATE TABLE `EMP` (
`EMPNO` int NOT NULL COMMENT '员工编号',
`ENAME` varchar(10) DEFAULT NULL COMMENT '员工名称',
`JOB` varchar(9) DEFAULT NULL COMMENT '工作',
`MGR` double DEFAULT NULL COMMENT '直属领导编号',
`HIREDATE` date DEFAULT NULL COMMENT '入职时间',
`SAL` double DEFAULT NULL COMMENT '工资',
`COMM` double DEFAULT NULL COMMENT '奖金',
`DEPTNO` int DEFAULT NULL COMMENT '部门编号',
PRIMARY KEY (`EMPNO`),
KEY `DEPTNO` (`DEPTNO`),
CONSTRAINT `EMP_ibfk_1` FOREIGN KEY (`DEPTNO`) REFERENCES `DEPT` (`DEPTNO`)
) ENGINE=InnoDB DEFAULT CHARSET=utf16 COMMENT='员工';
INSERT INTO vanna.EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) VALUES
(7369,'SMITH','CLERK',7902.0,'1980-12-17',800.0,NULL,20),
(7499,'ALLEN','SALESMAN',7698.0,'1981-02-20',1600.0,300.0,30),
(7521,'WARD','SALESMAN',7698.0,'1981-02-22',1250.0,500.0,30),
(7566,'JONES','MANAGER',7839.0,'1981-04-02',2975.0,NULL,20),
(7654,'MARTIN','SALESMAN',7698.0,'1981-09-28',1250.0,1400.0,30),
(7698,'BLAKE','MANAGER',7839.0,'1981-05-01',2850.0,NULL,30),
(7782,'CLARK','MANAGER',7839.0,'1981-06-09',2450.0,NULL,10),
(7788,'SCOTT','ANALYST',7566.0,'1987-07-13',3000.0,NULL,20),
(7839,'KING','PRESIDENT',NULL,'1981-11-17',5000.0,NULL,10),
(7844,'TURNER','SALESMAN',7698.0,'1981-09-08',1500.0,0.0,30),
(7876,'ADAMS','CLERK',7788.0,'1987-07-13',1100.0,NULL,20),
(7900,'JAMES','CLERK',7698.0,'1981-12-03',950.0,NULL,30),
(7902,'FORD','ANALYST',7566.0,'1981-12-03',3000.0,NULL,20),
(7934,'MILLER','CLERK',7782.0,'1982-01-23',1300.0,NULL,10);
INSERT INTO vanna.SALGRADE (GRADE,LOSAL,HISAL) VALUES
(1,700.0,1200.0),
(2,1201.0,1400.0),
(3,1401.0,2000.0),
(4,2001.0,3000.0),
(5,3001.0,9999.0);
INSERT INTO vanna.DEPT (DEPTNO,DNAME,LOC) VALUES
(10,'ACCOUNTING','NEW YORK'),
(20,'RESEARCH','DALLAS'),
(30,'SALES','CHICAGO'),
(40,'OPERATIONS','BOSTON');
6. 安装Vanna。
参照Vanna的github指导:
https://github.com/vanna-ai/vanna
pip install vanna
安装完成后,根据官网介绍,如果使用在线大模型、向量数据库和数据库,几行代码就可以run起来,前提是需要vanna.ai网站申请api key.
https://vanna.ai/docs/app/
7. 安装开源向量数据库Chromadb。我们把向量数据库安装到本地,pip install即可,但是安装过程中会有一些问题,建议可以根据报错在在网上搜索答案。
8. 装PyCharm,远程连接虚拟机的Python环境。具体安装细节网上自行搜索,我电脑安装后的效果如下:
9. Python代码开发,连接qwen大模型,连接mysql,训练数据,启动vanna。
完整代码如下:
from vanna.ollama import Ollama
from vanna.chromadb import ChromaDB_VectorStore
import pandas as pd
import mysql.connector
from vanna.flask import VannaFlaskApp
class MyVanna(ChromaDB_VectorStore, Ollama):
def __init__(self, config=None):
ChromaDB_VectorStore.__init__(self, config=config)
Ollama.__init__(self, config=config)
#vn = MyVanna(config={'model': 'llama3.2:latest','ollama_host':'http://192.168.112.164:11434'})
vn = MyVanna(config={'model': 'qwen2.5:3b','ollama_host':'http://192.168.112.164:11434'})
def run_sql(sql: str) -> pd.DataFrame:
cnx = mysql.connector.connect(user='root', password='888888', host='192.168.112.164', database='vanna')
cursor = cnx.cursor()
cursor.execute(sql)
result = cursor.fetchall()
columns = cursor.column_names
#print('columns:',columns)
df = pd.DataFrame(result, columns=columns)
return df
vn.run_sql = run_sql
vn.run_sql_is_set = True
#给模型训练ddl语句
df_count = vn.run_sql("show tables")
for i in range(0,len(df_count)):
str = "show create table " + df_count['Tables_in_vanna'].loc[i]
df = vn.run_sql(str)
#print(df['Create Table'].loc[df.index[0]])
vn.train(ddl=df['Create Table'].loc[df.index[0]])
#执行web应用
VannaFlaskApp(vn,allow_llm_to_see_data=True).run()
10. 运行Vanna,测试Text2SQL功能:
可以看到控制台给出了运行的地址,这里的localhost换成虚拟机的ip即可访问:
在Training Data页签可以看到我们代码里添加的DDL训练数据加到向量数据库了。然后通过New Question来向数据库提问题:
由于笔记本性能问题,可能要运行一会才出答案,不得不说大模型也是很烧硬件的。
至此,Text2SQL的本地化部署和实践已完成。下一步有待继续探索真实案例和复杂场景的尝试。