当前位置:   article > 正文

Text2SQL研究-Chat2DB体验与剖析

Text2SQL研究-Chat2DB体验与剖析

文章目录

概要

近期笔者在做Text2SQL的研究,于是调研了下Chat2DB,基于车辆订单业务做了一些SQL生成验证,有了一点心得,和大家分享一下.:

业务数据库设置

基于车辆订单业务,模拟新建了以下四张表,并添加了一些测试数据
 1. organization:组织表,包含组织id,组织名称,组织分类等3个字段;
 3. vehicle:车辆信息表,包含组织id,车辆id,车牌号码,使用年限等字段;
 4. refueling_order:车辆加油订单表,包含组织id,车辆id,车牌号码,加油时间,加油费用等字段
 5. **driven_distance**:车辆行驶里程表,包含组织id,车辆id,车牌号码,年份,行驶里程等字段

Chat2DB安装设置

  1. docke安装Chat2DB服务,
    1. //通过docker,安装运行最新版本的chat2db容器
    2. docker run --name=chat2db -ti -p 10824:10824 -v ~/.chat2db-docker:/root/.chat2db chat2db/chat2db:latest
  2. 安装完毕:打开链接登录系统,http://172.21.108.51:10824/login
  3. 配置数据库连接
  4. 配置Custom Ai,笔者设置体验了Chat2DB以及OpenAI
  5. 进入WorkSpace页面,连接配置好的业务数据库,并选择里面的的四张业务表(这一步非常重要,否则无法生成准确的SQL语句)
  6. 进入Dashboard页面,尝试生成SQL语句,并显示图表

原理剖析

