diff --git a/src/main/java/com/guo/learningprogresstracker/service/impl/AiServiceClient.java b/src/main/java/com/guo/learningprogresstracker/service/impl/AiServiceClient.java index 7955c70..729694f 100644 --- a/src/main/java/com/guo/learningprogresstracker/service/impl/AiServiceClient.java +++ b/src/main/java/com/guo/learningprogresstracker/service/impl/AiServiceClient.java @@ -2,6 +2,7 @@ package com.guo.learningprogresstracker.service.impl; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.guo.learningprogresstracker.utils.CompareResult; import jakarta.annotation.PostConstruct; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -129,4 +130,40 @@ public class AiServiceClient { return Optional.empty(); } } + + /** + * 调用 lpt-ai 进行语义化回忆对比。 + * + * @return 带 MATCHED/MISSED 标注和评价文本的对比结果;服务未配置或调用失败时返回 empty + */ + public Optional compareRecall(String taskName, String standardOutline, String recallOutline) { + if (!isConfigured()) { + return Optional.empty(); + } + try { + Map body = Map.of( + "taskName", taskName, + "standardOutline", standardOutline, + "recallOutline", recallOutline + ); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url + "/ai/compare-recall")) + .timeout(Duration.ofSeconds(timeoutSeconds)) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(body))) + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + if (response.statusCode() != 200) { + log.warn("lpt-ai 回忆对比失败: status={}, body={}", response.statusCode(), + response.body() != null ? response.body().substring(0, Math.min(200, response.body().length())) : ""); + return Optional.empty(); + } + return Optional.of(objectMapper.readValue(response.body(), CompareResult.class)); + } catch (Exception e) { + log.warn("lpt-ai 回忆对比调用异常,将降级为内置算法: {}", e.getMessage()); + return Optional.empty(); + } + } } diff --git a/src/main/java/com/guo/learningprogresstracker/service/impl/StandardMindMapServiceImpl.java b/src/main/java/com/guo/learningprogresstracker/service/impl/StandardMindMapServiceImpl.java index 434fe02..01669c8 100644 --- a/src/main/java/com/guo/learningprogresstracker/service/impl/StandardMindMapServiceImpl.java +++ b/src/main/java/com/guo/learningprogresstracker/service/impl/StandardMindMapServiceImpl.java @@ -39,6 +39,7 @@ public class StandardMindMapServiceImpl implements StandardMindMapService { private final List aiClients; private final ObjectMapper objectMapper; + private final AiServiceClient aiServiceClient; private static final String GENERATOR_BUILTIN = "BUILTIN"; private static final String GENERATOR_USER = "USER"; @@ -105,8 +106,28 @@ public class StandardMindMapServiceImpl implements StandardMindMapService { MindMapNode standardRoot = MindMapTreeTool.fromJson(standard.getContent(), objectMapper); MindMapNode recallRoot = MindMapTreeTool.parseOutline(recallOutline); - // 3. 执行对比 - CompareResult result = compareTrees(standardRoot, recallRoot); + // 3. 执行对比(优先 AI 语义对比,失败时降级为内置算法) + CompareResult result; + if (aiServiceClient.isConfigured()) { + String taskName = tasksMapper.selectOne( + Wrappers.lambdaQuery().eq(TaskEntity::getTaskNum, taskNum).last("LIMIT 1")) + .getTaskName(); + var optResult = aiServiceClient.compareRecall( + taskName, + MindMapTreeTool.toFullOutline(standardRoot), + recallOutline + ); + if (optResult.isPresent()) { + result = optResult.get(); + log.info("AI 回忆对比: taskNum={}, recallRatio={}, evaluation={}", + taskNum, result.getRecallRatio(), + result.getEvaluation() != null ? result.getEvaluation().substring(0, Math.min(50, result.getEvaluation().length())) : ""); + } else { + result = compareTrees(standardRoot, recallRoot); + } + } else { + result = compareTrees(standardRoot, recallRoot); + } // 4. 序列化对比结果 String resultJson; diff --git a/src/main/java/com/guo/learningprogresstracker/utils/CompareResult.java b/src/main/java/com/guo/learningprogresstracker/utils/CompareResult.java index 4a041bb..2e023b2 100644 --- a/src/main/java/com/guo/learningprogresstracker/utils/CompareResult.java +++ b/src/main/java/com/guo/learningprogresstracker/utils/CompareResult.java @@ -32,6 +32,9 @@ public class CompareResult { /** 额外节点数 */ private int extraCount; + /** AI 评价文本(仅 AI 对比时有值,内置对比为 null) */ + private String evaluation; + @Data @NoArgsConstructor @AllArgsConstructor diff --git a/src/test/java/com/guo/learningprogresstracker/service/impl/StandardMindMapServiceImplTest.java b/src/test/java/com/guo/learningprogresstracker/service/impl/StandardMindMapServiceImplTest.java index 291c840..58cbcaf 100644 --- a/src/test/java/com/guo/learningprogresstracker/service/impl/StandardMindMapServiceImplTest.java +++ b/src/test/java/com/guo/learningprogresstracker/service/impl/StandardMindMapServiceImplTest.java @@ -45,6 +45,8 @@ class StandardMindMapServiceImplTest { private List aiClients; @Mock private MindMapAiClient mockAiClient; + @Mock + private AiServiceClient aiServiceClient; private final ObjectMapper objectMapper = new ObjectMapper(); @@ -189,7 +191,7 @@ class StandardMindMapServiceImplTest { standardMindMapMapper, recallRecordMapper, tasksMapper, studyReportsMapper, studyReportFragmentsMapper, taskApplicationMapper, studySessionsMapper, - List.of(mockAiClient), objectMapper); + List.of(mockAiClient), objectMapper, aiServiceClient); return s; } }