Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,10 @@ private List<EvaluationQuestionAnalysisResult> buildQuestionAnalyses(
continue;
}
if (status == QuestionAnalysisStatus.FABRICATED
&& !AnalysisSanitizationRules.hasFabricatedDirectConflictReason(item.reason())) {
&& !AnalysisSanitizationRules.hasFabricatedDirectConflictEvidence(
item.sentence(),
item.reason()
)) {
continue;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1297,7 +1297,10 @@ private List<AnalysisCandidateResponse.AnalysisCandidate> sanitizeAnalysisCandid
continue;
}
if (status == QuestionAnalysisStatus.FABRICATED
&& !AnalysisSanitizationRules.hasFabricatedDirectConflictReason(candidate.reasonBasis())) {
&& !AnalysisSanitizationRules.hasFabricatedDirectConflictEvidence(
candidate.sentence(),
candidate.reasonBasis()
)) {
continue;
}
if (!StringUtils.hasText(candidate.candidateId())) {
Expand Down Expand Up @@ -1818,7 +1821,10 @@ private List<AnalysisLlmResponse.QuestionAnalysisItem> buildAcceptedQuestionAnal
continue;
}
if (status == QuestionAnalysisStatus.FABRICATED
&& !AnalysisSanitizationRules.hasFabricatedDirectConflictReason(decision.reason())) {
&& !AnalysisSanitizationRules.hasFabricatedDirectConflictEvidence(
candidate.sentence(),
decision.reason()
)) {
continue;
}
String answer = answerByQuestionId.get(candidate.questionId());
Expand Down Expand Up @@ -2114,7 +2120,10 @@ private CandidateReviewResponse.CandidateDecision validateAcceptedDecision(
return null;
}
if (status == QuestionAnalysisStatus.FABRICATED
&& !AnalysisSanitizationRules.hasFabricatedDirectConflictReason(decision.reason())) {
&& !AnalysisSanitizationRules.hasFabricatedDirectConflictEvidence(
candidate.sentence(),
decision.reason()
)) {
return null;
}
String improvement = AnalysisSanitizationRules.normalizeImprovement(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,10 @@ private List<QuestionAnalysis> buildQuestionAnalyses(
continue;
}
if (status == QuestionAnalysisStatus.FABRICATED
&& !AnalysisSanitizationRules.hasFabricatedDirectConflictReason(item.reason())) {
&& !AnalysisSanitizationRules.hasFabricatedDirectConflictEvidence(
item.sentence(),
item.reason()
)) {
continue;
}
int currentCount = analysisCountByQuestionId.getOrDefault(question.getId(), 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public final class AnalysisSanitizationRules {
"구체적으로 서술합니다", "명확히 설명합니다", "명확히 서술합니다",
"추가해 보", "추가하면 좋", "설명해 보", "구체적으로 작성", "구체적으로 설명",
"강조하겠", "추가하겠", "명확히 작성", "작성할 수 있", "설명할 수 있",
"보완하겠", "드러내겠", "제시하겠"
"보완하겠", "드러내겠", "제시하겠", "추가할 수 있", "수정할 수 있",
"수정하는 방향", "강조하는 방향"
};
private static final String[] CONTRADICTORY_PROVEN_REASON_TERMS = {
"근거가 부족", "성과가 부족", "수치가 부족", "구체성이 부족", "보완이 필요",
Expand Down Expand Up @@ -185,6 +186,17 @@ public static boolean hasFabricatedDirectConflictReason(String reason) {
return containsTeamAndIndividualProjectConflict(normalized);
}

public static boolean hasFabricatedDirectConflictEvidence(String sentence, String reason) {
if (!StringUtils.hasText(reason)) {
return false;
}
if (hasFabricatedDirectConflictReason(reason)) {
return true;
}
return StringUtils.hasText(sentence)
&& hasFabricatedDirectConflictReason(sentence + " " + reason);
}

private static boolean containsTeamAndIndividualProjectConflict(String normalizedReason) {
boolean teamClaim = normalizedReason.contains(normalize("팀 프로젝트"))
|| normalizedReason.contains(normalize("팀으로 진행"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,11 @@ void analyzeAllowsEmptyQuestionAnalyses() {
void analyzeKeepsTwoValidQuestionAnalyses() {
User user = saveUser("analysis-two-valid-items@example.com");
MockApply mockApply = saveMockApply(user);
Question question = saveQuestion(mockApply, "성과 경험", "첫 번째 문장입니다. 두 번째 문장입니다.");
Question question = saveQuestion(
mockApply,
"성과 경험",
"첫 번째 문장입니다. 모든 과정을 혼자 수행한 개인 프로젝트였습니다."
);
when(analysisAiClient.analyze(any(), any())).thenReturn(new AnalysisLlmResponse(
80,
80,
Expand All @@ -659,9 +663,9 @@ void analyzeKeepsTwoValidQuestionAnalyses() {
),
new AnalysisLlmResponse.QuestionAnalysisItem(
question.getId(),
"두 번째 문장입니다.",
"모든 과정을 혼자 수행한 개인 프로젝트였습니다.",
"fabricated",
"팀 프로젝트로 진행했으나 개인 프로젝트라고 주장함",
"실제 프로젝트는 총 5명이 함께 진행한 팀 프로젝트였습니다.",
null
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ void detectsFabricatedReasonByConflictMeaning() {
assertThat(AnalysisSanitizationRules.hasFabricatedDirectConflictReason(
"팀 프로젝트와 개인의 역할이 혼재되어 있습니다."
)).isFalse();
assertThat(AnalysisSanitizationRules.hasFabricatedDirectConflictEvidence(
"모든 과정을 혼자 수행한 개인 프로젝트였습니다.",
"실제 프로젝트는 총 5명이 함께 진행한 팀 프로젝트였습니다."
)).isTrue();
assertThat(AnalysisSanitizationRules.hasFabricatedDirectConflictEvidence(
"팀 프로젝트에 참여했습니다.",
"콘텐츠 제작 역할을 담당했습니다."
)).isFalse();
}

@Test
Expand Down Expand Up @@ -80,6 +88,18 @@ void removesUnsafeImprovement() {
"입사 후 API 운영 안정화에 기여하겠습니다.",
false
)).isEmpty();
assertThat(AnalysisSanitizationRules.normalizeImprovement(
"API 응답 속도를 개선했습니다.",
answer,
"구체적인 성과 수치를 추가하면 좋습니다.",
false
)).isEmpty();
assertThat(AnalysisSanitizationRules.normalizeImprovement(
"API 응답 속도를 개선했습니다.",
answer,
"성과가 드러나도록 수정할 수 있습니다.",
false
)).isEmpty();
}

@Test
Expand Down
Loading