Repository: ambition119/chatBI Branch: master Commit: 785185c9a6a2 Files: 35 Total size: 51.4 KB Directory structure: gitextract_nqa06b_p/ ├── .gitignore ├── README.md ├── chat_rest/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── am/ │ │ └── chat/ │ │ └── bi/ │ │ ├── ChatBIApplication.java │ │ ├── comm/ │ │ │ ├── AppCode.java │ │ │ ├── ErrorCode.java │ │ │ ├── Result.java │ │ │ ├── ResultMeta.java │ │ │ ├── StateCode.java │ │ │ └── Utils.java │ │ ├── config/ │ │ │ └── SwaggerConfiguration.java │ │ ├── controller/ │ │ │ └── LLMsController.java │ │ ├── domain/ │ │ │ ├── dao/ │ │ │ │ └── SchemaMetaInfo.java │ │ │ └── vo/ │ │ │ └── ResponseVo.java │ │ └── service/ │ │ ├── LLMsService.java │ │ └── impl/ │ │ └── LLMsServiceImpl.java │ ├── resources/ │ │ ├── application-dev.yml │ │ ├── application.yml │ │ ├── assembly.xml │ │ ├── dev/ │ │ │ └── setenv.sh │ │ ├── logback-dev.xml │ │ ├── logback.xml │ │ ├── press/ │ │ │ └── setenv.sh │ │ ├── prod/ │ │ │ └── setenv.sh │ │ ├── sql/ │ │ │ └── mysql.sql │ │ └── test/ │ │ └── setenv.sh │ └── shell/ │ ├── comm.sh │ ├── start.sh │ └── stop.sh ├── chat_ui/ │ └── pom.xml ├── plugins/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── am/ │ └── chat/ │ └── bi/ │ └── utils/ │ ├── AliLLMsUtil.java │ ├── LLMsResultUtil.java │ └── PromptUtil.java └── pom.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .idea target *.iml .DS_Store ================================================ FILE: README.md ================================================ 自定义BI改变了传统IT主导开发固定报表的时代,让数据能够即席分析达到所见即所得,随着大模型的兴起与火热,LLMs 结合数据可视化技术,通过问答的方式能够让系统智能与数据交互与生成图表,无论是BI copilot还是ChatBI, 除了替代之前小助手功能只能查询已有报表数据外,更多的是省略创建dashboard的时候图表的拖拉拽操作,让系统自动生成的图表能够在dashboard中布局应用。 ![自动图表生成](./doc/photo/自动生成图表.png) #### 一、实现思路主要考虑 1、如果没有大模型或者大模型服务宕机,自动化生成可视化工程化就不能实现? —— 直接解耦,大模型不能影响主体工程,图表这块不能大模型生成。 2、如果大模型正常服务能够推理,是否所有的问答都要经过推理服务? —— 大模型的推理是否那么靠谱稳定、对应响应的耗时以及对应采用商业大模型服务的费用成本因素。 3、以SQL交互为核心的配套组件,通过系统化的工程来达到生产可用要求。 借鉴[SQLChat](https://github.com/sqlchat/sqlchat), [DB-GPT](https://github.com/csunny/DB-GPT), 的开源项目思路,确定以SQL生成为核心,和数据库交互获取数据,通过data + 可视化图表组件方式实现。不考虑大模型自动生成图表的方式,主要考虑一是美观,二是灵活性,三最主要的是性能是否可控,尤其是大量查询的data很大时,是否分页等可以手动人为干预控制;而靠大模型自动生成图表类似markdown/html估计不会很理想。 #### 二、主要思路框架 ![框架图](./doc/photo/模块架构.jpeg) #### 三、说明 1、是否有表,指标字段,维度字段列级的查询权限,以及行级 ${input_dim_conditions} <= ${user_has_permission},否直接报权限不足。 2、如果维度、度量,都来自于一张表,解析查询条件,然后sql模版组装: ```sql select ${input_dim_names} , ${input_metric_names} from ${get_meta_table_name} where ${input_dim_conditions} [group by $s] [order by $s]; ``` 这种sql组装拼接成熟度很高,不一定直接依赖大模型服务,唯一有难点的就是文本中对应条件的解析。 3、 如果维度、度量都来自于多张表,则查找相似匹配的问题对应的答案Sql: ①、如果有对应sql, 则直接应用,可能只组装的就是sql的where条件。 ②、如果有相似sql, 例如2张表的join找2张表join的sql模版则对应需要增加子查询方式与拼接where, 即 ```sql select ${input_dim_names} , ${input_metric_names} from ( ${get_query_sql} ) TT where ${input_dim_conditions} [group by $s] [order by $s]; ``` **属于可选步骤(也可以直接调用大模型能力)**,这里where一般可以,sql引擎都支持谓词下推,但是子查询多列可能带来性能影响,不是所有的sql引擎都支持列剪裁。 同时注意相似如果是2张表join,则不能找3张表join的sql语句去拼接。 ③、如果sql为null或者校验error的sql, 则通过提示词(问题、schema)交给大模型推理返回推理生成查询sql。 整体框架可能存在不足,但是相信随着时间的进步,大模型会越来越稳定可靠,响应快 以及细分领域SQL LLMs的诞生;也相信利他主义的开源者,会诞生更优秀的ChatBI项目。 #### 四、模块结构 |--rest api层     |-- server 服务层,调用解析,执行sql,和数据库交互     |-- mapper 元数据匹配     |--validate sql校验 |--common |--parse 文本解析 |--plugins 大模型插件,和大模型交互,智能生成 ================================================ FILE: chat_rest/pom.xml ================================================ chatBI com.am 1.0-SNAPSHOT 4.0.0 chat_rest org.springframework.boot spring-boot-starter logback-classic ch.qos.logback org.springframework.boot spring-boot-starter-web org.springframework spring-context-support org.springframework.boot spring-boot-starter-websocket org.springframework.boot spring-boot-configuration-processor true org.springframework.boot spring-boot-starter-test test io.springfox springfox-swagger2 io.springfox springfox-swagger-ui com.github.pagehelper pagehelper-spring-boot-starter mybatis-spring-boot-starter org.mybatis.spring.boot org.mybatis.spring.boot mybatis-spring-boot-starter org.springframework.boot spring-boot-starter com.alibaba druid-spring-boot-starter mysql mysql-connector-java runtime org.mybatis.spring.boot mybatis-spring-boot-starter-test org.projectlombok lombok org.apache.commons commons-lang3 ch.qos.logback logback-classic logback-core ch.qos.logback ch.qos.logback logback-core redis.clients jedis com.google.code.gson gson com.am plugins compile 1.0-SNAPSHOT dev dev true test test prod prod press press ${project.artifactId} src/main/java src/main/shell src/main/resources *.xml **/*.xml *.yml static/ templates/ mapper/ org.mybatis.generator mybatis-generator-maven-plugin 1.3.2 mysql mysql-connector-java 5.1.38 Generate MyBatis Artifacts none generate src/main/resources/generatorConfig.xml true org.apache.maven.plugins maven-jar-plugin 3.0.2 false true lib/ ${start-class} config/ *.xml *.conf *.yml *.properties org.apache.maven.plugins maven-assembly-plugin src/main/resources/assembly.xml false make-assembly package single ================================================ FILE: chat_rest/src/main/java/com/am/chat/bi/ChatBIApplication.java ================================================ package com.am.chat.bi; import org.mybatis.spring.annotation.MapperScan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; @SpringBootApplication @ServletComponentScan @MapperScan(basePackages = { "com.am.chat.bi.dao" }) public class ChatBIApplication { private static final Logger LOG = LoggerFactory.getLogger(ChatBIApplication.class); public static void main(String[] args) { SpringApplication.run(ChatBIApplication.class, args); LOG.info("ChatBIApplication start run!"); } } ================================================ FILE: chat_rest/src/main/java/com/am/chat/bi/comm/AppCode.java ================================================ package com.am.chat.bi.comm; /** * @author wpl * 后期数据库配置 */ public enum AppCode { VSKIT(1, "vskit") ; private int code; private String appName; AppCode(int code, String appName) { this.code = code; this.appName = appName; } public int getCode() { return code; } public String getAppName() { return appName; } public static String getAppName(int code) { switch (code){ case 1: case 2: default: return VSKIT.getAppName(); } } } ================================================ FILE: chat_rest/src/main/java/com/am/chat/bi/comm/ErrorCode.java ================================================ package com.am.chat.bi.comm; /** * @author wpl */ public enum ErrorCode { SUCCESS(1000, "success"), FAIL(1010, "fail"), FAIL_LLMs(1020, "fail LLMs"), FAIL_INSERT(1001, "fail insert"), FAIL_UPDATE(1002, "fail update"), FAIL_SELECT(1003, "fail select"), FAIL_UPLOAD(1004, "fail upload"), FAIL_EXPORT(1005, "fail export"), FAIL_DEL(1006, "fail delete"), FAIL_RUN_TASK(1007, "fail run task"), ; private int code; private String msg; ErrorCode(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return code; } public String getMsg() { return msg; } } ================================================ FILE: chat_rest/src/main/java/com/am/chat/bi/comm/Result.java ================================================ package com.am.chat.bi.comm; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; /** * @author wpl */ @Data @ApiModel public class Result { @ApiModelProperty(value = "元数据", required = true) private ResultMeta meta; @ApiModelProperty(value = "返回结果") private T response; public Result() {} private Result(ErrorCode errorCode) { this.meta = new ResultMeta(errorCode); } public static Result success() { return new Result<>(ErrorCode.SUCCESS); } public static Result fail(ErrorCode errorCode) { return new Result<>(errorCode); } public static boolean isSuccess(Result result) { return result != null && result.meta != null && result.meta.getCode() == 0; } public Result withErrorMsg(String msg) { this.meta.setMsg(msg); return this; } public Result withResponse(T response) { this.response = response; return this; } @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); } } ================================================ FILE: chat_rest/src/main/java/com/am/chat/bi/comm/ResultMeta.java ================================================ package com.am.chat.bi.comm; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * @Author: wpl */ @Data @ApiModel public class ResultMeta { @ApiModelProperty(value = "错误码", required = true) private int code; @ApiModelProperty(value = "描述", required = true) private String msg; public ResultMeta() {} public ResultMeta(ErrorCode errorCode) { this.code = errorCode.getCode(); this.msg = errorCode.getMsg(); } void setMsg(String msg) { this.msg = this.msg + " : " + msg; } } ================================================ FILE: chat_rest/src/main/java/com/am/chat/bi/comm/StateCode.java ================================================ package com.am.chat.bi.comm; /** * @author wpl */ public enum StateCode { IS_ADD(0), IS_UPDATE(1), IS_DEL(2) ; private Integer code; StateCode(Integer code) { this.code = code; } public static StateCode getStateCode(Integer code) { switch (code){ case 1: return IS_UPDATE; case 2: return IS_DEL; default: return IS_ADD; } } public Integer getCode() { return code; } } ================================================ FILE: chat_rest/src/main/java/com/am/chat/bi/comm/Utils.java ================================================ package com.am.chat.bi.comm; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.atomic.AtomicLong; public class Utils { private static final Logger LOG = LoggerFactory.getLogger(Utils.class); public static String getStringDate(Date date) { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = formatter.format(date); return dateString; } public static Date getStringToDate(String date) { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { Date result = formatter.parse(date); return result; } catch (ParseException e) { e.printStackTrace(); LOG.error("string date 解析出错", e); } return null; } } ================================================ FILE: chat_rest/src/main/java/com/am/chat/bi/config/SwaggerConfiguration.java ================================================ package com.am.chat.bi.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** * @author wpl */ @EnableSwagger2 @Configuration public class SwaggerConfiguration { @Value("${com.am.chat.bi.api.doc.path:com.am.chat.bi}") private String path; @Value("${com.am.chat.bi.api.doc.title:Chat BI Platform API doc}") private String title; @Value("${com.am.chat.bi.api.doc.version:1.0}") private String version; @Value("${com.am.chat.bi.api.doc.description:Chat BI Platform API doc}") private String description; @Bean public Docket createApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .pathMapping("/") .select() .apis(RequestHandlerSelectors.basePackage(path)) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title(title) .description(description) .version(version) .build(); } } ================================================ FILE: chat_rest/src/main/java/com/am/chat/bi/controller/LLMsController.java ================================================ package com.am.chat.bi.controller; import com.am.chat.bi.comm.Result; import com.am.chat.bi.service.LLMsService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/chat") @Api(value = "大模型服务", description = "大模型服务", tags = {"LLMs APIß"}) public class LLMsController { @Autowired LLMsService llMsService; @PostMapping(value = "/intention", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) @ApiOperation(value = "查询意图", notes = "查询意图", response = Result.class) public ResponseEntity intention(@RequestBody String queryContent) { Result result = llMsService.getIntention(queryContent); return new ResponseEntity<>(result, HttpStatus.OK); } @PostMapping(value = "/dimMetrics", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) @ApiOperation(value = "查询维度指标", notes = "查询维度指标", response = Result.class) public ResponseEntity dimMetrics(@RequestBody String queryContent) { Result result = llMsService.getDimMetric(queryContent); return new ResponseEntity<>(result, HttpStatus.OK); } @PostMapping(value = "/querySql", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) @ApiOperation(value = "生成查询sql", notes = "生成查询sql", response = Result.class) public ResponseEntity querySql(@RequestBody String queryContent) { Result result = llMsService.getSql(queryContent); return new ResponseEntity<>(result, HttpStatus.OK); } } ================================================ FILE: chat_rest/src/main/java/com/am/chat/bi/domain/dao/SchemaMetaInfo.java ================================================ package com.am.chat.bi.domain.dao; public class SchemaMetaInfo { } ================================================ FILE: chat_rest/src/main/java/com/am/chat/bi/domain/vo/ResponseVo.java ================================================ package com.am.chat.bi.domain.vo; import lombok.Data; /** * @author wpl * @param */ @Data public class ResponseVo { //返回插入或者修改的主键id private String name; private T value; } ================================================ FILE: chat_rest/src/main/java/com/am/chat/bi/service/LLMsService.java ================================================ package com.am.chat.bi.service; import com.am.chat.bi.comm.Result; public interface LLMsService { Result getIntention(String queryContent); Result getDimMetric(String queryContent); Result getSql(String queryContent); } ================================================ FILE: chat_rest/src/main/java/com/am/chat/bi/service/impl/LLMsServiceImpl.java ================================================ package com.am.chat.bi.service.impl; import com.am.chat.bi.comm.ErrorCode; import com.am.chat.bi.comm.Result; import com.am.chat.bi.domain.vo.ResponseVo; import com.am.chat.bi.service.LLMsService; import com.am.chat.bi.utils.AliLLMsUtil; import com.am.chat.bi.utils.LLMsResultUtil; import com.am.chat.bi.utils.PromptUtil; import com.google.gson.JsonElement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @Service public class LLMsServiceImpl implements LLMsService { private static final Logger LOG = LoggerFactory.getLogger(LLMsServiceImpl.class); @Override public Result getIntention(String queryContent) { LOG.info("chat query content: {}", queryContent); try { String intentionPrompt = PromptUtil.getIntentionPrompt(queryContent); String intentionResult = AliLLMsUtil.getResult(intentionPrompt); String result = LLMsResultUtil.getIntentionResult(intentionResult); ResponseVo responseVo = new ResponseVo(); responseVo.setName("result"); responseVo.setValue(result); return Result.success().withResponse(responseVo); } catch (Exception e) { LOG.error("用户查询意图大模型结果判断异常:", e); return Result.fail(ErrorCode.FAIL_LLMs).withErrorMsg(e.getMessage()); } } @Override public Result getDimMetric(String queryContent) { LOG.info("chat query content: {}", queryContent); try { String dimensionMetricPrompt = PromptUtil.getDimensionMetricPrompt(queryContent); String dimensionMetricResult = AliLLMsUtil.getResult(dimensionMetricPrompt); String dimMetricJson = LLMsResultUtil.getDimMetricJson(dimensionMetricResult); JsonElement dimensions = LLMsResultUtil.getJsonValue(dimMetricJson, "dimensions"); JsonElement metrics = LLMsResultUtil.getJsonValue(dimMetricJson, "metrics"); ResponseVo responseVo = new ResponseVo(); responseVo.setName("dimensions"); responseVo.setValue(dimensions); responseVo.setName("metrics"); responseVo.setValue(metrics); return Result.success().withResponse(responseVo); } catch (Exception e) { LOG.error("用户查询大模型维度指标解析异常:", e); return Result.fail(ErrorCode.FAIL_LLMs).withErrorMsg(e.getMessage()); } } @Override public Result getSql(String queryContent) { LOG.info("chat query content: {}", queryContent); try { //TODO: 待补充数据库类型,基于解析的元数据信息 String schemaInfo = "{\n" + " \"schema\":\"report_db\",\n" + " \"tables\":[\n" + " \"dim_table\",\n" + " \"metric_table\"\n" + " ],\n" + " \"dim_table\":[\n" + " {\n" + " \"dim_date\":\"date\",\n" + " \"dim_app_id\":\"int\",\n" + " \"dim_app_name\":\"varchar\"\n" + " },\n" + " {\n" + " \"count_date\":\"date\",\n" + " \"app_id\":\"int\",\n" + " \"app_pv\":\"bigint\",\n" + " \"app_uv\":\"bigint\"\n" + " }\n" + " ]\n" + "}"; String sqlPrompt = PromptUtil.getSqlPrompt(queryContent,"postgresql",schemaInfo, null); LOG.info("LLMs sql prompt content: {}", sqlPrompt); String sqlResult = AliLLMsUtil.getResult(sqlPrompt); String result = LLMsResultUtil.getSqlResult(sqlResult); ResponseVo responseVo = new ResponseVo(); responseVo.setName("result"); responseVo.setValue(result); return Result.success().withResponse(responseVo); } catch (Exception e) { LOG.error("用户查询大模型生成sql异常:", e); return Result.fail(ErrorCode.FAIL_LLMs).withErrorMsg(e.getMessage()); } } } ================================================ FILE: chat_rest/src/main/resources/application-dev.yml ================================================ server: port: 8498 security: basic: enabled=false: spring: datasource: name: dev url: jdbc:mysql://127.0.0.1:3306/chat_bi?characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true username: root password: 123456 ================================================ FILE: chat_rest/src/main/resources/application.yml ================================================ server: context-path: /chatBI tomcat: accesslog: enabled: false buffered: false directory: pattern: common awaitTerminationInSec: 30 spring: profiles: ## dev, test, prod active: dev datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver druid: filters: # 初始化大小,最小,最大 initial-size: 1 max-active: 20 min-idle: 1 # 配置获取连接等待超时的时间 max-wait: 5000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 time-between-eviction-runs-millis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 min-evictable-idle-time-millis: 30000 validation-query: select 'x' test-while-idle: true test-on-borrow: false test-on-return: false pool-prepared-statements: true max-open-prepared-statements: 20 # 配置监控页面密码登录 # stat-view-servlet: # login-password: druid # login-username: druid http: multipart: max-file-size: 1024MB max-request-size: 1024MB mybatis: mapperLocations: classpath:mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl pagehelper: helperDialect: mysql reasonable: true management: health: redis: enabled: false ================================================ FILE: chat_rest/src/main/resources/assembly.xml ================================================ ${project.version} tar.gz true lib false ${project.basedir} / README* LICENSE* NOTICE* src/main/resources config/ *.xml *.yml static/ templates/ mapper/ logback-test.xml generatorConfig.xml application-*.yml src/main/resources config/ application-${env}.yml ${project.build.scriptSourceDirectory} bin *.* true 0755 src/main/resources/${env} bin setenv.sh ${project.build.directory} *.jar ================================================ FILE: chat_rest/src/main/resources/dev/setenv.sh ================================================ #!/bin/sh PRG="$0" PRGDIR=`dirname "$PRG"` BASE_HOME=`cd "$PRGDIR/.." >/dev/null; pwd` #server内存配置 export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -server -Xms1G -Xmx1G -Xmn512M -XX:PermSize=128M -XX:MaxPermSize=256M -Xss512k -XX:SurvivorRatio=8" #gc export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -XX:MaxTenuringThreshold=4 -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=2 -XX:+ExplicitGCInvokesConcurrent -XX:+CMSScavengeBeforeRemark" #gc日志 export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -Xloggc:$BASE_HOME/logs/jvm/gc.log.`date +%Y-%m-%d_%H_%M_%S` \ -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime -XX:PrintFLSStatistics=1" #内存溢出 export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$BASE_HOME/logs/heapdump.hprof" ================================================ FILE: chat_rest/src/main/resources/logback-dev.xml ================================================ UTF-8 %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: chat_rest/src/main/resources/logback.xml ================================================ UTF-8 %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n INFO DEBUG ${log.home}/label_%d{yyyy-MM-dd}_%i.log 100MB 7 10GB UTF-8 %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: chat_rest/src/main/resources/press/setenv.sh ================================================ #!/bin/sh PRG="$0" PRGDIR=`dirname "$PRG"` BASE_HOME=`cd "$PRGDIR/.." >/dev/null; pwd` #server内存配置 export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -server -Xms4G -Xmx4G -Xmn2G -XX:PermSize=256M -XX:MaxPermSize=512M -Xss1M -XX:SurvivorRatio=8" #gc export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -XX:MaxTenuringThreshold=4 -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=2 -XX:+ExplicitGCInvokesConcurrent -XX:+CMSScavengeBeforeRemark" #gc日志 export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -Xloggc:$BASE_HOME/logs/jvm/gc.log.`date +%Y-%m-%d_%H_%M_%S` \ -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime -XX:PrintFLSStatistics=1" #内存溢出 export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$BASE_HOME/logs/heapdump.hprof" ================================================ FILE: chat_rest/src/main/resources/prod/setenv.sh ================================================ #!/bin/sh PRG="$0" PRGDIR=`dirname "$PRG"` BASE_HOME=`cd "$PRGDIR/.." >/dev/null; pwd` #server内存配置 export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -server -Xms4G -Xmx4G -Xmn2G -XX:PermSize=256M -XX:MaxPermSize=512M -Xss1M -XX:SurvivorRatio=8" #gc export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -XX:MaxTenuringThreshold=4 -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=2 -XX:+ExplicitGCInvokesConcurrent -XX:+CMSScavengeBeforeRemark" #gc日志 export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -Xloggc:$BASE_HOME/logs/jvm/gc.log.`date +%Y-%m-%d_%H_%M_%S` \ -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime -XX:PrintFLSStatistics=1" #内存溢出 export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$BASE_HOME/logs/heapdump.hprof" ================================================ FILE: chat_rest/src/main/resources/sql/mysql.sql ================================================ USE chat_bi; DROP TABLE IF EXISTS `scheam_meta_info`; CREATE TABLE `scheam_meta_info` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `meta_name` varchar(64) NOT NULL COMMENT '维度,指标名称', `meta_zh_name` varchar(128) NOT NULL COMMENT '维度,指标中文名称', `meta_type` int(1) NOT NULL DEFAULT '1' COMMENT '维度1,指标2', `meta_expression` varchar(128) DEFAULT NULL COMMENT '维度,指标表达式', `meta_table_name` varchar(64) DEFAULT NULL COMMENT '维度,指标来源表名称', `meta_state` int(1) NOT NULL DEFAULT '0' COMMENT '操作类型,0:新增,1:更新,2:删除', `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='维度指标元数据信息表'; ================================================ FILE: chat_rest/src/main/resources/test/setenv.sh ================================================ #!/bin/sh PRG="$0" PRGDIR=`dirname "$PRG"` BASE_HOME=`cd "$PRGDIR/.." >/dev/null; pwd` #server内存配置 export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -d64 -server -Xms4G -Xmx4G -Xmn2G -XX:PermSize=256M -XX:MaxPermSize=512M -Xss1M -XX:SurvivorRatio=8" #gc export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -XX:MaxTenuringThreshold=4 -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=2 -XX:+ExplicitGCInvokesConcurrent -XX:+CMSScavengeBeforeRemark" #gc日志 export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -Xloggc:$BASE_HOME/logs/jvm/gc.log.`date +%Y-%m-%d_%H_%M_%S` \ -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime -XX:PrintFLSStatistics=1" #内存溢出 export JAVA_MEM_OPTS="$JAVA_MEM_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$BASE_HOME/logs/heapdump.hprof" ================================================ FILE: chat_rest/src/main/shell/comm.sh ================================================ #!/bin/sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ]; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done COMMAND="$1" # Get standard environment variables PRG_DIR=`dirname "$PRG"` PLATFORM_HOME=`cd "$PRG_DIR/.." >/dev/null; pwd` export PLATFORM_HOME PLATFORM_CONF_DIR="$PLATFORM_HOME/config" PLATFORM_LIB_DIR="$PLATFORM_HOME/lib" export PLATFORM_LIB_DIR LIB_JARS=`ls $PLATFORM_LIB_DIR | grep -E '.jar$' | awk '{print "'$PLATFORM_LIB_DIR'/"$0}' | tr "\n" ":"` PRO_NAME=${project.artifactId} PIDFILE="$PLATFORM_HOME/$(basename $PRO_NAME).pid" PID=0 if [[ -f $PIDFILE ]]; then PID=`cat $PIDFILE` fi JAVA_OPTS="" JAVA_MEM_OPTS="" JAVA_SPRING_OPTS=" --spring.profiles.active=${env}" PLATFORM_MAIN_CLASS=${start-class} running() { if [[ -z $1 || $1 == 0 ]]; then echo 0 return fi if ps -p $1 > /dev/null; then echo 1 return fi echo 0 return } set_env() { if [ -r "$PLATFORM_HOME/bin/setenv.sh" ]; then source "$PLATFORM_HOME/bin/setenv.sh" else JAVA_MEM_OPTS=" -server -Xms4g -Xmx4g -XX:SurvivorRatio=2 -XX:+UseParallelGC " fi } start() { if [[ $(running $PID) != 0 ]]; then echo "$PRO_NAME is running" return fi echo "### starting $PRO_NAME `date '+%Y-%m-%d %H:%M:%S'` ###" >> /dev/null 2>&1 & set_env echo "$JAVA_HOME/bin/java $JAVA_OPTS $JAVA_MEM_OPTS -classpath $PLATFORM_CONF_DIR:$LIB_JARS $PLATFORM_MAIN_CLASS $JAVA_SPRING_OPTS" START_CMD="$JAVA_HOME/bin/java $JAVA_OPTS $JAVA_MEM_OPTS -classpath $PLATFORM_CONF_DIR:$LIB_JARS $PLATFORM_MAIN_CLASS $JAVA_SPRING_OPTS" print_env nohup $START_CMD >> $PLATFORM_HOME/server.log 2>&1 & if [[ $(running $!) == 0 ]]; then echo "failed to start $PRO_NAME" exit 1 fi PID=$! echo $! > $PIDFILE # echo "new pid $!" } stop() { if [[ $(running $PID) == 0 ]]; then echo "no $PRO_NAME is running" return fi echo "stopping $PID of $PRO_NAME ..." kill $PID } restart() { stop start } print_env() { echo "JRE_HOME: $JAVA_HOME" echo "ENV: ${env}" echo "JAVA_OPTS: $JAVA_OPTS" echo "JAVA_MEM_OPTS: $JAVA_MEM_OPTS" echo "JAVA_SPRING_OPTS: $JAVA_SPRING_OPTS" echo "START_CMD: $START_CMD" } print_usage() { echo "Usage: label.sh (start|stop)" } case $COMMAND in (start) start ;; (stop) stop ;; (*) print_usage exit 1 ;; esac ================================================ FILE: chat_rest/src/main/shell/start.sh ================================================ #!/bin/sh PRG="$0" PRGDIR=`dirname "$PRG"` PLATFORM_HOME=`cd "$PRGDIR/.." >/dev/null; pwd` EXECUTABLE=comm.sh exec "$PRGDIR"/"$EXECUTABLE" start "$@" ================================================ FILE: chat_rest/src/main/shell/stop.sh ================================================ #!/bin/sh PRG="$0" PRGDIR=`dirname "$PRG"` PLATFORM_HOME=`cd "$PRGDIR/.." >/dev/null; pwd` EXECUTABLE=comm.sh exec "$PRGDIR"/"$EXECUTABLE" stop "$@" ================================================ FILE: chat_ui/pom.xml ================================================ chatBI com.am 1.0-SNAPSHOT 4.0.0 chat_ui ================================================ FILE: plugins/pom.xml ================================================ chatBI com.am 1.0-SNAPSHOT 4.0.0 plugins plugins com.google.code.gson gson com.alibaba dashscope-sdk-java ch.qos.logback logback-classic ch.qos.logback logback-core ================================================ FILE: plugins/src/main/java/com/am/chat/bi/utils/AliLLMsUtil.java ================================================ package com.am.chat.bi.utils; import com.alibaba.dashscope.aigc.generation.Generation; import com.alibaba.dashscope.aigc.generation.GenerationResult; import com.alibaba.dashscope.aigc.generation.models.QwenParam; import com.alibaba.dashscope.common.ResultCallback; import com.alibaba.dashscope.utils.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.Semaphore; public class AliLLMsUtil { private static final Logger LOG = LoggerFactory.getLogger(AliLLMsUtil.class); static { Constants.apiKey = "sk-547"; } public static String getResult(String prompt) throws Exception { Generation gen = new Generation(); QwenParam param = QwenParam.builder().model(Generation.Models.QWEN_TURBO).prompt(prompt) .topP(0.8).build(); Semaphore semaphore = new Semaphore(0); final String[] result = {null}; gen.call(param, new ResultCallback() { @Override public void onEvent(GenerationResult message) { System.out.println(message); result[0] = message.getOutput().getText(); LOG.info("阿里大模型返回结果:{}",message); } @Override public void onError(Exception ex){ System.out.println(ex.getMessage()); semaphore.release(); } @Override public void onComplete(){ System.out.println("onComplete"); semaphore.release(); } }); semaphore.acquire(); return result[0]; } } ================================================ FILE: plugins/src/main/java/com/am/chat/bi/utils/LLMsResultUtil.java ================================================ package com.am.chat.bi.utils; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import java.util.regex.Matcher; import java.util.regex.Pattern; public class LLMsResultUtil { public static String getIntentionResult(String result){ String intentionResult = null; Pattern pattern = Pattern.compile("(\\d+)"); Matcher matcher = pattern.matcher(result); if (matcher.find()) { intentionResult = matcher.group(); } return intentionResult; } public static JsonElement getJsonValue(String result, String name) { JsonParser parser = new JsonParser(); JsonObject jsonObject = parser.parse(result).getAsJsonObject(); return jsonObject.get(name); } public static String getDimMetricJson(String result){ String sqlResult = null; // Pattern pattern = Pattern.compile("```json(.*?)```"); Pattern pattern = Pattern.compile("\\{.*\\}"); Matcher matcher = pattern.matcher(result); if (matcher.find()) { sqlResult = matcher.group(); } return sqlResult; } public static String getSqlResult(String result){ String sqlResult = null; Pattern pattern = Pattern.compile("querySql: \"(.*?)\"}"); Matcher matcher = pattern.matcher(result); if (matcher.find()) { sqlResult = matcher.group(); } return sqlResult; } } ================================================ FILE: plugins/src/main/java/com/am/chat/bi/utils/PromptUtil.java ================================================ package com.am.chat.bi.utils; public class PromptUtil { /** * 查询意图 * @param queryContent * @return */ public static String getIntentionPrompt(String queryContent) { String intentionPrompt = "“" + queryContent + "” 请帮忙判断这句话的意图是需要" + "知识库还是数据查询。如果意图是知识库的话result返回值是1,是数据查询的话result返回值是2。" + "最后返回值采用json数据格式,返回值格式为{result:result_$返回值}" ; return intentionPrompt; } public static String getDimensionMetricPrompt(String queryContent) { String dimMetricPrompt = "“" + queryContent + "” 请帮忙解析这句话中含有的维度与指标。解析要求:" + "1,语句中除了指标名词外,默认其他名词为维度。" + "2,最后的返回结果采用json数据格式,指定返回格式为{dimensions:array[$维度名称],metrics:array[$指标名称]}。"; return dimMetricPrompt; } public static String getSqlPrompt(String queryContent, String dbType, String schemaInfo, String dbDialect) { String sqlPrompt = "假设你是"+dbType+"的专家,需要通过问题描述和指令语句两部分内容帮忙生成对应查询SQL语句。第一部分问题说明:\n" + "“" + queryContent + "” \n" + "第二部分指令内容:\n" + "1,不能幻觉出现新的字段,schema字段、表名称、表字段名称必须使用提供的元数据内容信息,元数据信息json格式数据 " + schemaInfo + " \n" + "2,可能需要关联表的查询SQL才满足问题内容的要求,关联的表和表字段一定存在已经提供的元数据内容。\n" + "3,生产的sql语句使用json格式返回,返回数据格式要求{querySQL: $生成的结果SQL}。"; return sqlPrompt; } } ================================================ FILE: pom.xml ================================================ 4.0.0 com.am chatBI pom 1.0-SNAPSHOT chat_rest chat_ui plugins UTF-8 8 8 1.5.9.RELEASE 4.3.13.RELEASE 1.3.2 1.1.10 1.1.1 3.7 1.2.47 2.7.0 1.2.3 1.18.0 2.7.2 5.1.46 org.springframework.boot spring-boot-starter ${spring-boot-starter-parent} logback-classic ch.qos.logback org.springframework.boot spring-boot-starter-web ${spring-boot-starter-parent} org.springframework spring-context-support ${spring-content.version} org.springframework.boot spring-boot-starter-websocket ${spring-boot-starter-parent} org.springframework.boot spring-boot-configuration-processor ${spring-boot-starter-parent} true org.springframework.boot spring-boot-starter-test ${spring-boot-starter-parent} test io.springfox springfox-swagger2 ${swagger.version} io.springfox springfox-swagger-ui ${swagger.version} com.github.pagehelper pagehelper-spring-boot-starter ${pagehelper} mybatis-spring-boot-starter org.mybatis.spring.boot org.mybatis.spring.boot mybatis-spring-boot-starter ${mybatis-spring-boot-starter} org.springframework.boot spring-boot-starter com.alibaba druid-spring-boot-starter ${druid} mysql mysql-connector-java ${mysql.version} org.mybatis.spring.boot mybatis-spring-boot-starter-test ${mybatis-spring-boot-starter} org.projectlombok lombok ${lombok.version} org.apache.commons commons-lang3 ${commons-lang3} ch.qos.logback logback-classic ${logback.version} logback-core ch.qos.logback ch.qos.logback logback-core ${logback.version} com.google.code.gson gson 2.10.1 com.alibaba dashscope-sdk-java 2.6.1 redis.clients jedis ${redis.version}