|
@@ -11,10 +11,13 @@ import org.apache.commons.lang3.StringUtils;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.util.CollectionUtils;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
import javax.validation.constraints.NotNull;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
import java.util.List;
|
|
|
import java.util.regex.Pattern;
|
|
|
import java.util.stream.Collectors;
|
|
@@ -77,6 +80,22 @@ public class AiService {
|
|
|
* @return 判分结果;null表示无法获取判分结果
|
|
|
*/
|
|
|
public AutoScoreResult autoScore(@NotNull @Validated AutoScoreRequest request, @NotNull SignatureInfo signature) {
|
|
|
+ if (StringUtils.isBlank(request.getSubjectName())) {
|
|
|
+ throw new IllegalArgumentException("科目名称不能为空");
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(request.getQuestionBody())) {
|
|
|
+ throw new IllegalArgumentException("试题内容不能为空");
|
|
|
+ }
|
|
|
+ if (request.getScoreModel() == null) {
|
|
|
+ throw new IllegalArgumentException("评分模式不能为空");
|
|
|
+ }
|
|
|
+ if (CollectionUtils.isEmpty(request.getStandardAnswer())) {
|
|
|
+ throw new IllegalArgumentException("标答不能为空");
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(request.getStudentAnswer())) {
|
|
|
+ throw new IllegalArgumentException("考生回答不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
ChatResult result = llmApiClient.chatTemplate(signature, LlmAppType.AUTO_SCORE, request);
|
|
|
String text = result.getChoices().stream().filter(choice -> choice.getMessage().getRole() == ChatRole.assistant)
|
|
|
.map(choice -> choice.getMessage().getContent()).findFirst().orElse("");
|
|
@@ -85,7 +104,7 @@ public class AiService {
|
|
|
String[] values = StringUtils.split(text.replaceAll(",", ","), ",");
|
|
|
int stepCount = request.getStandardAnswer().size();
|
|
|
if (stepCount != values.length) {
|
|
|
- log.warn("评分结果无效:{},要求给出{}个关键分值,实际{}个", text, stepCount, values.length);
|
|
|
+ log.warn("评分结果无效,要求给出{}个关键分值,实际响应:{}", stepCount, text);
|
|
|
return null;
|
|
|
}
|
|
|
|
|
@@ -93,43 +112,66 @@ public class AiService {
|
|
|
double[] scores = new double[stepCount];
|
|
|
for (int i = 0; i < stepCount; i++) {
|
|
|
double stepScore = Double.parseDouble(values[i].trim());
|
|
|
- StandardAnswer step = request.getStandardAnswer().get(i);
|
|
|
|
|
|
+ StandardAnswer step = request.getStandardAnswer().get(i);
|
|
|
if (AutoScoreModel.LEVEL == request.getScoreModel()) {
|
|
|
// 按档次给分模式:关键步骤得分应介于当前档次分值区间
|
|
|
if (stepScore < step.getLowScore()) {
|
|
|
- log.warn("关键步骤得分无效:{},当前档次分值区间{}-{}分", stepScore, step.getLowScore(), step.getHighScore());
|
|
|
- scores[i] = 0;
|
|
|
+ log.warn("评分结果-第{}步得分:{},低于档次分值区间{}-{}分的最低分", i + 1, stepScore, step.getLowScore(), step.getHighScore());
|
|
|
+ stepScore = 0;
|
|
|
} else if (stepScore > step.getHighScore()) {
|
|
|
- scores[i] = Math.min(stepScore, step.getHighScore());
|
|
|
+ log.warn("评分结果-第{}步得分:{},高于档次分值区间{}-{}分的最高分", i + 1, stepScore, step.getLowScore(), step.getHighScore());
|
|
|
} else {
|
|
|
- scores[i] = stepScore;
|
|
|
+ // 修正分值-符合最小间隔分规则
|
|
|
+ stepScore = this.fixScore(stepScore, request.getIntervalScore());
|
|
|
}
|
|
|
|
|
|
- // 处理分值符合最小间隔分 todo
|
|
|
+ // 步骤分不能超过当前档次区间最高分和试题总分
|
|
|
+ scores[i] = Math.min(Math.min(stepScore, step.getHighScore()), request.getTotalScore());
|
|
|
totalScore = Math.max(totalScore, scores[i]);
|
|
|
} else {
|
|
|
// 按得分点给分模式:关键步骤得分应小于等于当前得分点的分值
|
|
|
if (stepScore > step.getScore()) {
|
|
|
- log.warn("关键步骤得分无效:{},当前得分点分值{}分", stepScore, step.getScore());
|
|
|
- scores[i] = 0;
|
|
|
- continue;
|
|
|
+ log.warn("评分结果-第{}步得分:{},大于得分点分值{}分的最大分", i + 1, stepScore, step.getScore());
|
|
|
+ } else {
|
|
|
+ // 修正分值-符合最小间隔分规则
|
|
|
+ stepScore = this.fixScore(stepScore, request.getIntervalScore());
|
|
|
}
|
|
|
|
|
|
- // 处理分值符合最小间隔分 todo
|
|
|
- scores[i] = stepScore;
|
|
|
- totalScore += stepScore;
|
|
|
+ // 步骤分不能超过当前得分点最高分和试题总分
|
|
|
+ scores[i] = Math.min(Math.min(stepScore, step.getScore()), request.getTotalScore());
|
|
|
+ totalScore += scores[i];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
AutoScoreResult scoreResult = new AutoScoreResult();
|
|
|
scoreResult.setStepScore(scores);
|
|
|
- scoreResult.setTotalScore(totalScore);
|
|
|
+ scoreResult.setTotalScore(Math.min(totalScore, request.getTotalScore()));
|
|
|
return scoreResult;
|
|
|
} catch (Exception e) {
|
|
|
- log.warn("评分结果无效:{},错误:{}", text, e.getMessage());
|
|
|
+ log.warn("评分结果无效,实际响应:{},错误:{}", text, e.getMessage());
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 将分数近似修正为指定“最小间隔分”的整数倍数
|
|
|
+ *
|
|
|
+ * @param score 分数
|
|
|
+ * @param intervalScore 最小间隔分
|
|
|
+ * @return 修正后的分数
|
|
|
+ */
|
|
|
+ private double fixScore(double score, double intervalScore) {
|
|
|
+ if (intervalScore <= 0) {
|
|
|
+ throw new IllegalArgumentException("最小间隔分必须大于0");
|
|
|
+ }
|
|
|
+
|
|
|
+ BigDecimal interval = BigDecimal.valueOf(intervalScore);
|
|
|
+
|
|
|
+ // 计算结果 = 近似取整(score / intervalScore) * intervalScore
|
|
|
+ return BigDecimal.valueOf(score)
|
|
|
+ .divide(interval, 0, RoundingMode.HALF_UP)
|
|
|
+ .multiply(interval).doubleValue();
|
|
|
+ }
|
|
|
+
|
|
|
}
|