GIT上下载并剖析源码,最核心的Text-2-SQL生成代码部分:

  1. ChatController::completions:Controller入口,接受Web端请求,生成SQL,并通过WebSocket返回
    1. /**
    2. * SQL转换模型
    3. *
    4. * @param queryRequest
    5. * @param headers
    6. * @return
    7. * @throws IOException
    8. */
    9. @GetMapping("/chat")
    10. @CrossOrigin
    11. public SseEmitter completions(ChatQueryRequest queryRequest, @RequestHeader Map<String, String> headers)
    12. throws IOException {
    13. //默认30秒超时,设置为0L则永不超时
    14. SseEmitter sseEmitter = new SseEmitter(CHAT_TIMEOUT);
    15. String uid = headers.get("uid");
    16. if (StrUtil.isBlank(uid)) {
    17. throw new ParamBusinessException("uid");
    18. }
    19. //提示消息不得为空
    20. if (StringUtils.isBlank(queryRequest.getMessage())) {
    21. throw new ParamBusinessException("message");
    22. }
    23. return distributeAISql(queryRequest, sseEmitter, uid);
    24. }
  2. distributeAISql:根据请求语句,以及系统的Custom AI设置进行SQL生成
    1. /**
    2. * distribute with different AI
    3. *
    4. * @return
    5. */
    6. public SseEmitter distributeAISql(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid) throws IOException {
    7. ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class);
    8. Config config = configService.find(RestAIClient.AI_SQL_SOURCE).getData();
    9. String aiSqlSource = AiSqlSourceEnum.CHAT2DBAI.getCode();
    10. if (Objects.nonNull(config)) {
    11. aiSqlSource = config.getContent();
    12. }
    13. AiSqlSourceEnum aiSqlSourceEnum = AiSqlSourceEnum.getByName(aiSqlSource);
    14. if (Objects.isNull(aiSqlSourceEnum)) {
    15. aiSqlSourceEnum = AiSqlSourceEnum.OPENAI;
    16. }
    17. uid = aiSqlSourceEnum.getCode() + uid;
    18. switch (Objects.requireNonNull(aiSqlSourceEnum)) {
    19. case OPENAI :
    20. return chatWithOpenAi(queryRequest, sseEmitter, uid);
    21. case CHAT2DBAI:
    22. return chatWithChat2dbAi(queryRequest, sseEmitter, uid);
    23. case RESTAI :
    24. case FASTCHATAI:
    25. return chatWithFastChatAi(queryRequest, sseEmitter, uid);
    26. case AZUREAI :
    27. return chatWithAzureAi(queryRequest, sseEmitter, uid);
    28. case CLAUDEAI:
    29. return chatWithClaudeAi(queryRequest, sseEmitter, uid);
    30. case WENXINAI:
    31. return chatWithWenxinAi(queryRequest, sseEmitter, uid);
    32. case BAICHUANAI:
    33. return chatWithBaichuanAi(queryRequest, sseEmitter, uid);
    34. case TONGYIQIANWENAI:
    35. return chatWithTongyiChatAi(queryRequest, sseEmitter, uid);
    36. case ZHIPUAI:
    37. return chatWithZhipuChatAi(queryRequest, sseEmitter, uid);
    38. }
    39. return chatWithOpenAi(queryRequest, sseEmitter, uid);
    40. }
  3. chatWithOpenAi:通过选择的业务表结构以及客户的问题生成prompt,来从大模型获取所需的SQL语句
    1. /**
    2. * 使用OPENAI SQL接口
    3. *
    4. * @param queryRequest
    5. * @param sseEmitter
    6. * @param uid
    7. * @return
    8. * @throws IOException
    9. */
    10. private SseEmitter chatWithOpenAi(ChatQueryRequest queryRequest, SseEmitter sseEmitter, String uid)
    11. throws IOException {
    12. String prompt = buildPrompt(queryRequest);
    13. if (prompt.length() / TOKEN_CONVERT_CHAR_LENGTH > MAX_PROMPT_LENGTH) {
    14. log.error("提示语超出最大长度:{},输入长度:{}, 请重新输入", MAX_PROMPT_LENGTH,
    15. prompt.length() / TOKEN_CONVERT_CHAR_LENGTH);
    16. throw new ParamBusinessException();
    17. }
    18. List<Message> messages = new ArrayList<>();
    19. prompt = prompt.replaceAll("#", "");
    20. log.info(prompt);
    21. Message currentMessage = Message.builder().content(prompt).role(Message.Role.USER).build();
    22. messages.add(currentMessage);
    23. buildSseEmitter(sseEmitter, uid);
    24. OpenAIEventSourceListener openAIEventSourceListener = new OpenAIEventSourceListener(sseEmitter);
    25. OpenAIClient.getInstance().streamChatCompletion(messages, openAIEventSourceListener);
    26. LocalCache.CACHE.put(uid, JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT);
    27. return sseEmitter;
    28. }
  4. 最后根据docker日志,可以发现chat2db 的mysql prompt组成,从这里可以发现真相其实并不复杂,整个Chat2DB可以说了除了通用的数据库方面的增删改查,最核心的部分其实就是根据表结构和用户问题生成prompt了
    1. 请根据以下table properties和SQL input将自然语言转换成SQL查询.
    2. MYSQL SQL tables, with their properties:
    3. ["CREATE TABLE `driven_distance` (\n `id` bigint(20) NOT NULL AUTO_INCREMENT,\n `organization_id` bigint(20) DEFAULT NULL,\n `vehicle_id` bigint(20) DEFAULT NULL,\n `license_plate` varchar(255) DEFAULT NULL,\n 。。。"]
    4. SQL input: 2023年,每个季度的加油金额各是多少元?

小结

经过测试,通常的业务查询基本上都能准确生成,另外通过上述一路使用和分析,笔者发现Text2SQL的技术几大要点

  1. 业务简库:跟3D渲染一样,离线渲染用精模,实时渲染用简模。Text2SQL一定要基于业务库做一个“素描”精简库 
  2. 自组Prompt:根据业务上下文所需的库表结构,拼接prompt
  3. 选择合法靠谱的大模型:ChatGPT4肯定是最好的,但在国内目前商业不合法,大家要根据自己业务进行尝试和选型
  4. 用户数据权限:通过拦截器,在prompt中加入当前用户ID,组织id等用户信息,从而巧妙实现用户数据权限等问题

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/71797
推荐阅读
相关标签
  

闽ICP备14008679号