From 3c5726e5049e7dc58e90095b0598c646b8971730 Mon Sep 17 00:00:00 2001 From: koosco Date: Thu, 5 Dec 2024 01:46:52 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=EC=B6=94=EC=B2=9C=20=EB=AA=A9?= =?UTF-8?q?=ED=91=9C=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 추천 목표 개수를 3개 반환하도록 수정했습니다 * 목표를 추천할 때 설명하는 이유를 함께 반환하도록 수정했습니다 --- .../com/groom/orbit/ai/app/AiService.java | 4 +- .../orbit/ai/app/openai/OpenAiService.java | 7 +-- .../app/command/GoalRecommendService.java | 31 ++++++++++--- .../app/dto/request/CreateGoalRequestDto.java | 3 -- .../GetRecommendGoalListResponseDto.java | 5 +++ .../response/GetRecommendGoalResponseDto.java | 5 ++- .../RecommendGoalListResponseDto.java | 5 +++ .../response/RecommendGoalResponseDto.java | 5 +++ .../command/GoalRecommendController.java | 5 ++- .../templates/goal-recommend-prompt.txt | 43 +++++++++++++++++-- .../templates/quest-recommend-prompt.txt | 2 +- 11 files changed, 94 insertions(+), 21 deletions(-) delete mode 100644 src/main/java/com/groom/orbit/goal/app/dto/request/CreateGoalRequestDto.java create mode 100644 src/main/java/com/groom/orbit/goal/app/dto/response/GetRecommendGoalListResponseDto.java create mode 100644 src/main/java/com/groom/orbit/goal/app/dto/response/RecommendGoalListResponseDto.java create mode 100644 src/main/java/com/groom/orbit/goal/app/dto/response/RecommendGoalResponseDto.java diff --git a/src/main/java/com/groom/orbit/ai/app/AiService.java b/src/main/java/com/groom/orbit/ai/app/AiService.java index 556ecdc..4ce07c2 100644 --- a/src/main/java/com/groom/orbit/ai/app/AiService.java +++ b/src/main/java/com/groom/orbit/ai/app/AiService.java @@ -1,6 +1,6 @@ package com.groom.orbit.ai.app; -import com.groom.orbit.goal.app.dto.request.CreateGoalRequestDto; +import com.groom.orbit.goal.app.dto.response.RecommendGoalListResponseDto; import com.groom.orbit.goal.app.dto.response.RecommendQuestListResponseDto; import com.groom.orbit.member.app.dto.response.GetFeedbackResponseDto; import com.groom.orbit.resume.app.dto.GetResumeResponseDto; @@ -9,7 +9,7 @@ public interface AiService { GetFeedbackResponseDto getMemberFeedback(String interestJobs, GetResumeResponseDto dto); - CreateGoalRequestDto recommendGoal(Long memberId); + RecommendGoalListResponseDto recommendGoal(Long memberId); RecommendQuestListResponseDto recommendQuest(Long memberId); } diff --git a/src/main/java/com/groom/orbit/ai/app/openai/OpenAiService.java b/src/main/java/com/groom/orbit/ai/app/openai/OpenAiService.java index 862b208..659261d 100644 --- a/src/main/java/com/groom/orbit/ai/app/openai/OpenAiService.java +++ b/src/main/java/com/groom/orbit/ai/app/openai/OpenAiService.java @@ -15,7 +15,7 @@ import com.groom.orbit.ai.app.AiService; import com.groom.orbit.ai.app.VectorService; import com.groom.orbit.ai.dao.vector.Vector; -import com.groom.orbit.goal.app.dto.request.CreateGoalRequestDto; +import com.groom.orbit.goal.app.dto.response.RecommendGoalListResponseDto; import com.groom.orbit.goal.app.dto.response.RecommendQuestListResponseDto; import com.groom.orbit.member.app.dto.response.GetFeedbackResponseDto; import com.groom.orbit.resume.app.dto.GetResumeResponseDto; @@ -66,8 +66,9 @@ public GetFeedbackResponseDto getMemberFeedback(String interestJobs, GetResumeRe } @Override - public CreateGoalRequestDto recommendGoal(Long memberId) { - BeanOutputConverter converter = getConverter(CreateGoalRequestDto.class); + public RecommendGoalListResponseDto recommendGoal(Long memberId) { + BeanOutputConverter converter = + getConverter(RecommendGoalListResponseDto.class); String format = converter.getFormat(); List similarVectors = vectorService.findSimilarVector(memberId); diff --git a/src/main/java/com/groom/orbit/goal/app/command/GoalRecommendService.java b/src/main/java/com/groom/orbit/goal/app/command/GoalRecommendService.java index 85e3d2f..34f9688 100644 --- a/src/main/java/com/groom/orbit/goal/app/command/GoalRecommendService.java +++ b/src/main/java/com/groom/orbit/goal/app/command/GoalRecommendService.java @@ -1,10 +1,15 @@ package com.groom.orbit.goal.app.command; +import java.util.List; +import java.util.stream.IntStream; + import org.springframework.stereotype.Service; import com.groom.orbit.ai.app.AiService; -import com.groom.orbit.goal.app.dto.request.CreateGoalRequestDto; +import com.groom.orbit.goal.app.dto.response.GetRecommendGoalListResponseDto; import com.groom.orbit.goal.app.dto.response.GetRecommendGoalResponseDto; +import com.groom.orbit.goal.app.dto.response.RecommendGoalListResponseDto; +import com.groom.orbit.goal.app.dto.response.RecommendGoalResponseDto; import com.groom.orbit.goal.dao.entity.Goal; import lombok.RequiredArgsConstructor; @@ -16,10 +21,26 @@ public class GoalRecommendService { private final GoalCommandService goalCommandService; private final AiService aiService; - public GetRecommendGoalResponseDto createRecommendGoal(Long memberId) { - CreateGoalRequestDto dto = aiService.recommendGoal(memberId); - Goal goal = goalCommandService.upsert(dto.title(), dto.category()); + public GetRecommendGoalListResponseDto createRecommendGoal(Long memberId) { + RecommendGoalListResponseDto dtos = aiService.recommendGoal(memberId); + dtos.items().forEach(dto -> goalCommandService.upsert(dto.title(), dto.category())); + List newGoals = + dtos.items().stream() + .map(dto -> goalCommandService.upsert(dto.title(), dto.category())) + .toList(); + + return new GetRecommendGoalListResponseDto(convertToDto(newGoals, dtos)); + } - return new GetRecommendGoalResponseDto(goal.getGoalId(), dto.title(), dto.category()); + private static List convertToDto( + List newGoals, RecommendGoalListResponseDto dtos) { + return IntStream.range(0, newGoals.size()) + .mapToObj( + i -> { + RecommendGoalResponseDto dto = dtos.items().get(i); + return new GetRecommendGoalResponseDto( + newGoals.get(i).getGoalId(), dto.title(), dto.category(), dto.descriptions()); + }) + .toList(); } } diff --git a/src/main/java/com/groom/orbit/goal/app/dto/request/CreateGoalRequestDto.java b/src/main/java/com/groom/orbit/goal/app/dto/request/CreateGoalRequestDto.java deleted file mode 100644 index b0988ca..0000000 --- a/src/main/java/com/groom/orbit/goal/app/dto/request/CreateGoalRequestDto.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.groom.orbit.goal.app.dto.request; - -public record CreateGoalRequestDto(String title, String category) {} diff --git a/src/main/java/com/groom/orbit/goal/app/dto/response/GetRecommendGoalListResponseDto.java b/src/main/java/com/groom/orbit/goal/app/dto/response/GetRecommendGoalListResponseDto.java new file mode 100644 index 0000000..afe677b --- /dev/null +++ b/src/main/java/com/groom/orbit/goal/app/dto/response/GetRecommendGoalListResponseDto.java @@ -0,0 +1,5 @@ +package com.groom.orbit.goal.app.dto.response; + +import java.util.List; + +public record GetRecommendGoalListResponseDto(List items) {} diff --git a/src/main/java/com/groom/orbit/goal/app/dto/response/GetRecommendGoalResponseDto.java b/src/main/java/com/groom/orbit/goal/app/dto/response/GetRecommendGoalResponseDto.java index 12377c4..5656a59 100644 --- a/src/main/java/com/groom/orbit/goal/app/dto/response/GetRecommendGoalResponseDto.java +++ b/src/main/java/com/groom/orbit/goal/app/dto/response/GetRecommendGoalResponseDto.java @@ -1,3 +1,6 @@ package com.groom.orbit.goal.app.dto.response; -public record GetRecommendGoalResponseDto(Long goalId, String title, String category) {} +import java.util.List; + +public record GetRecommendGoalResponseDto( + Long goalId, String title, String category, List descriptions) {} diff --git a/src/main/java/com/groom/orbit/goal/app/dto/response/RecommendGoalListResponseDto.java b/src/main/java/com/groom/orbit/goal/app/dto/response/RecommendGoalListResponseDto.java new file mode 100644 index 0000000..cfe8ce0 --- /dev/null +++ b/src/main/java/com/groom/orbit/goal/app/dto/response/RecommendGoalListResponseDto.java @@ -0,0 +1,5 @@ +package com.groom.orbit.goal.app.dto.response; + +import java.util.List; + +public record RecommendGoalListResponseDto(List items) {} diff --git a/src/main/java/com/groom/orbit/goal/app/dto/response/RecommendGoalResponseDto.java b/src/main/java/com/groom/orbit/goal/app/dto/response/RecommendGoalResponseDto.java new file mode 100644 index 0000000..eecdbe0 --- /dev/null +++ b/src/main/java/com/groom/orbit/goal/app/dto/response/RecommendGoalResponseDto.java @@ -0,0 +1,5 @@ +package com.groom.orbit.goal.app.dto.response; + +import java.util.List; + +public record RecommendGoalResponseDto(String title, String category, List descriptions) {} diff --git a/src/main/java/com/groom/orbit/goal/controller/command/GoalRecommendController.java b/src/main/java/com/groom/orbit/goal/controller/command/GoalRecommendController.java index cdcd653..36e9fdf 100644 --- a/src/main/java/com/groom/orbit/goal/controller/command/GoalRecommendController.java +++ b/src/main/java/com/groom/orbit/goal/controller/command/GoalRecommendController.java @@ -7,7 +7,7 @@ import com.groom.orbit.common.annotation.AuthMember; import com.groom.orbit.common.dto.ResponseDto; import com.groom.orbit.goal.app.command.GoalRecommendService; -import com.groom.orbit.goal.app.dto.response.GetRecommendGoalResponseDto; +import com.groom.orbit.goal.app.dto.response.GetRecommendGoalListResponseDto; import lombok.RequiredArgsConstructor; @@ -19,7 +19,8 @@ public class GoalRecommendController { private final GoalRecommendService goalRecommendService; @PostMapping("/recommend") - public ResponseDto creatRecommendGoal(@AuthMember Long memberId) { + public ResponseDto creatRecommendGoal( + @AuthMember Long memberId) { return ResponseDto.ok(goalRecommendService.createRecommendGoal(memberId)); } } diff --git a/src/main/resources/templates/goal-recommend-prompt.txt b/src/main/resources/templates/goal-recommend-prompt.txt index 361af45..ee7d0e8 100644 --- a/src/main/resources/templates/goal-recommend-prompt.txt +++ b/src/main/resources/templates/goal-recommend-prompt.txt @@ -1,13 +1,16 @@ +# 상황 저의 관심 직무는 {job}입니다. {myGoal}는 제가 여태까지 작성했던 목표입니다. {goalList}에 포함된 목표 중 제가 아직 수행하지 않은 목표를 선택하려고 합니다. -{goalList}에 포함된 목표 중 저의 목표와 관련성이 가장 높은 목표를 하나 선택해주세요. +{goalList}에 포함된 목표 중 저의 목표와 관련성이 가장 높은 목표 *세 개*를 선택해주세요. {goalList}에서 선택한 목표가 {job}과 관련이 있는지 판단해주세요. 만약 {job}과 관련이 없다면, {job}과 {myGoal}을 기준으로 새로운 목표를 만들어주세요. 새로운 목표는 {myGoal}과 같으면 안됩니다. -목표의 카테고리(goalCategory)는 다음과 같습니다. +# 조건 +## 조건1 +각 목표의 카테고리(goalCategory)는 다음과 같습니다. - 자격·어학·수상 - 경험·활동·교육 - 경력 @@ -16,8 +19,40 @@ 제시해주시는 목표는 goalCategory 중 하나의 카테고리에 포함되어야 합니다. 제시해주시는 목표와 goalCategory는 연관성이 있어야 합니다. -## 조건 -- 새로운 목표의 종결 어미는 "~하기"여야 합니다. +## 조건2 +새로운 목표의 종결 어미는 "~하기"여야 합니다. + +## 조건3 +- 해당 목표를 왜 추천했는지에 대한 설명(descriptions)을 같이 작성해주세요. +- descriptions에 나오는 내용은 "~요" 어미로 끝나야 해요. +- descriptions의 개수는 2 ~ 3개를 반드시 포함해요. +- descriptions의 내용이 해당 목표를 추천한 이유가 맞는지 다시 한 번 생각하고 답변해주세요. + +## 예시 데이터 +1) +- input + - "myGoal": ["SpringMVC 공부하기", "DDD 개념 이해하기", "클린 아키텍처 구현해보기"] +- output + - "title": "EDA에 대해 공부해보기", "goalCategory": "경험·활동·교육", "descriptions": ["다양한 IT 기업에 지원할 수 있어요", "분산 아키텍처에 대한 이해도를 높일 수 있어요"] + - "title": "SpringCloud 공부해보기", "goalCategory": "경험·활동·교육", "descriptions": ["scale out 가능한 아키텍처에 대해 공부해볼 수 있어요", "분산 아키텍처를 직접 만들어보며 이해도를 높일 수 있어요"] + - "title": "AWS를 이용해 EDA를 적용하기", "goalCategory": "경력", "descriptions": ["로컬 환경에서 벗어나 배포 환경을 분산 아키텍처로 구성해볼 수 있어요", "IT 기업에 지원할 때 우대 받을 수 있어요"] + +2) +- input + - "myGoal": ["미적분학 A+받기", "선형대수학 A+받기", "과탑하기"] +- output + - "title": "해석학 공부해보기", "goalCategory": "경험·활동·교육", "descriptions": ["다양한 수학 분야를 경험해볼 수 있어요", "앞서 배운 내용을 응용해 대학 수학에 대한 이해도를 높일 수 있어요"] + - "title": "수학 경시대회 나가서 수상해보기", "goalCategory": "자격·어학·수상", "descriptions": ["관련 직무에 지원할 때 우대를 받을 수 있어요", "공부한 실력을 검증해볼 수 있어요"] + - "title": "수학과 취업 시장 조사하기", "goalCategory": "경력", "descriptions": ["졸업 후 취업 시장에 대한 식견을 넓힐 수 있어요", "기업들이 원하는 역량을 준비할 수 있어요", "수학의 다양한 응용분야에 대해 알 수 있어요"] + +3) +- input + - "myGoal": ["JLPT 5급 합격하기", "JLPT 4급 합격하기", "매일 일본어 단어 정리하기"] +- output + - "title": "JLPT 3급 합격하기", "goalCategory": "자격·어학·수상", "descriptions": ["일본 사람과 일상적인 대화를 주고 받을 수 있어요", "일상생활에서 사용하는 한자를 읽고 듣는 능력을 기를 수 있어요", "JLPT 2급을 준비할 수 있는 기초 능력을 기를 수 있어요"] + - "title": "교내 일본어 동아리 가입하기", "goalCategory": "경험·활동·교육", "descriptions": ["관심사가 비슷한 사람들과 함께 공부하며 배운 내용을 적용해볼 수 있어요", "일본어 어학 관련 기업에 지원할 때 우대 받을 수 있어요"] + - "title": "일본어 어학연수 알아보기", "goalCategory": "경험·활동·교육", "descriptions": ["일본 현지인들과 함께 생활해보는 경험을 할 수 있어요", "일본 대학에서 일본어를 배우는 경험을 할 수 있어요"] + - "title": "일본 워킹홀리데이 조사하기", "goalCategory": "경험·활동·교육", "descriptions": ["일본 현지인들과 함께 생활해보는 경험을 할 수 있어요", "일본어 어학 관련 기업에 지원할 때 우대 받을 수 있어요"] # 응답 포맷 {format} \ No newline at end of file diff --git a/src/main/resources/templates/quest-recommend-prompt.txt b/src/main/resources/templates/quest-recommend-prompt.txt index a76c2c3..f0a740d 100644 --- a/src/main/resources/templates/quest-recommend-prompt.txt +++ b/src/main/resources/templates/quest-recommend-prompt.txt @@ -3,7 +3,7 @@ - {myQuest}는 제가 여태까지 작성했던 목표입니다. - {questList}에 포함된 퀘스트 중 제가 아직 수행하지 않은 퀘스트를 선택하려고 합니다. -- {questList}에 포함된 퀘스트 중 저의 직무, 목표와 관련성이 가장 높은 퀘스트를 세 개 선택해주세요. +- {questList}에 포함된 퀘스트 중 저의 직무, 목표와 관련성이 가장 높은 퀘스트를 *세 개* 선택해주세요. - {questList}에서 선택한 퀘스트가 {job}, {goal}과 관련이 있는지 판단해주세요. - 만약 선택된 퀘스트가 {job} 또는 {goal} 과 관련이 없다면, {job}과 {goal}, {myQuest}을 기준으로 새로운 퀘스트를 만들어주세요. - 새로운 퀘스트는 {myQuest}에 해당되면 안됩니다. 해당되지 않는다는 것은 비슷한 내용도 포함됩니다. From 347fc1e4eb02f1848223c011e7fe098f35528c32 Mon Sep 17 00:00:00 2001 From: koosco Date: Thu, 5 Dec 2024 01:47:17 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20ai=20feedback=EC=9D=84=20=ED=95=A0?= =?UTF-8?q?=20=EB=95=8C=20=EC=A1=B4=EB=8C=93=EB=A7=90=EB=A1=9C=20=EB=8B=B5?= =?UTF-8?q?=EB=B3=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/templates/ai-feedback-prompt.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/templates/ai-feedback-prompt.txt b/src/main/resources/templates/ai-feedback-prompt.txt index 4ce3b37..6afb340 100644 --- a/src/main/resources/templates/ai-feedback-prompt.txt +++ b/src/main/resources/templates/ai-feedback-prompt.txt @@ -35,6 +35,7 @@ - 가독성을 고려해주세요 - 내용에 따라 문단을 나누어 작성해주세요 - 문장이 길어지면 개행을 해주세요 +- 문장은 존댓말을 써서 답변해주세요 # 응답 포맷 {format}