diff --git a/src/main/java/com/susanghan_guys/server/global/client/openai/prompt/DcaBriefEvaluationPrompt.java b/src/main/java/com/susanghan_guys/server/global/client/openai/prompt/DcaBriefEvaluationPrompt.java index c23d667..0b7770a 100644 --- a/src/main/java/com/susanghan_guys/server/global/client/openai/prompt/DcaBriefEvaluationPrompt.java +++ b/src/main/java/com/susanghan_guys/server/global/client/openai/prompt/DcaBriefEvaluationPrompt.java @@ -7,8 +7,8 @@ public class DcaBriefEvaluationPrompt { public static OpenAiPrompt buildDcaBriefEvaluationPrompt(DcaOpenAiRequest.BrandBriefPayload brief) { String system = """ You are a professional assistant that analyzes a competition entry into three fields: - interpretation, consistency, weakness. Respond ONLY in Korean and return JSON with - keys: interpretation, consistency, weakness. + interpretation, consistency. Respond ONLY in Korean and return JSON with + keys: interpretation, consistency. Global rules - Always output all three fields. @@ -34,24 +34,6 @@ public static OpenAiPrompt buildDcaBriefEvaluationPrompt(DcaOpenAiRequest.BrandB - Evaluate alignment between the interpreted brief and execution (planning intent ↔ media/message/engagement design). - Include at least one concrete example referencing story flow, media characteristics, or participation design. - Focus on how consistently the brief’s core direction is carried through—not on micro design polish. - - 3) weakness - - Exactly 1–2 sentences.\s - - Write in a short, critical, and declarative style (e.g., "~이 부족하다", "~이 약하다", "~이 드러나지 않는다"). - - Always ground the weakness in a specific element of the submission (e.g., medium used, participation path, message framing). - - Focus only on major misalignments with the brief’s core requirements or weak linkage to the brand’s essential value. - - Do NOT include trivial design issues, technical usability, or time/place limitations. - - Avoid phrasing like "~할 수 있다", "~기 때문이다"; always state issues directly and decisively. - - If the work is overall strong, still provide one subtle risk framed in the same short, critical style. - - # Good Weakness (use this style) - - "캐릭터 아이프렌즈로 귀여움과 친근함은 살렸으나, 브랜드와 직접적 연계가 약해 장기적 팬덤 구축까지는 한계가 있음." - - "버스 창문이 닫힐 때 전달되는 ‘채워짐’의 순간은 시각적으로 임팩트 있지만, 창문이 열리면 메시지가 단절되어 브랜드 기억 지속성이 약할 수 있다." - - # Bad Weakness (do NOT write like this) - - "더 창의적이어야 한다." # ambiguous - - "앱 사용성이 떨어질 수 있다." # minor/Design Points - - "문제가 없다고 본다." # uncritical """; String briefBlock = (brief == null) ? "" : """ @@ -71,7 +53,6 @@ public static OpenAiPrompt buildDcaBriefEvaluationPrompt(DcaOpenAiRequest.BrandB Analyze the following submission and output a JSON object with: - "interpretation": exactly 2 sentences ([brief core] → [how reflected in campaign]). - "consistency": 3–4 sentences with concrete examples of alignment. - - "weakness": **at least 1 sentence** (prefer 2), each naming a concrete element and its impact on brief/brand alignment; avoid design-only or time/place-specific remarks. """.formatted(briefBlock); return new OpenAiPrompt(system, user); diff --git a/src/main/java/com/susanghan_guys/server/global/client/openai/prompt/DcaWorkEvaluationPrompt.java b/src/main/java/com/susanghan_guys/server/global/client/openai/prompt/DcaWorkEvaluationPrompt.java index 6faea9e..ab35cc5 100644 --- a/src/main/java/com/susanghan_guys/server/global/client/openai/prompt/DcaWorkEvaluationPrompt.java +++ b/src/main/java/com/susanghan_guys/server/global/client/openai/prompt/DcaWorkEvaluationPrompt.java @@ -106,6 +106,10 @@ public static OpenAiPrompt buildDcaDetailEvaluationPrompt( If you assign ≤5, you must justify with one clear weakness/risk factor (do not list more than one). - Below 5: Not allowed. + Rationale rule: + - If score == 6 → add exactly one short weakness. + - If score ≥ 7 → no weaknesses, no “그러나/하지만/다만” style contrast. + Conservativeness: - Do NOT inflate scores without clear evidence. - Be conservative: most scores should be 8 or below. diff --git a/src/main/java/com/susanghan_guys/server/personalwork/application/StrengthWeaknessService.java b/src/main/java/com/susanghan_guys/server/personalwork/application/StrengthWeaknessService.java index 73bb18c..e0f597c 100644 --- a/src/main/java/com/susanghan_guys/server/personalwork/application/StrengthWeaknessService.java +++ b/src/main/java/com/susanghan_guys/server/personalwork/application/StrengthWeaknessService.java @@ -27,9 +27,10 @@ public List getStrengths(Long workId) personalWorkValidator.validatePersonalWorkAccessible(workId, currentUserProvider.getCurrentUser()); personalWorkValidator.validateEvaluationExists(workId); - List strengths = detailEvalRepository.findTopStrengths(workId, PageRequest.of(0, 3)); + List strengths = detailEvalRepository.findTopStrengths(workId, PageRequest.of(0, 6)); + List selected = adjustIfSameEvalTop3(strengths); - return strengths.stream().map(StrengthWeaknessMapper::toResponse).toList(); + return selected.stream().map(StrengthWeaknessMapper::toResponse).toList(); } @@ -38,23 +39,70 @@ public List getWeaknesses(Long workId personalWorkValidator.validatePersonalWorkAccessible(workId, currentUserProvider.getCurrentUser()); personalWorkValidator.validateEvaluationExists(workId); - List weaknesses = detailEvalRepository.findBottomWeaknesses(workId, PageRequest.of(0, 3)); - List bounded = applyBoundaryRule(weaknesses); + List weaknesses = detailEvalRepository.findBottomWeaknesses(workId, PageRequest.of(0, 6)); + List selected = applyBoundaryThenReplaceIfSameEval(weaknesses); + + return selected.stream().map(StrengthWeaknessMapper::toResponse).toList(); + } - return bounded.stream().map(StrengthWeaknessMapper::toResponse).toList(); -} - private List applyBoundaryRule(List sortedAscTop3) { - int n = sortedAscTop3.size(); - if (n <= 2) return sortedAscTop3; + private List adjustIfSameEvalTop3(List top6) { - int s2 = sortedAscTop3.get(1).getScore(); - int s3 = sortedAscTop3.get(2).getScore(); + List first3 = top6.subList(0, 3); + Long e1 = first3.get(0).getEvaluation().getId(); + Long e2 = first3.get(1).getEvaluation().getId(); + Long e3 = first3.get(2).getEvaluation().getId(); - if (s2 != s3) { - return sortedAscTop3.subList(0, 2); + // 셋 다 같은 evaluation이면 교체 + if (e1.equals(e2) && e2.equals(e3)) { + for (int i = 3; i < Math.min(6, top6.size()); i++) { + DetailEval cand = top6.get(i); + if (!cand.getEvaluation().getId().equals(e1)) { + List adjusted = new java.util.ArrayList<>(first3); + adjusted.set(2, cand); + return adjusted; + } + } + return first3; } + return first3; + } + + private List applyBoundaryThenReplaceIfSameEval(List bottom6) { + + // 경계 규칙 적용 + List top3Slice = bottom6.subList(0, 3); + List base = applyBoundaryRule(top3Slice); + + if (base.size() < 3) return base; + + Long e1 = base.get(0).getEvaluation().getId(); + Long e2 = base.get(1).getEvaluation().getId(); + Long e3 = base.get(2).getEvaluation().getId(); + + boolean allSameEval = e1.equals(e2) && e2.equals(e3); + if (!allSameEval) return base; + + int s3 = base.get(2).getScore(); + + for (int i = 3; i < Math.min(6, bottom6.size()); i++) { + DetailEval cand = bottom6.get(i); + if (cand.getScore() == s3 && !cand.getEvaluation().getId().equals(e1)) { + List adjusted = new java.util.ArrayList<>(base); + adjusted.set(2, cand); + return adjusted; + } + } + + return base.subList(0, 2); + } + + private List applyBoundaryRule(List top3) { + int s2 = top3.get(1).getScore(); + int s3 = top3.get(2).getScore(); + + if (s2 != s3) return top3.subList(0, 2); - return sortedAscTop3; + return top3; } } \ No newline at end of file diff --git a/src/main/java/com/susanghan_guys/server/personalwork/domain/BriefAnalysis.java b/src/main/java/com/susanghan_guys/server/personalwork/domain/BriefAnalysis.java index 0b3f259..2076391 100644 --- a/src/main/java/com/susanghan_guys/server/personalwork/domain/BriefAnalysis.java +++ b/src/main/java/com/susanghan_guys/server/personalwork/domain/BriefAnalysis.java @@ -24,9 +24,6 @@ public class BriefAnalysis extends BaseEntity { @Column(name = "consistency", nullable = false, length = 300) private String consistency; - @Column(name = "weakness", nullable = false, length = 300) - private String weakness; - @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "work_id", nullable = false, unique = true) private Work work; @@ -35,12 +32,10 @@ public class BriefAnalysis extends BaseEntity { public BriefAnalysis( String interpretation, String consistency, - String weakness, Work work ) { this.interpretation = interpretation; this.consistency = consistency; - this.weakness = weakness; this.work = work; } } diff --git a/src/main/java/com/susanghan_guys/server/personalwork/dto/response/DcaBriefEvaluationResponse.java b/src/main/java/com/susanghan_guys/server/personalwork/dto/response/DcaBriefEvaluationResponse.java index ce4fd51..205bf70 100644 --- a/src/main/java/com/susanghan_guys/server/personalwork/dto/response/DcaBriefEvaluationResponse.java +++ b/src/main/java/com/susanghan_guys/server/personalwork/dto/response/DcaBriefEvaluationResponse.java @@ -17,12 +17,6 @@ public record DcaBriefEvaluationResponse( description = "반영 일관성", example = "캠페인은 타이머와 빼빼로를 결합하여 '잠시 톡! 끊어가도 괜찮아'라는 메시지를 통해 영타겟에게 휴식의 중요성을 전달하고 있다. 이는 브랜드의 본질적 가치인 '나눔/연결의 가치'를 유지하면서도 새로운 경험을 제공하려는 시도이다. 또한, 패키지 디자인과 메시지 전달 방식이 젊은 층의 일상과 잘 맞아떨어져 자발적 참여를 유도하고 있다. 이러한 요소들은 브랜드의 감성적 연결을 강화하는 데 일조하고 있다." ) - String consistency, - - @Schema( - description = "보완점", - example = "타이머와의 결합이 신선하지만, 빼빼로 자체의 매력보다는 부가적인 요소에 의존하는 경향이 있어 브랜드의 본질적 가치와의 직접적 연계가 약할 수 있다." - ) - String weakness + String consistency ) { } diff --git a/src/main/java/com/susanghan_guys/server/personalwork/infrastructure/mapper/BriefAnalysisMapper.java b/src/main/java/com/susanghan_guys/server/personalwork/infrastructure/mapper/BriefAnalysisMapper.java index b624df5..c6c68ee 100644 --- a/src/main/java/com/susanghan_guys/server/personalwork/infrastructure/mapper/BriefAnalysisMapper.java +++ b/src/main/java/com/susanghan_guys/server/personalwork/infrastructure/mapper/BriefAnalysisMapper.java @@ -12,7 +12,6 @@ public static BriefAnalysis toEntity(Work work, DcaBriefEvaluationResponse resp) return BriefAnalysis.builder() .interpretation(resp.interpretation()) .consistency(safeTrim(resp.consistency(), MAX_LEN)) - .weakness(safeTrim(resp.weakness(), MAX_LEN)) .work(work) .build(); } @@ -21,7 +20,6 @@ public static DcaBriefEvaluationResponse toResponse(BriefAnalysis e) { return DcaBriefEvaluationResponse.builder() .interpretation(e.getInterpretation()) .consistency(safeTrim(e.getConsistency(), MAX_LEN)) - .weakness(safeTrim(e.getWeakness(), MAX_LEN)) .build(); }