From 49546de3513a0e6b0fff875ca67ad19559908217 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Thu, 12 Dec 2024 16:05:05 +0800 Subject: [PATCH 1/2] =?UTF-8?q?perf:=20job-analysis=E9=83=A8=E5=88=86?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96=20#3322?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 重构部分代码,补充部分单元测试。 --- .../job/analysis/api/dto/StatisticsDTO.java | 6 +- .../model/web/CommonStatisticWithRateVO.java | 4 +- .../bk/job/analysis/consts/DataTrendEnum.java | 52 ++++++++ .../analysis/service/AppStatisticService.java | 2 +- .../service/CommonStatisticService.java | 2 +- .../bk/job/analysis/util/ai/AIAnswerUtil.java | 43 ++++--- .../util/calc/AbstractMomYoyCalculator.java | 113 ++++++++++++------ .../util/calc/AppMomYoyCalculator.java | 12 +- .../util/calc/SimpleMomYoyCalculator.java | 13 +- .../analysis/util/ai/AIAnswerUtilTest.java | 80 +++++++++++++ .../util/calc/AppMomYoyCalculatorTest.java | 98 +++++++++++++++ .../util/calc/SimpleMomYoyCalculatorTest.java | 92 ++++++++++++++ 12 files changed, 449 insertions(+), 68 deletions(-) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/DataTrendEnum.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/test/java/com/tencent/bk/job/analysis/util/ai/AIAnswerUtilTest.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/test/java/com/tencent/bk/job/analysis/util/calc/AppMomYoyCalculatorTest.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/test/java/com/tencent/bk/job/analysis/util/calc/SimpleMomYoyCalculatorTest.java diff --git a/src/backend/job-analysis/api-common-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/dto/StatisticsDTO.java b/src/backend/job-analysis/api-common-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/dto/StatisticsDTO.java index 89eff17146..90c142ec63 100644 --- a/src/backend/job-analysis/api-common-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/dto/StatisticsDTO.java +++ b/src/backend/job-analysis/api-common-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/dto/StatisticsDTO.java @@ -62,7 +62,7 @@ public class StatisticsDTO implements Cloneable { */ private String date; /** - * 统计值 + * 序列化的统计数据 */ private String value; /** @@ -74,6 +74,10 @@ public class StatisticsDTO implements Cloneable { */ private Long lastModifyTime; + public StatisticsDTO(String value) { + this.value = value; + } + @Override public StatisticsDTO clone() { StatisticsDTO statisticsDTO = new StatisticsDTO(); diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/CommonStatisticWithRateVO.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/CommonStatisticWithRateVO.java index 5963d09091..bfc96c172c 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/CommonStatisticWithRateVO.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/CommonStatisticWithRateVO.java @@ -52,9 +52,9 @@ public class CommonStatisticWithRateVO { private Float momRate; @ApiModelProperty("同比趋势:1:上升,0:不变,-1:下降") - private Long yoyTrend; + private Integer yoyTrend; @ApiModelProperty("环比趋势:1:上升,0:不变,-1:下降") - private Long momTrend; + private Integer momTrend; } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/DataTrendEnum.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/DataTrendEnum.java new file mode 100644 index 0000000000..9cd9045c48 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/DataTrendEnum.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.consts; + +import lombok.Getter; + +/** + * 数据趋势 + */ +@Getter +public enum DataTrendEnum { + /** + * 上升 + */ + UP(1), + /** + * 不变 + */ + NOT_CHANGE(0), + /** + * 下降 + */ + DOWN(-1); + + private final int value; + + DataTrendEnum(int value) { + this.value = value; + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/AppStatisticService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/AppStatisticService.java index 21ca23d140..ac752971bb 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/AppStatisticService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/AppStatisticService.java @@ -65,7 +65,7 @@ public AppStatisticService(StatisticsDAO statisticsDAO, StatisticConfig statisti public CommonStatisticWithRateVO calcAppMomYoyStatistic(StatisticsDTO statisticsDTO, StatisticsDTO momStatisticsDTO, StatisticsDTO yoyStatisticsDTO) { - return new AppMomYoyCalculator(statisticsDTO, momStatisticsDTO, yoyStatisticsDTO).getResult(); + return new AppMomYoyCalculator(statisticsDTO, momStatisticsDTO, yoyStatisticsDTO).calc(); } public CommonStatisticWithRateVO getAppTotalStatistics(String username, List appIdList, String date) { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/CommonStatisticService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/CommonStatisticService.java index 1446446497..a54830c800 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/CommonStatisticService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/CommonStatisticService.java @@ -87,7 +87,7 @@ public List getJoinedAppIdList(String date) { */ public CommonStatisticWithRateVO calcMomYoyStatistic(StatisticsDTO statisticsDTO, StatisticsDTO momStatisticsDTO, StatisticsDTO yoyStatisticsDTO) { - return new SimpleMomYoyCalculator(statisticsDTO, momStatisticsDTO, yoyStatisticsDTO).getResult(); + return new SimpleMomYoyCalculator(statisticsDTO, momStatisticsDTO, yoyStatisticsDTO).calc(); } public CommonStatisticWithRateVO getCommonTotalStatistics(TotalMetricEnum metric, List appIdList, diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/ai/AIAnswerUtil.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/ai/AIAnswerUtil.java index 18a760369e..bd520f7f05 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/ai/AIAnswerUtil.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/ai/AIAnswerUtil.java @@ -36,6 +36,9 @@ import java.io.OutputStream; import java.nio.charset.StandardCharsets; +/** + * 处理AI回答内容、报错信息的工具类 + */ @Slf4j public class AIAnswerUtil { @@ -46,18 +49,7 @@ public class AIAnswerUtil { * @return 处理后的AI回答报错信息 */ public static String getLimitedErrorMessage(String errorMessage) { - if (errorMessage == null) { - return null; - } - if (errorMessage.length() > AIConsts.MAX_LENGTH_AI_ANSWER_ERROR_MESSAGE) { - log.info( - "aiAnswer errorMessage is too long({}), truncated to {}", - errorMessage.length(), - AIConsts.MAX_LENGTH_AI_ANSWER_ERROR_MESSAGE - ); - return StringUtil.substring(errorMessage, AIConsts.MAX_LENGTH_AI_ANSWER_ERROR_MESSAGE); - } - return errorMessage; + return getLimitedString(errorMessage, AIConsts.MAX_LENGTH_AI_ANSWER_ERROR_MESSAGE, "errorMessage"); } /** @@ -67,18 +59,31 @@ public static String getLimitedErrorMessage(String errorMessage) { * @return 处理后的AI回答 */ public static String getLimitedAIAnswer(String aiAnswer) { - if (aiAnswer == null) { + return getLimitedString(aiAnswer, AIConsts.MAX_LENGTH_AI_ANSWER, "aiAnswer"); + } + + /** + * 获取限长的字符串 + * + * @param rawString 原始字符串 + * @param maxLength 最大长度 + * @param stringDesc 原始字符串描述,用于日志打印 + * @return 处理后的AI回答 + */ + private static String getLimitedString(String rawString, int maxLength, String stringDesc) { + if (rawString == null) { return null; } - if (aiAnswer.length() > AIConsts.MAX_LENGTH_AI_ANSWER) { + if (rawString.length() > maxLength) { log.info( - "aiAnswer is too long({}), truncated to {}", - aiAnswer.length(), - AIConsts.MAX_LENGTH_AI_ANSWER + "{} is too long({}), truncated to {}", + stringDesc, + rawString.length(), + maxLength ); - return StringUtil.substring(aiAnswer, AIConsts.MAX_LENGTH_AI_ANSWER); + return StringUtil.substring(rawString, maxLength); } - return aiAnswer; + return rawString; } /** diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/calc/AbstractMomYoyCalculator.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/calc/AbstractMomYoyCalculator.java index e68db9ba11..58ba46ac0c 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/calc/AbstractMomYoyCalculator.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/calc/AbstractMomYoyCalculator.java @@ -25,68 +25,105 @@ package com.tencent.bk.job.analysis.util.calc; import com.tencent.bk.job.analysis.api.dto.StatisticsDTO; +import com.tencent.bk.job.analysis.consts.DataTrendEnum; import com.tencent.bk.job.analysis.model.web.CommonStatisticWithRateVO; +import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; +/** + * 抽象的同环比计算器 + */ @Slf4j public abstract class AbstractMomYoyCalculator { - StatisticsDTO statisticsDTO; - StatisticsDTO momStatisticsDTO; - StatisticsDTO yoyStatisticsDTO; + // 当前统计数据 + private final StatisticsDTO statisticsDTO; + // 环比上一周期的统计数据 + private final StatisticsDTO momStatisticsDTO; + // 同比上一周期的统计数据 + private final StatisticsDTO yoyStatisticsDTO; - public AbstractMomYoyCalculator(StatisticsDTO statisticsDTO, StatisticsDTO momStatisticsDTO, + public AbstractMomYoyCalculator(StatisticsDTO statisticsDTO, + StatisticsDTO momStatisticsDTO, StatisticsDTO yoyStatisticsDTO) { this.statisticsDTO = statisticsDTO; this.momStatisticsDTO = momStatisticsDTO; this.yoyStatisticsDTO = yoyStatisticsDTO; } - abstract Long getCountFromStatisticValue(String value); + /** + * 从序列化的存储数据中解析统计量数值 + * + * @param serializedData 序列化的存储数据 + * @return 统计量数值 + */ + protected abstract Long getCountFromSerializedData(String serializedData); - public CommonStatisticWithRateVO getResult() { + /** + * 根据几个时间点的统计数据计算同环比数据 + * + * @return 同环比数据 + */ + public CommonStatisticWithRateVO calc() { if (statisticsDTO == null) { return null; } - Long count = getCountFromStatisticValue(statisticsDTO.getValue()); + Long count = getCountFromSerializedData(statisticsDTO.getValue()); CommonStatisticWithRateVO commonStatisticWithRateVO = new CommonStatisticWithRateVO(); commonStatisticWithRateVO.setCount(count); if (momStatisticsDTO != null) { - Long momCount = getCountFromStatisticValue(momStatisticsDTO.getValue()); // 环比计算 - Long momValue = count - momCount; - Float momRate = 1f; - if (momCount > 0) { - momRate = (count - momCount) / (float) momCount; - } - long momTrend = 0; - if (momValue > 0) { - momTrend = 1; - } else if (momValue < 0) { - momTrend = -1; - } - commonStatisticWithRateVO.setMomValue(momValue); - commonStatisticWithRateVO.setMomRate(momRate); - commonStatisticWithRateVO.setMomTrend(momTrend); + Increment increment = calcIncrement(count, momStatisticsDTO); + commonStatisticWithRateVO.setMomValue(increment.getValue()); + commonStatisticWithRateVO.setMomRate(increment.getRate()); + commonStatisticWithRateVO.setMomTrend(increment.getTrend().getValue()); } if (yoyStatisticsDTO != null) { - Long yoyCount = getCountFromStatisticValue(yoyStatisticsDTO.getValue()); // 同比计算 - Long yoyValue = count - yoyCount; - Float yoyRate = 1f; - if (yoyCount > 0) { - yoyRate = (count - yoyCount) / (float) yoyCount; - } - long yoyTrend = 0; - if (yoyValue > 0) { - yoyTrend = 1; - } else if (yoyValue < 0) { - yoyTrend = -1; - } - - commonStatisticWithRateVO.setYoyValue(yoyValue); - commonStatisticWithRateVO.setYoyRate(yoyRate); - commonStatisticWithRateVO.setYoyTrend(yoyTrend); + Increment increment = calcIncrement(count, yoyStatisticsDTO); + commonStatisticWithRateVO.setYoyValue(increment.getValue()); + commonStatisticWithRateVO.setYoyRate(increment.getRate()); + commonStatisticWithRateVO.setYoyTrend(increment.getTrend().getValue()); } return commonStatisticWithRateVO; } + + /** + * 根据当前统计值与上一时间点的统计数据计算数据增量 + * + * @param count 当前统计值 + * @param previousStatisticsDTO 上一时间点统计数据 + * @return 数据增量 + */ + private Increment calcIncrement(Long count, StatisticsDTO previousStatisticsDTO) { + Long previousCount = getCountFromSerializedData(previousStatisticsDTO.getValue()); + // 增量计算 + long value = count - previousCount; + // 从无到有的初始增长率认定为1 + float rate = 1f; + if (previousCount > 0) { + rate = (count - previousCount) / (float) previousCount; + } + DataTrendEnum trend = DataTrendEnum.NOT_CHANGE; + if (value > 0) { + trend = DataTrendEnum.UP; + } else if (value < 0) { + trend = DataTrendEnum.DOWN; + } + return new Increment(value, rate, trend); + } + + /** + * 数据增量 + */ + @AllArgsConstructor + @Getter + static class Increment { + // 增量值 + private final long value; + // 增量比率 + private final float rate; + // 增量趋势 + private final DataTrendEnum trend; + } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/calc/AppMomYoyCalculator.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/calc/AppMomYoyCalculator.java index d46ba8bcc2..9db1a11085 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/calc/AppMomYoyCalculator.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/calc/AppMomYoyCalculator.java @@ -41,11 +41,17 @@ public AppMomYoyCalculator(StatisticsDTO statisticsDTO, StatisticsDTO momStatist super(statisticsDTO, momStatisticsDTO, yoyStatisticsDTO); } - Long getCountFromStatisticValue(String value) { + /** + * 从序列化的存储数据中解析出业务数量 + * + * @param serializedData 序列化的存储数据 + * @return 业务数量 + */ + protected Long getCountFromSerializedData(String serializedData) { // 解析统计量 - List applicationDTOList = JsonUtils.fromJson(value, + List applicationDTOList = JsonUtils.fromJson(serializedData, new TypeReference>() { - }); + }); return (long) applicationDTOList.size(); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/calc/SimpleMomYoyCalculator.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/calc/SimpleMomYoyCalculator.java index 54e785476a..b82a196ae8 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/calc/SimpleMomYoyCalculator.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/util/calc/SimpleMomYoyCalculator.java @@ -31,13 +31,20 @@ */ public class SimpleMomYoyCalculator extends AbstractMomYoyCalculator { - public SimpleMomYoyCalculator(StatisticsDTO statisticsDTO, StatisticsDTO momStatisticsDTO, + public SimpleMomYoyCalculator(StatisticsDTO statisticsDTO, + StatisticsDTO momStatisticsDTO, StatisticsDTO yoyStatisticsDTO) { super(statisticsDTO, momStatisticsDTO, yoyStatisticsDTO); } - Long getCountFromStatisticValue(String value) { + /** + * 从序列化的存储数据中解析出统计值数字 + * + * @param serializedData 序列化的存储数据 + * @return 统计值数字 + */ + protected Long getCountFromSerializedData(String serializedData) { // 解析统计量 - return Long.parseLong(value); + return Long.parseLong(serializedData); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/test/java/com/tencent/bk/job/analysis/util/ai/AIAnswerUtilTest.java b/src/backend/job-analysis/service-job-analysis/src/test/java/com/tencent/bk/job/analysis/util/ai/AIAnswerUtilTest.java new file mode 100644 index 0000000000..12787169ca --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/test/java/com/tencent/bk/job/analysis/util/ai/AIAnswerUtilTest.java @@ -0,0 +1,80 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.util.ai; + +import com.tencent.bk.job.analysis.consts.AIConsts; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 单元测试 - 处理AI回答内容、报错信息的工具类 + */ +public class AIAnswerUtilTest { + + @Test + public void testGetLimitedErrorMessage() { + assertThat(AIAnswerUtil.getLimitedErrorMessage(null)).isNull(); + String rawMessage = ""; + String limitedErrorMessage = AIAnswerUtil.getLimitedErrorMessage(rawMessage); + assertThat(limitedErrorMessage).isNotNull(); + assertThat(limitedErrorMessage).isEqualTo(rawMessage); + rawMessage = "123456"; + limitedErrorMessage = AIAnswerUtil.getLimitedErrorMessage(rawMessage); + assertThat(limitedErrorMessage).isEqualTo(rawMessage); + + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < 52; i++) { + stringBuilder.append("1234567890"); + } + rawMessage = stringBuilder.toString(); + limitedErrorMessage = AIAnswerUtil.getLimitedErrorMessage(rawMessage); + assertThat(limitedErrorMessage.length()).isEqualTo(AIConsts.MAX_LENGTH_AI_ANSWER_ERROR_MESSAGE); + assertThat(limitedErrorMessage).endsWith("12"); + } + + @Test + public void testGetLimitedAIAnswer() { + assertThat(AIAnswerUtil.getLimitedAIAnswer(null)).isNull(); + String rawAIAnswer = ""; + String limitedAIAnswer = AIAnswerUtil.getLimitedAIAnswer(rawAIAnswer); + assertThat(limitedAIAnswer).isNotNull(); + assertThat(limitedAIAnswer).isEqualTo(rawAIAnswer); + rawAIAnswer = "123456"; + limitedAIAnswer = AIAnswerUtil.getLimitedAIAnswer(rawAIAnswer); + assertThat(limitedAIAnswer).isEqualTo(rawAIAnswer); + + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < 1500; i++) { + stringBuilder.append("1234567890"); + } + stringBuilder.append("ABC"); + rawAIAnswer = stringBuilder.toString(); + limitedAIAnswer = AIAnswerUtil.getLimitedAIAnswer(rawAIAnswer); + assertThat(limitedAIAnswer.length()).isEqualTo(AIConsts.MAX_LENGTH_AI_ANSWER); + assertThat(limitedAIAnswer).endsWith("1234567890"); + } + +} diff --git a/src/backend/job-analysis/service-job-analysis/src/test/java/com/tencent/bk/job/analysis/util/calc/AppMomYoyCalculatorTest.java b/src/backend/job-analysis/service-job-analysis/src/test/java/com/tencent/bk/job/analysis/util/calc/AppMomYoyCalculatorTest.java new file mode 100644 index 0000000000..36e37ccc70 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/test/java/com/tencent/bk/job/analysis/util/calc/AppMomYoyCalculatorTest.java @@ -0,0 +1,98 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.util.calc; + +import com.tencent.bk.job.analysis.api.dto.StatisticsDTO; +import com.tencent.bk.job.analysis.consts.DataTrendEnum; +import com.tencent.bk.job.analysis.model.web.CommonStatisticWithRateVO; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 单元测试-解析存储JSON串的业务数据的同环比计算器 + */ +public class AppMomYoyCalculatorTest { + + @Test + public void testCalc() { + // 从0上升 + StatisticsDTO statisticsDTO = new StatisticsDTO( + "[{\"name\": \"app1\"},{\"name\": \"app2\"}," + + "{\"name\": \"app3\"},{\"name\": \"app4\"}]" + ); + StatisticsDTO momStatisticsDTO = new StatisticsDTO("[]"); + StatisticsDTO yoyStatisticsDTO = new StatisticsDTO("[]"); + AppMomYoyCalculator appMomYoyCalculator = new AppMomYoyCalculator( + statisticsDTO, + momStatisticsDTO, + yoyStatisticsDTO + ); + CommonStatisticWithRateVO commonStatisticWithRateVO = appMomYoyCalculator.calc(); + assertThat(commonStatisticWithRateVO.getCount()).isEqualTo(4L); + assertThat(commonStatisticWithRateVO.getMomValue()).isEqualTo(4L); + assertThat(commonStatisticWithRateVO.getMomRate()).isEqualTo(1.0f); + assertThat(commonStatisticWithRateVO.getMomTrend()).isEqualTo(DataTrendEnum.UP.getValue()); + assertThat(commonStatisticWithRateVO.getYoyValue()).isEqualTo(4L); + assertThat(commonStatisticWithRateVO.getYoyRate()).isEqualTo(1.0f); + assertThat(commonStatisticWithRateVO.getYoyTrend()).isEqualTo(DataTrendEnum.UP.getValue()); + // 非0上升 + momStatisticsDTO = new StatisticsDTO("[{\"name\": \"app1\"},{\"name\": \"app2\"}]"); + yoyStatisticsDTO = new StatisticsDTO("[{\"name\": \"app1\"}]"); + appMomYoyCalculator = new AppMomYoyCalculator( + statisticsDTO, + momStatisticsDTO, + yoyStatisticsDTO + ); + commonStatisticWithRateVO = appMomYoyCalculator.calc(); + assertThat(commonStatisticWithRateVO.getCount()).isEqualTo(4L); + assertThat(commonStatisticWithRateVO.getMomValue()).isEqualTo(2L); + assertThat(commonStatisticWithRateVO.getMomRate()).isEqualTo(1.0f); + assertThat(commonStatisticWithRateVO.getMomTrend()).isEqualTo(DataTrendEnum.UP.getValue()); + assertThat(commonStatisticWithRateVO.getYoyValue()).isEqualTo(3L); + assertThat(commonStatisticWithRateVO.getYoyRate()).isEqualTo(3.0f); + assertThat(commonStatisticWithRateVO.getYoyTrend()).isEqualTo(DataTrendEnum.UP.getValue()); + // 下降 + statisticsDTO = new StatisticsDTO("[{\"name\": \"app1\"}]"); + momStatisticsDTO = new StatisticsDTO("[{\"name\": \"app1\"},{\"name\": \"app2\"}]"); + yoyStatisticsDTO = new StatisticsDTO( + "[{\"name\": \"app1\"},{\"name\": \"app2\"}," + + "{\"name\": \"app3\"},{\"name\": \"app4\"}]" + ); + appMomYoyCalculator = new AppMomYoyCalculator( + statisticsDTO, + momStatisticsDTO, + yoyStatisticsDTO + ); + commonStatisticWithRateVO = appMomYoyCalculator.calc(); + assertThat(commonStatisticWithRateVO.getCount()).isEqualTo(1L); + assertThat(commonStatisticWithRateVO.getMomValue()).isEqualTo(-1L); + assertThat(commonStatisticWithRateVO.getMomRate()).isEqualTo(-0.5f); + assertThat(commonStatisticWithRateVO.getMomTrend()).isEqualTo(DataTrendEnum.DOWN.getValue()); + assertThat(commonStatisticWithRateVO.getYoyValue()).isEqualTo(-3L); + assertThat(commonStatisticWithRateVO.getYoyRate()).isEqualTo(-0.75f); + assertThat(commonStatisticWithRateVO.getYoyTrend()).isEqualTo(DataTrendEnum.DOWN.getValue()); + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/test/java/com/tencent/bk/job/analysis/util/calc/SimpleMomYoyCalculatorTest.java b/src/backend/job-analysis/service-job-analysis/src/test/java/com/tencent/bk/job/analysis/util/calc/SimpleMomYoyCalculatorTest.java new file mode 100644 index 0000000000..76353d7c49 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/test/java/com/tencent/bk/job/analysis/util/calc/SimpleMomYoyCalculatorTest.java @@ -0,0 +1,92 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.util.calc; + +import com.tencent.bk.job.analysis.api.dto.StatisticsDTO; +import com.tencent.bk.job.analysis.consts.DataTrendEnum; +import com.tencent.bk.job.analysis.model.web.CommonStatisticWithRateVO; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 单元测试-简单同环比计算器 + */ +public class SimpleMomYoyCalculatorTest { + + @Test + public void testCalc() { + // 从0上升 + StatisticsDTO statisticsDTO = new StatisticsDTO("150"); + StatisticsDTO momStatisticsDTO = new StatisticsDTO("0"); + StatisticsDTO yoyStatisticsDTO = new StatisticsDTO("0"); + SimpleMomYoyCalculator simpleMomYoyCalculator = new SimpleMomYoyCalculator( + statisticsDTO, + momStatisticsDTO, + yoyStatisticsDTO + ); + CommonStatisticWithRateVO commonStatisticWithRateVO = simpleMomYoyCalculator.calc(); + assertThat(commonStatisticWithRateVO.getCount()).isEqualTo(150L); + assertThat(commonStatisticWithRateVO.getMomValue()).isEqualTo(150L); + assertThat(commonStatisticWithRateVO.getMomRate()).isEqualTo(1.0f); + assertThat(commonStatisticWithRateVO.getMomTrend()).isEqualTo(DataTrendEnum.UP.getValue()); + assertThat(commonStatisticWithRateVO.getYoyValue()).isEqualTo(150L); + assertThat(commonStatisticWithRateVO.getYoyRate()).isEqualTo(1.0f); + assertThat(commonStatisticWithRateVO.getYoyTrend()).isEqualTo(DataTrendEnum.UP.getValue()); + // 非0上升 + momStatisticsDTO = new StatisticsDTO("100"); + yoyStatisticsDTO = new StatisticsDTO("50"); + simpleMomYoyCalculator = new SimpleMomYoyCalculator( + statisticsDTO, + momStatisticsDTO, + yoyStatisticsDTO + ); + commonStatisticWithRateVO = simpleMomYoyCalculator.calc(); + assertThat(commonStatisticWithRateVO.getCount()).isEqualTo(150L); + assertThat(commonStatisticWithRateVO.getMomValue()).isEqualTo(50L); + assertThat(commonStatisticWithRateVO.getMomRate()).isEqualTo(0.5f); + assertThat(commonStatisticWithRateVO.getMomTrend()).isEqualTo(DataTrendEnum.UP.getValue()); + assertThat(commonStatisticWithRateVO.getYoyValue()).isEqualTo(100L); + assertThat(commonStatisticWithRateVO.getYoyRate()).isEqualTo(2.0f); + assertThat(commonStatisticWithRateVO.getYoyTrend()).isEqualTo(DataTrendEnum.UP.getValue()); + // 下降 + statisticsDTO = new StatisticsDTO("50"); + momStatisticsDTO = new StatisticsDTO("100"); + yoyStatisticsDTO = new StatisticsDTO("200"); + simpleMomYoyCalculator = new SimpleMomYoyCalculator( + statisticsDTO, + momStatisticsDTO, + yoyStatisticsDTO + ); + commonStatisticWithRateVO = simpleMomYoyCalculator.calc(); + assertThat(commonStatisticWithRateVO.getCount()).isEqualTo(50L); + assertThat(commonStatisticWithRateVO.getMomValue()).isEqualTo(-50L); + assertThat(commonStatisticWithRateVO.getMomRate()).isEqualTo(-0.5f); + assertThat(commonStatisticWithRateVO.getMomTrend()).isEqualTo(DataTrendEnum.DOWN.getValue()); + assertThat(commonStatisticWithRateVO.getYoyValue()).isEqualTo(-150L); + assertThat(commonStatisticWithRateVO.getYoyRate()).isEqualTo(-0.75f); + assertThat(commonStatisticWithRateVO.getYoyTrend()).isEqualTo(DataTrendEnum.DOWN.getValue()); + } +} From 724abc8fecad88248fc8f6e33348552431fb1b42 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Thu, 12 Dec 2024 21:43:53 +0800 Subject: [PATCH 2/2] =?UTF-8?q?perf:=20job-analysis=E9=83=A8=E5=88=86?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96=20#3322?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 重构部分代码,增加部分注释。 --- .../model/web/req/AIAnalyzeErrorReq.java | 1 - .../service/ai/AIAnalyzeErrorService.java | 3 + .../service/ai/AIChatHistoryService.java | 3 + .../service/ai/AICheckScriptService.java | 3 + .../bk/job/analysis/service/ai/AIService.java | 3 + .../job/analysis/service/ai/ChatService.java | 3 + .../ai/CheckScriptAIPromptService.java | 3 + .../FileTransferTaskErrorAIPromptService.java | 3 + ...ScriptExecuteTaskErrorAIPromptService.java | 3 + .../ai/context/TaskContextService.java | 3 + .../context/impl/FileTaskContextService.java | 27 ++++++++ .../context/impl/TaskContextServiceImpl.java | 13 ++++ ...ConsumerAndStreamingResponseBodyPair.java} | 11 +-- .../ai/context/model/FileTaskContext.java | 25 ++++++- .../ai/context/model/ScriptTaskContext.java | 5 ++ .../service/ai/context/model/TaskContext.java | 3 + .../ai/impl/AIAnalyzeErrorServiceImpl.java | 3 + .../ai/impl/AIAnswerStreamSynchronizer.java | 14 ++-- .../service/ai/impl/AIBasePromptService.java | 9 +++ .../service/ai/impl/AIBaseService.java | 3 + .../ai/impl/AIChatHistoryServiceImpl.java | 33 +++++++++ .../ai/impl/AICheckScriptServiceImpl.java | 12 ++++ .../service/ai/impl/AIConfigService.java | 8 +++ .../ai/impl/AIMessagePartConsumer.java | 15 +++- .../service/ai/impl/AIServiceImpl.java | 29 +++++++- .../service/ai/impl/AITemplateVarService.java | 56 +++++++++++++++ .../service/ai/impl/ChatServiceImpl.java | 69 +++++++++++++++++-- .../impl/CheckScriptAIPromptServiceImpl.java | 19 +++++ ...eTransferTaskErrorAIPromptServiceImpl.java | 13 ++++ ...ptExecuteTaskErrorAIPromptServiceImpl.java | 15 ++++ .../ai/impl/ScriptTemplateService.java | 19 +++++ .../task/ai/AIChatHistoryCleanTask.java | 8 +++ 32 files changed, 415 insertions(+), 22 deletions(-) rename src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/{AsyncConsumerAndProducerPair.java => AsyncConsumerAndStreamingResponseBodyPair.java} (83%) diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java index 7c19bef30c..9066a8ca78 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java @@ -43,7 +43,6 @@ public class AIAnalyzeErrorReq { private Long taskInstanceId; @ApiModelProperty("步骤执行类型:1-脚本,2-文件") -// @NotNull(message = "{validation.constraints.AIAnalyzeError_stepExecuteTypeEmpty.message}") private Integer stepExecuteType; @ApiModelProperty(value = "步骤ID") diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java index faae11d762..ef291ab51a 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java @@ -27,6 +27,9 @@ import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; +/** + * AI分析报错信息服务 + */ public interface AIAnalyzeErrorService { /** diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java index 756beacf09..f3698995ad 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java @@ -30,6 +30,9 @@ import java.util.List; +/** + * AI聊天记录服务 + */ public interface AIChatHistoryService { /** * 构建AI聊天记录 diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AICheckScriptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AICheckScriptService.java index 584b947862..c4141a5bec 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AICheckScriptService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AICheckScriptService.java @@ -26,6 +26,9 @@ import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; +/** + * AI脚本检查服务 + */ public interface AICheckScriptService { /** diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java index 04a87ae8f5..c8072b9cb1 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java @@ -30,6 +30,9 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; +/** + * AI核心能力服务 + */ public interface AIService { /** diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java index 345b8aad5b..752e93b47b 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java @@ -30,6 +30,9 @@ import java.util.List; +/** + * AI聊天服务 + */ public interface ChatService { /** diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/CheckScriptAIPromptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/CheckScriptAIPromptService.java index cfd16c80e1..c8fe3bbd85 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/CheckScriptAIPromptService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/CheckScriptAIPromptService.java @@ -26,6 +26,9 @@ import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; +/** + * 检查脚本的AI提示符服务 + */ public interface CheckScriptAIPromptService { /** diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/FileTransferTaskErrorAIPromptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/FileTransferTaskErrorAIPromptService.java index 5a0941e13c..e9484f5e20 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/FileTransferTaskErrorAIPromptService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/FileTransferTaskErrorAIPromptService.java @@ -27,6 +27,9 @@ import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; import com.tencent.bk.job.analysis.service.ai.context.model.FileTaskContext; +/** + * 文件分发任务报错信息AI提示符服务 + */ public interface FileTransferTaskErrorAIPromptService { /** diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ScriptExecuteTaskErrorAIPromptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ScriptExecuteTaskErrorAIPromptService.java index 21b2846a25..fdd070ef69 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ScriptExecuteTaskErrorAIPromptService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ScriptExecuteTaskErrorAIPromptService.java @@ -27,6 +27,9 @@ import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; import com.tencent.bk.job.analysis.service.ai.context.model.ScriptTaskContext; +/** + * 脚本执行任务报错信息的AI提示符服务 + */ public interface ScriptExecuteTaskErrorAIPromptService { /** diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/TaskContextService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/TaskContextService.java index f7e5fb5581..454fee27ec 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/TaskContextService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/TaskContextService.java @@ -27,6 +27,9 @@ import com.tencent.bk.job.analysis.service.ai.context.model.TaskContext; import com.tencent.bk.job.analysis.service.ai.context.model.TaskContextQuery; +/** + * 任务上下文服务接口 + */ public interface TaskContextService { /** diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java index e98457ca86..24c4937b68 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java @@ -63,6 +63,13 @@ public FileTaskContextService(ServiceLogResource logResource, this.fileTaskFailLogAnalyzer = fileTaskFailLogAnalyzer; } + /** + * 根据步骤实例与上下文查询条件获取对应的任务上下文 + * + * @param stepInstance 步骤实例 + * @param contextQuery 上下文查询条件 + * @return 任务上下文 + */ public TaskContext getTaskContext(ServiceStepInstanceDTO stepInstance, TaskContextQuery contextQuery) { String jobCreateDate = LogFieldUtil.buildJobCreateDate(stepInstance.getCreateTime()); // 上传日志 @@ -106,6 +113,14 @@ public TaskContext getTaskContext(ServiceStepInstanceDTO stepInstance, TaskConte return buildContextForFileTask(stepInstance, uploadExecuteObjectLogList, downloadExecuteObjectLog); } + /** + * 构建文件任务上下文 + * + * @param stepInstance 步骤实例 + * @param uploadExecuteObjectLogList 上传执行对象日志 + * @param downloadExecuteObjectLog 下载执行对象日志 + * @return 任务上下文 + */ private TaskContext buildContextForFileTask(ServiceStepInstanceDTO stepInstance, List uploadExecuteObjectLogList, ServiceExecuteObjectLogDTO downloadExecuteObjectLog) { @@ -139,6 +154,12 @@ private TaskContext buildContextForFileTask(ServiceStepInstanceDTO stepInstance, return new TaskContext(stepInstance.getExecuteType(), stepInstance.getStatus(), null, fileTaskContext); } + /** + * 判断是否为上传失败日志 + * + * @param fileTaskLog 文件任务日志 + * @return 是否为上传失败日志 + */ private boolean isUploadFailLog(ServiceFileTaskLogDTO fileTaskLog) { Integer mode = fileTaskLog.getMode(); Integer status = fileTaskLog.getStatus(); @@ -146,6 +167,12 @@ private boolean isUploadFailLog(ServiceFileTaskLogDTO fileTaskLog) { && FileDistStatusEnum.FAILED.getValue().equals(status); } + /** + * 判断是否为下载失败日志 + * + * @param fileTaskLog 文件任务日志 + * @return 是否为下载失败日志 + */ private boolean isDownloadFailLog(ServiceFileTaskLogDTO fileTaskLog) { Integer mode = fileTaskLog.getMode(); Integer status = fileTaskLog.getStatus(); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java index 5e13d289d8..40c3be46eb 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java @@ -57,6 +57,13 @@ public TaskContextServiceImpl(ServiceStepInstanceResource serviceStepInstanceRes this.fileTaskContextService = fileTaskContextService; } + /** + * 根据用户名与上下文查询条件获取对应的任务上下文 + * + * @param username 用户名 + * @param contextQuery 上下文查询条件 + * @return 任务上下文 + */ @Override public TaskContext getTaskContext(String username, TaskContextQuery contextQuery) { InternalResponse resp = serviceStepInstanceResource.getStepInstance( @@ -81,6 +88,12 @@ public TaskContext getTaskContext(String username, TaskContextQuery contextQuery throw new ServiceException(resp.getErrorMsg(), ErrorType.valOf(resp.getErrorType()), resp.getCode()); } + /** + * 构建脚本任务上下文 + * + * @param stepInstance 步骤实例 + * @return 脚本任务上下文 + */ private TaskContext buildContextForScriptTask(ServiceStepInstanceDTO stepInstance) { ServiceScriptStepInstanceDTO scriptStepInstance = stepInstance.getScriptStepInstance(); ScriptTaskContext scriptTaskContext = new ScriptTaskContext( diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/AsyncConsumerAndProducerPair.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/AsyncConsumerAndStreamingResponseBodyPair.java similarity index 83% rename from src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/AsyncConsumerAndProducerPair.java rename to src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/AsyncConsumerAndStreamingResponseBodyPair.java index 141aabf8c2..14d1cfda40 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/AsyncConsumerAndProducerPair.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/AsyncConsumerAndStreamingResponseBodyPair.java @@ -30,15 +30,18 @@ import java.util.function.Consumer; +/** + * 互相绑定的异步任务消费者与流式响应体对 + */ @AllArgsConstructor @Data -public class AsyncConsumerAndProducerPair { +public class AsyncConsumerAndStreamingResponseBodyPair { /** - * 消费者 + * 消费者,消费其他数据源产生的数据,将其写入消息队列 */ Consumer consumer; /** - * 生产者 + * 流式响应体,读取消息队列中的数据并将其写入输出流中 */ - StreamingResponseBody producer; + StreamingResponseBody streamingResponseBody; } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java index 82e82364d7..99f2f73a15 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java @@ -40,6 +40,9 @@ import java.util.List; import java.util.Map; +/** + * 文件任务上下文 + */ @AllArgsConstructor @Data public class FileTaskContext { @@ -55,7 +58,9 @@ public class FileTaskContext { * 文件分发任务错误根源分析结果 */ FileTaskErrorSourceResult errorSourceResult; - + /** + * 执行对象表,Key为执行对象ID,Value为执行对象 + */ Map executeObjectMap; public FileTaskContext(String name, @@ -66,6 +71,9 @@ public FileTaskContext(String name, this.errorSourceResult = errorSourceResult; } + /** + * 根据文件步骤实例信息构建执行对象表 + */ private void buildExecuteObjectMapIfNeeded() { if (executeObjectMap != null) { return; @@ -100,10 +108,20 @@ private void buildExecuteObjectMapIfNeeded() { } } + /** + * 获取文件任务错误根源的国际化Key + * + * @return 国际化Key + */ public String getFileTaskErrorSourceI18nKey() { return errorSourceResult.getErrorSource().getI18nKey(); } + /** + * 获取上传文件错误数据 + * + * @return 错误数据 + */ @SuppressWarnings("DuplicatedCode") public String getUploadFileErrorData() { buildExecuteObjectMapIfNeeded(); @@ -137,6 +155,11 @@ public String getUploadFileErrorData() { return JsonUtils.toJson(uploadFileErrorInfoList); } + /** + * 获取下载文件错误数据 + * + * @return 错误数据 + */ @SuppressWarnings("DuplicatedCode") public String getDownloadFileErrorData() { buildExecuteObjectMapIfNeeded(); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/ScriptTaskContext.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/ScriptTaskContext.java index eeacb460e6..5a01b7c04f 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/ScriptTaskContext.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/ScriptTaskContext.java @@ -51,6 +51,11 @@ public class ScriptTaskContext { */ private boolean secureParam; + /** + * 获取脱敏后的脚本参数 + * + * @return 脱敏后的脚本参数 + */ public String getInsensitiveScriptParamsStr() { if (!secureParam) { return scriptParams == null ? "" : scriptParams; diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java index 52c476ef3a..d63c606619 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java @@ -29,6 +29,9 @@ import lombok.AllArgsConstructor; import lombok.Data; +/** + * 任务上下文,用作动态数据填充模板,进而构建向AI提问的Prompt + */ @AllArgsConstructor @Data public class TaskContext { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java index f06346e363..7a92051d48 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java @@ -43,6 +43,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +/** + * 通过AI分析任务报错信息的服务实现类 + */ @Slf4j @Service public class AIAnalyzeErrorServiceImpl extends AIBaseService implements AIAnalyzeErrorService { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerStreamSynchronizer.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerStreamSynchronizer.java index 538f23624a..a68fab45ed 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerStreamSynchronizer.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerStreamSynchronizer.java @@ -25,7 +25,7 @@ package com.tencent.bk.job.analysis.service.ai.impl; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; -import com.tencent.bk.job.analysis.service.ai.context.model.AsyncConsumerAndProducerPair; +import com.tencent.bk.job.analysis.service.ai.context.model.AsyncConsumerAndStreamingResponseBodyPair; import com.tencent.bk.job.analysis.service.ai.context.model.MessagePartEvent; import com.tencent.bk.job.analysis.util.ai.AIAnswerUtil; import com.tencent.bk.job.common.constant.ErrorCode; @@ -43,7 +43,7 @@ /** * AI回答流式响应同步器 - * 使用阻塞队列构建异步消费者与生产者组合,消费者读取到数据后立即提供给生产者进行输出 + * 使用阻塞队列构建互相绑定的异步消费者与流式响应体组合,消费者从流式数据源读取到数据后将其写入到阻塞队列中并提供给流式响应体进行读取输出 */ @Slf4j public class AIAnswerStreamSynchronizer { @@ -61,11 +61,11 @@ public AIAnswerStreamSynchronizer(int capacity) { } /** - * 构建异步消费者与生产者组合 + * 构建异步消费者与流式响应体组合 * - * @return 异步消费者与生产者组合 + * @return 异步消费者与流式响应体组合 */ - public AsyncConsumerAndProducerPair buildAsyncConsumerAndProducerPair() { + public AsyncConsumerAndStreamingResponseBodyPair buildAsyncConsumerAndStreamingResponseBodyPair() { StreamingResponseBody streamingResponseBody = outputStream -> { while (!isFinished.get()) { try { @@ -111,11 +111,11 @@ public AsyncConsumerAndProducerPair buildAsyncConsumerAndProducerPair() { outputStream.close(); }; Consumer partialRespConsumer = new AIMessagePartConsumer(messageQueue); - return new AsyncConsumerAndProducerPair(partialRespConsumer, streamingResponseBody); + return new AsyncConsumerAndStreamingResponseBodyPair(partialRespConsumer, streamingResponseBody); } /** - * 触发结束事件,消费者读取完数据后,触发生产者停止生产并清理 + * 触发结束事件,消费者读取完数据后,通知流式响应体做出相应的停止输出动作并清理 * * @param throwable 异常 */ diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBasePromptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBasePromptService.java index 25cc8fa8c4..608a5b4b1b 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBasePromptService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBasePromptService.java @@ -31,6 +31,9 @@ import com.tencent.bk.job.common.util.JobContextUtil; import lombok.extern.slf4j.Slf4j; +/** + * 基础AI提示符服务 + */ @Slf4j public class AIBasePromptService { @@ -40,6 +43,12 @@ public AIBasePromptService(AIPromptTemplateDAO aiPromptTemplateDAO) { this.aiPromptTemplateDAO = aiPromptTemplateDAO; } + /** + * 根据模板代码获取提示符模板 + * + * @param templateCode 模板代码 + * @return AI提示符模板 + */ protected AIPromptTemplateDTO getPromptTemplate(String templateCode) { String userLang = JobContextUtil.getUserLang(); AIPromptTemplateDTO promptTemplate = aiPromptTemplateDAO.getAIPromptTemplate( diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java index fe3b515053..567deac4ad 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java @@ -35,6 +35,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +/** + * AI基础服务 + */ @Slf4j @Service public class AIBaseService { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java index b0f1883f03..3591170c5e 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java @@ -44,6 +44,9 @@ @Service public class AIChatHistoryServiceImpl implements AIChatHistoryService { + /** + * AI聊天记录数据库操作对象 + */ private final AIChatHistoryDAO aiChatHistoryDAO; @Autowired @@ -51,6 +54,17 @@ public AIChatHistoryServiceImpl(AIChatHistoryDAO aiChatHistoryDAO) { this.aiChatHistoryDAO = aiChatHistoryDAO; } + /** + * 构建AI聊天记录数据传输对象 + * + * @param username 用户名 + * @param appId Job业务ID + * @param startTime 开始时间 + * @param aiPromptDTO AI提示符信息 + * @param status 对话状态 + * @param aiAnswer AI回答 + * @return AI聊天记录数据传输对象 + */ @Override public AIChatHistoryDTO buildAIChatHistoryDTO(String username, Long appId, @@ -87,11 +101,24 @@ public boolean existsChatHistory(String username) { return aiChatHistoryDAO.existsChatHistory(username); } + /** + * 设置AI聊天记录状态为回复中 + * + * @param historyId AI聊天记录ID + * @return 受影响行数 + */ @Override public int setAIAnswerReplying(Long historyId) { return aiChatHistoryDAO.updateChatHistoryStatus(historyId, AIChatStatusEnum.REPLYING.getStatus()); } + /** + * 设置AI聊天记录状态为已完成 + * + * @param historyId AI聊天记录ID + * @param aiAnswer AI回答内容 + * @return 受影响行数 + */ @Override public int finishAIAnswer(Long historyId, AIAnswer aiAnswer) { return aiChatHistoryDAO.updateChatHistoryStatusAndAIAnswer( @@ -104,6 +131,12 @@ public int finishAIAnswer(Long historyId, AIAnswer aiAnswer) { ); } + /** + * 设置AI聊天记录状态为已终止 + * + * @param historyId AI聊天记录ID + * @return 受影响行数 + */ @Override public int terminateAIAnswer(Long historyId) { return aiChatHistoryDAO.updateChatHistoryStatusAndAIAnswer( diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java index 535f4c94e5..f4a27d9e59 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java @@ -33,6 +33,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +/** + * 通过AI大模型检查脚本内容服务实现类 + */ @Slf4j @Service public class AICheckScriptServiceImpl extends AIBaseService implements AICheckScriptService { @@ -46,6 +49,15 @@ public AICheckScriptServiceImpl(CheckScriptAIPromptService checkScriptAIPromptSe this.checkScriptAIPromptService = checkScriptAIPromptService; } + /** + * 通过AI大模型检查脚本内容 + * + * @param username 用户名 + * @param appId Job业务ID + * @param type 脚本类型 + * @param scriptContent 脚本内容 + * @return AI对话记录 + */ @Override public AIChatRecord check(String username, Long appId, Integer type, String scriptContent) { AIPromptDTO aiPromptDTO = checkScriptAIPromptService.getPrompt(type, scriptContent); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIConfigService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIConfigService.java index 8a5e66fa23..3cf5030e72 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIConfigService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIConfigService.java @@ -31,6 +31,9 @@ import java.util.HashMap; import java.util.Map; +/** + * AI配置服务 + */ @Service public class AIConfigService { private final AIProperties aiProperties; @@ -40,6 +43,11 @@ public AIConfigService(AIProperties aiProperties) { this.aiProperties = aiProperties; } + /** + * 获取AI配置 + * + * @return AI配置表,key为配置名称,value为配置取值 + */ public Map getAIConfig() { Map map = new HashMap<>(); Long logMaxLengthBytes = aiProperties.getAnalyzeErrorLog().getLogMaxLengthBytes(); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIMessagePartConsumer.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIMessagePartConsumer.java index e6a542b700..fae8b2bd8b 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIMessagePartConsumer.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIMessagePartConsumer.java @@ -29,6 +29,9 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.function.Consumer; +/** + * AI大模型回复的流式消息块消费者 + */ public class AIMessagePartConsumer implements Consumer { private final LinkedBlockingQueue messageQueue; @@ -36,12 +39,22 @@ public AIMessagePartConsumer(LinkedBlockingQueue messageQueue) this.messageQueue = messageQueue; } - + /** + * 消费一块流式消息,将其放入阻塞队列中 + * + * @param s 一块流式消息 + */ @Override public void accept(String s) { messageQueue.offer(MessagePartEvent.normalEvent(s)); } + /** + * 将当前消费者和另一个消费者串行执行 + * + * @param after 下一个消费者 + * @return 串行执行的两个消费者组合 + */ @Override public Consumer andThen(Consumer after) { return Consumer.super.andThen(after); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java index e4c0b8caea..6431a632bd 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java @@ -39,6 +39,9 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; +/** + * AI服务实现类 + */ @Slf4j @Service public class AIServiceImpl implements AIService { @@ -56,6 +59,15 @@ public AIServiceImpl(LoginTokenService loginTokenService, this.aiMessageI18nService = aiMessageI18nService; } + + /** + * 通过蓝鲸OpenAI接口获取AI回答流 + * + * @param chatHistoryDTOList 历史聊天记录 + * @param userInput 用户输入 + * @param partialRespConsumer AI回答流回调 + * @return AI回答结果Future + */ @Override public CompletableFuture getAIAnswerStream(List chatHistoryDTOList, String userInput, @@ -69,9 +81,15 @@ public CompletableFuture getAIAnswerStream(List chatHi ); } + /** + * 构建传递给蓝鲸OpenAI接口的历史消息记录列表,用作问答上下文 + * + * @param chatHistoryDTOList 历史聊天记录 + * @return 历史消息列表 + */ private List buildMessageHistoryList(List chatHistoryDTOList) { if (CollectionUtils.isEmpty(chatHistoryDTOList)) { - return getSystemMessageList(); + return buildSystemMessageList(); } List messageHistoryList = new ArrayList<>(); for (AIChatHistoryDTO chatHistoryDTO : chatHistoryDTOList) { @@ -84,11 +102,16 @@ private List buildMessageHistoryList(List chatHi aiDevMessage.setContent(chatHistoryDTO.getAiAnswer()); messageHistoryList.add(aiDevMessage); } - messageHistoryList.addAll(getSystemMessageList()); + messageHistoryList.addAll(buildSystemMessageList()); return messageHistoryList; } - private List getSystemMessageList() { + /** + * 构建系统消息列表,用于指定环境语言等基础信息 + * + * @return 系统消息列表 + */ + private List buildSystemMessageList() { List systemMessageList = new ArrayList<>(); AIDevMessage languageSpecifyMessage = new AIDevMessage(); // ROLE_SYSTEM系统角色不生效,使用ROLE_USER消息代替 diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java index b8388c5352..d6942e2825 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java @@ -32,47 +32,103 @@ @Service public class AITemplateVarService { + /** + * 获取模板变量占位符 + * + * @param varName 变量名 + * @return 占位符 + */ public String getTemplateVarPlaceHolder(String varName) { String TEMPLATE_VAR_PREFIX = "{BK_JOB_AI_TEMPLATE_VAR"; return TEMPLATE_VAR_PREFIX + "{" + varName + "}}"; } + /** + * 获取脚本类型占位符 + * + * @return 脚本类型占位符 + */ public String getScriptTypePlaceHolder() { return getTemplateVarPlaceHolder("script_type"); } + /** + * 获取脚本模板占位符 + * + * @return 脚本模板占位符 + */ public String getScriptTemplatePlaceHolder() { return getTemplateVarPlaceHolder("script_template"); } + /** + * 获取脚本参数占位符 + * + * @return 脚本参数占位符 + */ public String getScriptParamsPlaceHolder() { return getTemplateVarPlaceHolder("script_params"); } + /** + * 获取脚本内容占位符 + * + * @return 脚本内容占位符 + */ public String getScriptContentPlaceHolder() { return getTemplateVarPlaceHolder("script_content"); } + /** + * 获取报错信息占位符 + * + * @return 报错信息占位符 + */ public String getErrorContentPlaceHolder() { return getTemplateVarPlaceHolder("error_content"); } + /** + * 获取文件任务错误源占位符 + * + * @return 文件任务错误源占位符 + */ public String getFileTaskErrorSourcePlaceHolder() { return getTemplateVarPlaceHolder("file_task_error_source"); } + /** + * 获取上传文件错误数据占位符 + * + * @return 上传文件错误数据占位符 + */ public String getUploadFileErrorDataPlaceHolder() { return getTemplateVarPlaceHolder("upload_file_error_data"); } + /** + * 获取下载文件错误数据占位符 + * + * @return 下载文件错误数据占位符 + */ public String getDownloadFileErrorDataPlaceHolder() { return getTemplateVarPlaceHolder("download_file_error_data"); } + /** + * 获取BK助手链接占位符 + * + * @return BK助手链接占位符 + */ public String getBkHelperLinkPlaceHolder() { return getTemplateVarPlaceHolder("bk_helper_link"); } + /** + * 获取步骤实例名占位符 + * + * @return 步骤实例名占位符 + */ public String getStepInstanceNamePlaceHolder() { return getTemplateVarPlaceHolder("step_instance_name"); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java index 5607a8b7af..535f88aaf7 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java @@ -34,7 +34,7 @@ import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; import com.tencent.bk.job.analysis.service.ai.AIService; import com.tencent.bk.job.analysis.service.ai.ChatService; -import com.tencent.bk.job.analysis.service.ai.context.model.AsyncConsumerAndProducerPair; +import com.tencent.bk.job.analysis.service.ai.context.model.AsyncConsumerAndStreamingResponseBodyPair; import com.tencent.bk.job.analysis.util.ai.AIAnswerUtil; import com.tencent.bk.job.common.constant.ErrorCode; import com.tencent.bk.job.common.exception.NotFoundException; @@ -53,6 +53,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +/** + * AI对话服务实现类 + */ @Slf4j @Service public class ChatServiceImpl implements ChatService { @@ -73,6 +76,14 @@ public ChatServiceImpl(AIChatHistoryService aiChatHistoryService, this.aiChatOperationEventDispatcher = aiChatOperationEventDispatcher; } + /** + * 与AI对话 + * + * @param username 用户名 + * @param appId Job业务ID + * @param userInput 用户输入 + * @return AI对话记录 + */ @Override public AIChatRecord chatWithAI(String username, Long appId, String userInput) { Long startTime = System.currentTimeMillis(); @@ -91,11 +102,26 @@ public AIChatRecord chatWithAI(String username, Long appId, String userInput) { return aiChatHistoryDTO.toAIChatRecord(); } + /** + * 获取最新对话记录列表 + * + * @param username 用户名 + * @param start 起始位置 + * @param length 长度 + * @return 最新对话记录列表 + */ @Override public List getLatestChatHistoryList(String username, Integer start, Integer length) { return aiChatHistoryService.getLatestChatHistoryList(username, start, length); } + /** + * 生成对话流式数据 + * + * @param username 用户名 + * @param recordId 对话记录ID + * @return 对话流式数据 + */ @Override public StreamingResponseBody generateChatStream(String username, Long recordId) { log.info("Start to generateChatStream, username={}, recordId={}", username, recordId); @@ -119,12 +145,12 @@ public StreamingResponseBody generateChatStream(String username, Long recordId) // 4.将AI回答流数据与接口输出流进行同步对接 int inMemoryMessageMaxNum = 10000; AIAnswerStreamSynchronizer aiAnswerStreamSynchronizer = new AIAnswerStreamSynchronizer(inMemoryMessageMaxNum); - AsyncConsumerAndProducerPair consumerAndProducerPair = - aiAnswerStreamSynchronizer.buildAsyncConsumerAndProducerPair(); + AsyncConsumerAndStreamingResponseBodyPair consumerAndStreamingResponseBodyPair = + aiAnswerStreamSynchronizer.buildAsyncConsumerAndStreamingResponseBodyPair(); CompletableFuture future = aiService.getAIAnswerStream( chatHistoryDTOList, currentChatHistoryDTO.getAiInput(), - consumerAndProducerPair.getConsumer() + consumerAndStreamingResponseBodyPair.getConsumer() ); Locale locale = LocaleContextHolder.getLocale(); log.debug("language={}", locale.getLanguage()); @@ -136,9 +162,15 @@ public StreamingResponseBody generateChatStream(String username, Long recordId) aiAnswerStreamSynchronizer.triggerEndEvent(throwable); }); futureMap.put(recordId, future); - return consumerAndProducerPair.getProducer(); + return consumerAndStreamingResponseBodyPair.getStreamingResponseBody(); } + /** + * 根据已完成的对话记录直接生成回复流,不再请求AI接口 + * + * @param currentChatHistoryDTO 对话记录 + * @return 回复流 + */ private StreamingResponseBody generateRespFromFinishedAIAnswer(AIChatHistoryDTO currentChatHistoryDTO) { return outputStream -> { AIAnswer aiAnswer = AIAnswer.successAnswer(currentChatHistoryDTO.getAiAnswer()); @@ -148,6 +180,12 @@ private StreamingResponseBody generateRespFromFinishedAIAnswer(AIChatHistoryDTO }; } + /** + * 构建对话历史记录 + * + * @param username 用户名 + * @return 对话历史记录 + */ private List buildChatHistory(String username) { List chatHistoryDTOList = aiChatHistoryService.getLatestFinishedChatHistoryList( username, @@ -164,6 +202,13 @@ private List buildChatHistory(String username) { return chatHistoryDTOList; } + /** + * 根据ID获取指定对话记录 + * + * @param username 用户名 + * @param recordId 对话记录ID + * @return 对话记录 + */ private AIChatHistoryDTO getChatHistory(String username, Long recordId) { AIChatHistoryDTO chatHistoryDTO = aiChatHistoryService.getChatHistory(username, recordId); if (chatHistoryDTO == null) { @@ -175,6 +220,13 @@ private AIChatHistoryDTO getChatHistory(String username, Long recordId) { return chatHistoryDTO; } + /** + * 终止当前实例上正在进行的指定ID的对话(如果存在) + * + * @param username 用户名 + * @param recordId 对话记录ID + * @return 是否操作成功 + */ public boolean terminateChat(String username, Long recordId) { CompletableFuture future = futureMap.get(recordId); if (future == null) { @@ -186,6 +238,13 @@ public boolean terminateChat(String username, Long recordId) { return result; } + /** + * 通过MQ广播消息,通知所有实例终止对话 + * + * @param username 用户名 + * @param recordId 对话记录ID + * @return 是否操作成功 + */ public boolean triggerTerminateChat(String username, Long recordId) { AIChatHistoryDTO chatHistoryDTO = getChatHistory(username, recordId); if (!chatHistoryDTO.isInitOrReplying()) { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/CheckScriptAIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/CheckScriptAIPromptServiceImpl.java index 1c442ba090..281193a0f8 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/CheckScriptAIPromptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/CheckScriptAIPromptServiceImpl.java @@ -37,6 +37,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +/** + * 检查脚本的AI提示符服务实现类 + */ @Slf4j @Service public class CheckScriptAIPromptServiceImpl implements CheckScriptAIPromptService { @@ -54,6 +57,13 @@ public CheckScriptAIPromptServiceImpl(ScriptTemplateService scriptTemplateServic this.aiTemplateVarService = aiTemplateVarService; } + /** + * 根据脚本类型与内容获取检查脚本的AI提示符 + * + * @param type 脚本类型 + * @param scriptContent 脚本内容 + * @return AI提示符 + */ @Override public AIPromptDTO getPrompt(Integer type, String scriptContent) { String userLang = JobContextUtil.getUserLang(); @@ -82,6 +92,15 @@ public AIPromptDTO getPrompt(Integer type, String scriptContent) { return new AIPromptDTO(promptTemplate.getId(), promptTemplate.getRawPrompt(), renderedPrompt); } + /** + * 渲染检查脚本的AI提示符 + * + * @param promptTemplateContent 提示符模板内容 + * @param type 脚本类型 + * @param scriptTemplate 脚本模板 + * @param scriptContent 脚本内容 + * @return 渲染后的提示符 + */ private String renderCheckScriptPrompt(String promptTemplateContent, Integer type, String scriptTemplate, diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java index c4ee769106..de26d15893 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java @@ -58,6 +58,12 @@ public FileTransferTaskErrorAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemp this.aiMessageI18nService = aiMessageI18nService; } + /** + * 根据文件任务上下文获取文件分发任务报错分析AI提示符 + * + * @param context 文件分发任务上下文 + * @return AI提示符 + */ @Override public AIPromptDTO getPrompt(FileTaskContext context) { String templateCode = PromptTemplateCodeEnum.ANALYZE_FILE_TRANSFER_TASK_ERROR.name(); @@ -67,6 +73,13 @@ public AIPromptDTO getPrompt(FileTaskContext context) { return new AIPromptDTO(promptTemplate.getId(), renderedRawPrompt, renderedPrompt); } + /** + * 渲染AI提示符 + * + * @param promptTemplateContent AI提示符模板内容 + * @param context 文件任务上下文 + * @return 渲染后的AI提示符 + */ private String renderPrompt(String promptTemplateContent, FileTaskContext context) { return promptTemplateContent .replace(aiTemplateVarService.getStepInstanceNamePlaceHolder(), context.getName()) diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java index b3c6558bdc..3e70c0374e 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java @@ -56,6 +56,13 @@ public ScriptExecuteTaskErrorAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTem this.bkConfig = bkConfig; } + /** + * 根据脚本任务上下文与报错信息获取AI提示符 + * + * @param context 脚本任务上下文 + * @param errorContent 报错内容 + * @return AI提示符 + */ @Override public AIPromptDTO getPrompt(ScriptTaskContext context, String errorContent) { String templateCode = PromptTemplateCodeEnum.ANALYZE_SCRIPT_EXECUTE_TASK_ERROR.name(); @@ -65,6 +72,14 @@ public AIPromptDTO getPrompt(ScriptTaskContext context, String errorContent) { return new AIPromptDTO(promptTemplate.getId(), renderedRawPrompt, renderedPrompt); } + /** + * 渲染AI提示符 + * + * @param promptTemplateContent 提示符模板内容 + * @param context 脚本任务上下文 + * @param errorContent 报错信息 + * @return 渲染后的提示符 + */ private String renderPrompt(String promptTemplateContent, ScriptTaskContext context, String errorContent) { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptTemplateService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptTemplateService.java index 351b862cfc..73e3674af3 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptTemplateService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptTemplateService.java @@ -37,11 +37,18 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +/** + * 脚本模板服务 + */ @Slf4j @Service public class ScriptTemplateService { private final ServiceScriptTemplateResource scriptTemplateResource; + + /** + * 各种语言类型的脚本模板数量有限且很少变动,使用本地缓存提高加载速率,也避免频繁的服务调用 + */ private final LoadingCache scriptTemplateCache = CacheBuilder.newBuilder() .maximumSize(6).expireAfterWrite(1, TimeUnit.DAYS). build(new CacheLoader() { @@ -58,6 +65,12 @@ public ScriptTemplateService(ServiceScriptTemplateResource scriptTemplateResourc this.scriptTemplateResource = scriptTemplateResource; } + /** + * 根据脚本类型获取脚本模板,优先从缓存获取,如果缓存未命中则通过服务调用获取 + * + * @param scriptType 脚本类型 + * @return 脚本模板 + */ public String getScriptTemplate(Integer scriptType) { try { return scriptTemplateCache.get(scriptType); @@ -67,6 +80,12 @@ public String getScriptTemplate(Integer scriptType) { } } + /** + * 通过服务调用获取脚本模板 + * + * @param scriptType 脚本类型 + * @return 脚本模板 + */ public String getScriptTemplateIndeed(Integer scriptType) { InternalResponse resp = scriptTemplateResource.getScriptTemplate(scriptType); if (log.isDebugEnabled()) { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/task/ai/AIChatHistoryCleanTask.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/task/ai/AIChatHistoryCleanTask.java index c6366e698d..37fee37988 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/task/ai/AIChatHistoryCleanTask.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/task/ai/AIChatHistoryCleanTask.java @@ -65,6 +65,9 @@ public AIChatHistoryCleanTask(RedisTemplate redisTemplate, this.aiChatHistoryDAO = aiChatHistoryDAO; } + /** + * 清理任务执行入口,多实例并发仅随机选择一个实例真正执行 + */ public void execute() { log.info("AIChatHistoryCleanTask start"); StopWatch watch = new StopWatch(); @@ -93,6 +96,11 @@ public void execute() { } } + /** + * 根据配置信息执行清理任务 + * + * @param watch 计时器 + */ public void doExecute(StopWatch watch) { watch.start("cleanChatHistoryByMaxKeepDays"); Integer maxKeepDays = aiProperties.getChatHistory().getMaxKeepDays();