智能BI(后端) -- 智能分析业务

发布于:2024-05-09 ⋅ 阅读:(89) ⋅ 点赞:(0)

业务流程

  1. 用户输入

a. 分析目标
b. 上传原始数据
c. 更精细话地控制图表,比如图表类型,图表名称等

  1. 后端校验

    a. 校验用户的输入是否合法
    b. 成本控制(次数统计和校验,鉴权等)

  2. 把处理后的数据输入给AI模型(调用AI接口),让AI模型给我们提供图表信息,结论文本

  3. 图表信息(是一段json配置,是一段代码),结论文本在前端进行展示

开发接口

easyExcel从excel读取数据,数据过滤并进行数据压缩

不要想复杂啦,读取数据很正常,数据过滤就是过滤掉null的数据,数据压缩转化为csv,其实就是转换格式,用英文逗号分割,节省空间,给个预设语句,可以使得AI回答地更准确

package com.yupi.springbootinit.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * excel相关工具类
 */
@Slf4j
public class ExcelUtils {
    /**
     * 数据压缩为csv,返回字符串
     */
    public static String excelToCsv(MultipartFile multipartFile){
        //读取数据
        List<Map<Integer, String>> list = null;
        try {
            list = EasyExcel.read(multipartFile.getInputStream())
                    .excelType(ExcelTypeEnum.XLSX)
                    .sheet()
                    .headRowNumber(0)
                    .doReadSync();
        } catch (IOException e) {
            log.error("表格处理错误");
        }
        System.out.println(list);
        //如果数据为空
        if(CollUtil.isEmpty(list)){
            return "";
        }
        //转换为csv (数据压缩)
        StrBuilder strBuilder = new StrBuilder();
        //过滤表头数据
        //todo 问为什么要用LinkedHashMap呢?
        LinkedHashMap<Integer, String> headMap = (LinkedHashMap<Integer, String>) list.get(0);
        List<String> headValueList = headMap.values().stream().filter(item -> ObjectUtil.isNotEmpty(item)).collect(Collectors.toList());
        String head = StringUtils.join(headValueList, ",");
        System.out.println(head);
        strBuilder.append(head).append("\n");
        //过滤表数据
        for (int i = 1; i < list.size(); i ++){
            LinkedHashMap<Integer, String> dataListMap = (LinkedHashMap)list.get(i);
            List<String> dataList = dataListMap.values().stream().filter(item -> ObjectUtil.isNotEmpty(item)).collect(Collectors.toList());
            String data = StringUtils.join(dataList, ",");
            System.out.println(data);
            strBuilder.append(data).append("\n");
        }
        return strBuilder.toString();
    }

    /**
     * test
     * @param args
     */
    public static void main(String[] args) {
        excelToCsv(null);
    }
}

分析接口

    /**
     * 上传文件,返回数据
     *
     * @param multipartFile
     * @param genChartByAiRequest
     * @param request
     * @return
     */
    @PostMapping("/gen")
    public BaseResponse<String> genChartByAi(@RequestPart("file") MultipartFile multipartFile,
                                           GenChartByAiRequest genChartByAiRequest, HttpServletRequest request) {
        String name = genChartByAiRequest.getName();
        String goal = genChartByAiRequest.getGoal();
        String chartType = genChartByAiRequest.getChartType();
        //分析目标为空抛出异常
        ThrowUtils.throwIf(StringUtils.isBlank(goal),ErrorCode.PARAMS_ERROR,"分析目标为空");
        //名称不为空并且名称>100 提示名称过长
        ThrowUtils.throwIf(StringUtils.isNotBlank(name) && name.length() > 100,ErrorCode.PARAMS_ERROR,"图表名称过长");
        StrBuilder userInput = new StrBuilder();
        userInput.append("你是一个数据分析师,接下来我会给你我的分析目标和原始数据,请告诉我分析结论:").append("\n");
        userInput.append("分析目标:").append(goal).append("\n");
        String result = ExcelUtils.excelToCsv(multipartFile);
        userInput.append("数据:").append(result).append("\n");
        return ResultUtils.success(userInput.toString());
    }

利用AI生成结论和图表

生成结论

生成图表:
AI无法直接生成现成的图表,但是AI可以生成图表代码,可以把代码利用前端组件库(Echarts)在网页进行展示

预期生成的图表代码

AI提问技巧

如果想让AI更好的理解我们的输入,给我们预期的,精确格式的输出,我们就需要严格控制咱们的提问词
0)使用系统预设
1)控制输入格式(便于AI精确地理解我们的需求和提问)
2)控制输出格式(便于AI返回的内容能够更加方便的为我们所用)
3)指定一个示例问答

调用AI

直接调用AI大模型官网的接口

官方文档:https://platform.openai.com/docs/api-reference
优点:不经封装,最灵活,最原始
缺点:要钱,要魔法
本质上openAI就是提供了Http接口,我们可以用任何语言去调用
1)在请求头中指定OPENAI_API_KEY
2)找到你要使用的接口,比如AI对话接口
3)按照接口文档的示例,构造Http请求,比如用Hutool工具类,或者HttpClient

/**
 * AI 对话(需要自己创建请求响应对象)
 *
 * @param request
 * @param openAiApiKey
 * @return
 */
public CreateChatCompletionResponse createChatCompletion(CreateChatCompletionRequest request, String openAiApiKey) {
    if (StringUtils.isBlank(openAiApiKey)) {
        throw new BusinessException(ErrorCode.PARAMS_ERROR, "未传 openAiApiKey");
    }
    String url = "https://api.openai.com/v1/chat/completions";
    String json = JSONUtil.toJsonStr(request);
    String result = HttpRequest.post(url)
    .header("Authorization", "Bearer " + openAiApiKey)
    .body(json)
    .execute()
    .body();
    return JSONUtil.toBean(result, CreateChatCompletionResponse.class);
}

使用云服务商提供的,封装后的AI接口

比如:Azure云
优点:不用魔法
缺点:依然要钱,可能比直接调用原始的接口更贵

利用鱼聪明AI提供的开放SDK

优点:有很多现成的模型,可以预设
https://github.com/liyupi/yucongming-java-sdk

智能接口实现

echarts在线调试:https://echarts.apache.org/examples/zh/editor.html?c=line-simple

  1. 构造用户请求(用户消息,csv,图表类型)
  2. 调用鱼聪明sdk,得到AI响应结果
  3. 从AI响应结果中,取出需要的信息
  4. 保存图表到数据库