Przeglądaj źródła

feat:线程池优化作文整体评价性能

baiqi 1 rok temu
rodzic
commit
468e2f6d7b

+ 36 - 20
src/main/java/com/njuzr/eaibackend/config/ThreadPoolConfig.java

@@ -18,7 +18,42 @@ import java.util.concurrent.ThreadPoolExecutor;
 public class ThreadPoolConfig {
     @Bean(name = "doubaoTaskExecutor")
     public ThreadPoolTaskExecutor doubaoTaskExecutor() {
-        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor() {
+        ThreadPoolTaskExecutor executor = getExecutor();
+
+        // 设置核心线程数
+        executor.setCorePoolSize(20);
+        // 设置最大线程数
+        executor.setMaxPoolSize(50);
+        // 设置队列容量
+        executor.setQueueCapacity(50);
+        // 设置线程名称前缀
+        executor.setThreadNamePrefix("Doubao-");
+        // 线程池关闭的时候等待所有任务都完成再继续销毁其他的 Bean
+        executor.setWaitForTasksToCompleteOnShutdown(true);
+        // 设置线程池中任务的等待时间,如果超过这个时间还没有销毁就强制销毁
+        executor.setAwaitTerminationSeconds(60);
+
+        // 设置拒绝策略(当队列和线程池都满了时的处理方式)
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+
+        executor.initialize();
+        return executor;
+    }
+
+    @Bean("aiTaskExecutor")
+    public ThreadPoolTaskExecutor aiTaskExecutor() {
+        ThreadPoolTaskExecutor executor = getExecutor();
+        executor.setCorePoolSize(20);
+        executor.setMaxPoolSize(50);
+        executor.setQueueCapacity(100);
+        executor.setThreadNamePrefix("ai-eval-");
+        executor.initialize();
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        return executor;
+    }
+
+    private static ThreadPoolTaskExecutor getExecutor() {
+        return new ThreadPoolTaskExecutor() {
             @Override
             public void execute(Runnable task) {
                 // 记录任务提交日志
@@ -42,24 +77,5 @@ public class ThreadPoolConfig {
                 };
             }
         };
-
-        // 设置核心线程数
-        executor.setCorePoolSize(20);
-        // 设置最大线程数
-        executor.setMaxPoolSize(50);
-        // 设置队列容量
-        executor.setQueueCapacity(50);
-        // 设置线程名称前缀
-        executor.setThreadNamePrefix("Doubao-");
-        // 线程池关闭的时候等待所有任务都完成再继续销毁其他的 Bean
-        executor.setWaitForTasksToCompleteOnShutdown(true);
-        // 设置线程池中任务的等待时间,如果超过这个时间还没有销毁就强制销毁
-        executor.setAwaitTerminationSeconds(60);
-
-        // 设置拒绝策略(当队列和线程池都满了时的处理方式)
-        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
-
-        executor.initialize();
-        return executor;
     }
 }

+ 4 - 2
src/main/java/com/njuzr/eaibackend/controller/EvaluationController.java

@@ -4,6 +4,8 @@ import com.njuzr.eaibackend.service.AIEvaluationService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.concurrent.ExecutionException;
+
 /**
  * @author BaiBai
  * @date 2025-01-17 10:58
@@ -29,8 +31,8 @@ public class EvaluationController {
     public MyResponse rewrite(
             @RequestParam Long assignmentId,
             @RequestParam Long studentId
-    ) {
-        return MyResponse.success(aiEvaluationService.rewrite(assignmentId, studentId));
+    ) throws ExecutionException, InterruptedException {
+        return MyResponse.success(aiEvaluationService.evaluationAsync(assignmentId, studentId).get());
     }
 
     /**

+ 17 - 0
src/main/java/com/njuzr/eaibackend/mapper/EvaluationMapperServiceImpl.java

@@ -0,0 +1,17 @@
+package com.njuzr.eaibackend.mapper;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.njuzr.eaibackend.po.Evaluation;
+import org.springframework.stereotype.Service;
+
+@Service
+public class EvaluationMapperServiceImpl extends ServiceImpl<EvaluationMapper, Evaluation> {
+
+    public Evaluation getByVersion(Long assignmentId, Long userId, Integer version) {
+        return this.lambdaQuery()
+                .eq(Evaluation::getAssignmentId, assignmentId)
+                .eq(Evaluation::getStudentId, userId)
+                .eq(Evaluation::getVersion, version)
+                .one();
+    }
+}

+ 2 - 0
src/main/java/com/njuzr/eaibackend/po/Evaluation.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
+import lombok.experimental.Accessors;
 
 import java.util.Date;
 
@@ -12,6 +13,7 @@ import java.util.Date;
  * @date 2025-01-18 12:23
  */
 @Data
+@Accessors(chain = true)
 @TableName("evaluation")
 public class Evaluation {
     @TableId(type = IdType.AUTO)

+ 2 - 0
src/main/java/com/njuzr/eaibackend/po/OverallEvaluation.java

@@ -4,12 +4,14 @@ import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
+import lombok.experimental.Accessors;
 
 /**
  * @author BaiBai
  * @date 2025-01-17 18:41
  */
 @Data
+@Accessors(chain = true)
 @TableName("overall_evaluation")
 public class OverallEvaluation {
     @TableId(type = IdType.AUTO)

+ 63 - 70
src/main/java/com/njuzr/eaibackend/service/AIEvaluationService.java

@@ -1,23 +1,25 @@
 package com.njuzr.eaibackend.service;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.njuzr.eaibackend.exception.MyException;
 import com.njuzr.eaibackend.mapper.*;
 import com.njuzr.eaibackend.po.*;
-import com.njuzr.eaibackend.utils.FileUtil;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.StopWatch;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.http.HttpStatus;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.Date;
 import java.util.List;
-import java.util.stream.Collectors;
+import java.util.concurrent.CompletableFuture;
 
 /**
  * @author BaiBai
@@ -109,16 +111,25 @@ public class AIEvaluationService {
 
     private final EvaluationMapper evaluationMapper;
 
-    private final FileUtil fileUtil = new FileUtil();
+    private final EvaluationMapperServiceImpl evaluationMapperServiceImpl;
 
     @Autowired
-    public AIEvaluationService(AIRequestService aiRequestService, StudentAssignmentMapper studentAssignmentMapper, AssignmentMapper assignmentMapper, OverallEvaluationMapper overallEvaluationMapper, SentenceEvaluationMapper sentenceEvaluationMapper, EvaluationMapper evaluationMapper) {
+    @Qualifier("aiTaskExecutor")
+    private ThreadPoolTaskExecutor aiTaskExecutor;
+
+    @Autowired
+    public AIEvaluationService(AIRequestService aiRequestService, StudentAssignmentMapper studentAssignmentMapper, AssignmentMapper assignmentMapper, OverallEvaluationMapper overallEvaluationMapper, SentenceEvaluationMapper sentenceEvaluationMapper, EvaluationMapper evaluationMapper, EvaluationMapperServiceImpl evaluationMapperServiceImpl) {
         this.aiRequestService = aiRequestService;
         this.studentAssignmentMapper = studentAssignmentMapper;
         this.assignmentMapper = assignmentMapper;
         this.overallEvaluationMapper = overallEvaluationMapper;
         this.sentenceEvaluationMapper = sentenceEvaluationMapper;
         this.evaluationMapper = evaluationMapper;
+        this.evaluationMapperServiceImpl = evaluationMapperServiceImpl;
+    }
+
+    public CompletableFuture<AIEntry> evaluationAsync(Long assignmentId, Long studentId) {
+        return CompletableFuture.supplyAsync(() -> evaluation(assignmentId, studentId), aiTaskExecutor);
     }
     /**
      * AI作文整体智能批改
@@ -126,91 +137,73 @@ public class AIEvaluationService {
      * @param studentId
      * @return
      */
-    public AIEntry rewrite(Long assignmentId, Long studentId) {
-        // 1 通过assignmentId和studentId找到engagement,获取当前文件链接
-        Engagement engagement = studentAssignmentMapper.findEngagementByStudentIdAndAssignmentId(studentId, assignmentId);
-        if(engagement == null)
-            throw MyException.create(HttpStatus.BAD_REQUEST, "作业参与不存在");
+    public AIEntry evaluation(Long assignmentId, Long studentId) {
+        StopWatch sw = StopWatch.createStarted();
 
-        // 通过assignmentId找到作业,获取作业描述
+        Engagement engagement = studentAssignmentMapper.findEngagementByStudentIdAndAssignmentId(studentId, assignmentId);
+        if(engagement == null) throw MyException.create(HttpStatus.BAD_REQUEST, "作业参与不存在");
         Assignment assignment = assignmentMapper.selectById(assignmentId);
-        if (assignment == null)
-            throw MyException.create(HttpStatus.BAD_REQUEST, "作业不存在");
-
-        // 确定当前批改没有进行过
-        Evaluation evaluationJudge = evaluationMapper.selectOne(new QueryWrapper<Evaluation>()
-                .eq("assignment_id", assignmentId)
-                .eq("student_id", studentId)
-                .eq("version", engagement.getVersion()));
-        if(evaluationJudge != null && evaluationJudge.getOverall() == 1){
-            throw MyException.create(HttpStatus.BAD_REQUEST, "作业已进行过AI分析");
-        }
-
-        String assignmentDescription = assignment.getDescription();
-
-        // 2 找到学生作业内容
-        String content = engagement.getTextContent();
-        log.info("作文内容为:" + content);
-
-        // 3 构造提示词
-        String prompt = String.format(REWRITE_PROMPT_TEMPLATE, assignmentDescription, content);
-        log.info("内置提示词为:"+ prompt);
+        if (assignment == null) throw MyException.create(HttpStatus.BAD_REQUEST, "作业不存在");
+        Evaluation evaluationJudge = evaluationMapperServiceImpl.getByVersion(assignmentId, studentId, engagement.getVersion());
+        if(evaluationJudge != null && evaluationJudge.getOverall() != null) throw MyException.create(HttpStatus.BAD_REQUEST, "作业已进行过AI分析");
 
-        // 4 请求AI
-        List<AIEntry> entry = new ArrayList<>();
-        entry.add(new AIEntry("user", prompt));
-        AIRequestService.AIResponse response = aiRequestService.requestChatGLM4(entry);
+        if(evaluationJudge == null) initEvaluationRecord(assignmentId, studentId, engagement);
 
-        // 5 获取返回数据
+        String prompt = String.format(REWRITE_PROMPT_TEMPLATE, assignment.getDescription(), engagement.getTextContent());
+        AIRequestService.AIResponse response = requestAI(prompt, 0);
         String retContent;
         String role;
-        Long timestamp;
         try {
             retContent = response.getChoices().get(0).getMessage().getContent();
             role = response.getChoices().get(0).getMessage().getRole();
-            timestamp = response.getCreated();
-            log.info("AI返回的数据为:"+retContent);
+            log.info("evaluation requestAI Response:"+retContent);
         } catch (Exception e) {
-            log.error("AI请求返回的数据内容有误,数据为{}", response);
+            log.error("evaluation requestAI error: {}", response);
             throw new MyException(HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()+":"+"请求失败");
         }
 
-
-        // 6 存储数据,已修改为MySQL存储。历史版本使用MangoDB,可前往仓库查看。
-        // 6.1 存储评价
-        OverallEvaluation overallEvaluation = new OverallEvaluation();
-        overallEvaluation.setStudentId(studentId);
-        overallEvaluation.setAssignmentId(assignmentId);
-        overallEvaluation.setContent(content);
-        overallEvaluation.setEvaluation(retContent);
-        // 评价的版本跟随作业版本
-        overallEvaluation.setVersion(engagement.getVersion());
+        OverallEvaluation overallEvaluation = new OverallEvaluation()
+            .setStudentId(studentId)
+            .setAssignmentId(assignmentId)
+            .setContent(engagement.getTextContent())
+            .setEvaluation(retContent)
+            .setVersion(engagement.getVersion());
         overallEvaluationMapper.insert(overallEvaluation);
 
-        // 6.2 存储版本信息
-        Evaluation evaluationJudge2 = evaluationMapper.selectOne(new QueryWrapper<Evaluation>()
-                .eq("assignment_id", assignmentId)
+        evaluationMapperServiceImpl.update(new UpdateWrapper<Evaluation>()
+                .set("overall", 1)
                 .eq("student_id", studentId)
+                .eq("assignment_id", assignmentId)
                 .eq("version", engagement.getVersion()));
-        if(evaluationJudge2 == null){
-            Evaluation evaluation = new Evaluation();
-            evaluation.setTime(new Date());
-            evaluation.setAssignmentId(assignmentId);
-            evaluation.setStudentId(studentId);
-            evaluation.setVersion(engagement.getVersion());
-            evaluation.setSentence(0);
-            evaluation.setOverall(1);
-            evaluationMapper.insert(evaluation);
-        } else {
-            evaluationJudge.setOverall(1);
-            evaluationMapper.updateById(evaluationJudge);
-        }
-
 
+        log.info("evaluation cost: {}", sw.formatTime());
         return new AIEntry(role, retContent);
 
     }
 
+    private void initEvaluationRecord(Long assignmentId, Long studentId, Engagement engagement) {
+        Evaluation evaluation = new Evaluation()
+            .setTime(new Date())
+            .setAssignmentId(assignmentId)
+            .setStudentId(studentId)
+            .setVersion(engagement.getVersion())
+            .setSentence(0)
+            .setOverall(0);
+        evaluationMapperServiceImpl.save(evaluation);
+
+    }
+
+    private AIRequestService.AIResponse requestAI(String prompt, int retryTime) {
+        List<AIEntry> entry = new ArrayList<>();
+        entry.add(new AIEntry("user", prompt));
+        AIRequestService.AIResponse response = aiRequestService.requestChatGLM4(entry);
+        if(response == null && retryTime < 3) {
+            log.info("requestAI failed, retryTime: {}", retryTime + 1);
+            return requestAI(prompt, retryTime + 1);
+        }
+        return response;
+    }
+
     /**
      * AI作业逐句智能批改
      * @param assignmentId