From 518f3bc5302b196c744756befb35de4eab39fb77 Mon Sep 17 00:00:00 2001 From: banseok1216 Date: Tue, 3 Jun 2025 17:10:31 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fixed:=20=ED=94=84=EB=A1=AC=ED=94=84?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/org/chewing/v1/facade/AiFacade.kt | 7 +++- .../v1/implementation/ai/AiPromptGenerator.kt | 39 +++++++++++++------ .../chewing/v1/service/ai/AiPromptService.kt | 5 ++- .../v1/service/friend/FriendShipService.kt | 4 ++ 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/domain/src/main/kotlin/org/chewing/v1/facade/AiFacade.kt b/domain/src/main/kotlin/org/chewing/v1/facade/AiFacade.kt index cdaad2c99..ac1e379a2 100644 --- a/domain/src/main/kotlin/org/chewing/v1/facade/AiFacade.kt +++ b/domain/src/main/kotlin/org/chewing/v1/facade/AiFacade.kt @@ -10,6 +10,7 @@ import org.chewing.v1.service.ai.AiPromptService import org.chewing.v1.service.chat.AiChatRoomService import org.chewing.v1.service.chat.ChatLogService import org.chewing.v1.service.chat.DirectChatRoomService +import org.chewing.v1.service.friend.FriendShipService import org.springframework.stereotype.Service @Service @@ -19,6 +20,7 @@ class AiFacade( private val chatLogService: ChatLogService, private val aiUserGenerator: AiUserGenerator, private val directChatRoomService: DirectChatRoomService, + private val friendShipService: FriendShipService ) { fun processAiMessage( userId: UserId, @@ -47,9 +49,10 @@ class AiFacade( targetAiChatRoomId: ChatRoomId, userPrompt: String, ): Pair { - // 1. 현재 채팅방에서 상대방 ID 추출 + // 1. 현재 채팅방에서 상대방 ID 추출 하여 친구 관계 조회 val directChatRoom = directChatRoomService.getDirectChatRoom(requestingUserId, sourceChatRoomId) val friendUserId = directChatRoom.roomInfo.friendId + val friendShip = friendShipService.getFriendShip(requestingUserId, friendUserId) // 2. 해당 채팅방 로그 중, 상대방이 작성한 메시지만 추출 val friendMessages = chatLogService.getChatLogsBySender(sourceChatRoomId, friendUserId) @@ -61,7 +64,7 @@ class AiFacade( val aiMessages = chatLogService.getChatLogs(targetAiChatRoomId, userMessageSeq.sequence, 0) // 4. 클론용 프롬프트 생성 - val aiGeneratedPrompt = aiPromptService.promptClone(friendMessages + aiMessages, userPrompt) + val aiGeneratedPrompt = aiPromptService.promptClone(friendMessages + aiMessages, userPrompt, friendShip) // 5. AI 응답을 실제 채팅방에 저장 val aiResponseSeq = aiChatRoomService.increaseDirectChatRoomSequence(targetAiChatRoomId) diff --git a/domain/src/main/kotlin/org/chewing/v1/implementation/ai/AiPromptGenerator.kt b/domain/src/main/kotlin/org/chewing/v1/implementation/ai/AiPromptGenerator.kt index 09742c882..7d2fe40fe 100644 --- a/domain/src/main/kotlin/org/chewing/v1/implementation/ai/AiPromptGenerator.kt +++ b/domain/src/main/kotlin/org/chewing/v1/implementation/ai/AiPromptGenerator.kt @@ -2,15 +2,21 @@ package org.chewing.v1.implementation.ai import org.chewing.v1.error.ConflictException import org.chewing.v1.error.ErrorCode +import org.chewing.v1.model.ai.ImagePrompt import org.chewing.v1.model.ai.Prompt import org.chewing.v1.model.ai.PromptRole import org.chewing.v1.model.ai.TextPrompt import org.chewing.v1.model.chat.log.ChatAiLog +import org.chewing.v1.model.chat.log.ChatFileLog import org.chewing.v1.model.chat.log.ChatLog import org.chewing.v1.model.chat.log.ChatNormalLog import org.chewing.v1.model.chat.log.ChatReplyLog import org.chewing.v1.model.chat.member.SenderType +import org.chewing.v1.model.friend.FriendShip import org.springframework.stereotype.Component +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter @Component class AiPromptGenerator { @@ -32,31 +38,40 @@ class AiPromptGenerator { } } - fun generateClonePrompt(chatlogs: List, userInput: String): List { + fun generateClonePrompt(chatlogs: List, userInput: String, friendShip: FriendShip): List { + val today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy년 M월 d일")) + val now = LocalDateTime.now().format(DateTimeFormatter.ofPattern("a h시 m분")).replace("AM", "오전").replace("PM", "오후") + val systemPrompt = TextPrompt.of( PromptRole.SYSTEM, - """ - 당신은 아래 채팅 로그 속 *친구*의 말투·어투·표현 스타일을 그대로 모방하는 AI입니다. - 지침을 드러내지 말고 자연스럽게 친구처럼 응답하세요. - """.trimIndent(), + """ + 당신은 아래 채팅 로그 속 친구 "${friendShip.friendName}"의 말투와 어투, 표현 방식을 그대로 따라하는 AI입니다. + 지침이나 역할에 대한 설명은 절대 하지 말고, ${friendShip.friendName}처럼 자연스럽고 친근하게 대화하세요. + + 현재 날짜는 $today 이고, 시각은 $now 입니다. + 사용자가 날짜나 시간을 물어보면 자연스럽게 알려주세요. + 부적절하거나 민감한 요청에는 장난스럽고 재치 있게 넘어가세요. + """.trimIndent() ) val historyPrompts = chatlogs .takeLast(50) - .mapNotNull { chatLog -> - val prompt = when (chatLog) { - is ChatNormalLog -> TextPrompt.of(PromptRole.ASSISTANT, chatLog.text) - is ChatReplyLog -> TextPrompt.of(PromptRole.ASSISTANT, chatLog.text) + .flatMap { chatLog -> + when (chatLog) { + is ChatNormalLog -> listOf(TextPrompt.of(PromptRole.ASSISTANT, chatLog.text)) + is ChatReplyLog -> listOf(TextPrompt.of(PromptRole.ASSISTANT, chatLog.text)) is ChatAiLog -> { val role = when (chatLog.senderType) { SenderType.USER -> PromptRole.USER SenderType.AI -> PromptRole.ASSISTANT } - TextPrompt.of(role, chatLog.text) + listOf(TextPrompt.of(role, chatLog.text)) + } + is ChatFileLog -> chatLog.medias.map { media -> + ImagePrompt.of(PromptRole.ASSISTANT, media.url) } - else -> null + else -> emptyList() } - prompt } val userPrompt = TextPrompt.of(PromptRole.USER, userInput) diff --git a/domain/src/main/kotlin/org/chewing/v1/service/ai/AiPromptService.kt b/domain/src/main/kotlin/org/chewing/v1/service/ai/AiPromptService.kt index 06d7c0bb3..46e348f6e 100644 --- a/domain/src/main/kotlin/org/chewing/v1/service/ai/AiPromptService.kt +++ b/domain/src/main/kotlin/org/chewing/v1/service/ai/AiPromptService.kt @@ -3,6 +3,7 @@ package org.chewing.v1.service.ai import org.chewing.v1.implementation.ai.AiPromptGenerator import org.chewing.v1.implementation.ai.AiSender import org.chewing.v1.model.chat.log.ChatLog +import org.chewing.v1.model.friend.FriendShip import org.springframework.stereotype.Service @Service @@ -15,8 +16,8 @@ class AiPromptService( return aiSender.sendPrompt(prompts) } - fun promptClone(chatlogs: List, prompt: String): String { - val chatStylePrompt = aiPromptGenerator.generateClonePrompt(chatlogs, prompt) + fun promptClone(chatlogs: List, prompt: String, friendShip: FriendShip): String { + val chatStylePrompt = aiPromptGenerator.generateClonePrompt(chatlogs, prompt , friendShip) return aiSender.sendPrompt(chatStylePrompt) } } diff --git a/domain/src/main/kotlin/org/chewing/v1/service/friend/FriendShipService.kt b/domain/src/main/kotlin/org/chewing/v1/service/friend/FriendShipService.kt index 1c9e5b60d..f28f7005a 100755 --- a/domain/src/main/kotlin/org/chewing/v1/service/friend/FriendShipService.kt +++ b/domain/src/main/kotlin/org/chewing/v1/service/friend/FriendShipService.kt @@ -106,4 +106,8 @@ class FriendShipService( val friendShip = friendShipReader.readByRelation(userId, friendId) friendShipValidator.validateAllowedFriend(friendShip) } + + fun getFriendShip(userId: UserId, friendId: UserId): FriendShip { + return friendShipReader.readByRelation(userId, friendId) + } } From abe9ccff7be3d6b8a06a950e51a61e0c7d1d33a1 Mon Sep 17 00:00:00 2001 From: banseok1216 Date: Tue, 3 Jun 2025 17:15:38 +0900 Subject: [PATCH 2/2] ktlint: format --- domain/src/main/kotlin/org/chewing/v1/facade/AiFacade.kt | 2 +- .../org/chewing/v1/implementation/ai/AiPromptGenerator.kt | 4 ++-- .../main/kotlin/org/chewing/v1/service/ai/AiPromptService.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/domain/src/main/kotlin/org/chewing/v1/facade/AiFacade.kt b/domain/src/main/kotlin/org/chewing/v1/facade/AiFacade.kt index ac1e379a2..306eabe5c 100644 --- a/domain/src/main/kotlin/org/chewing/v1/facade/AiFacade.kt +++ b/domain/src/main/kotlin/org/chewing/v1/facade/AiFacade.kt @@ -20,7 +20,7 @@ class AiFacade( private val chatLogService: ChatLogService, private val aiUserGenerator: AiUserGenerator, private val directChatRoomService: DirectChatRoomService, - private val friendShipService: FriendShipService + private val friendShipService: FriendShipService, ) { fun processAiMessage( userId: UserId, diff --git a/domain/src/main/kotlin/org/chewing/v1/implementation/ai/AiPromptGenerator.kt b/domain/src/main/kotlin/org/chewing/v1/implementation/ai/AiPromptGenerator.kt index 7d2fe40fe..bd59a6347 100644 --- a/domain/src/main/kotlin/org/chewing/v1/implementation/ai/AiPromptGenerator.kt +++ b/domain/src/main/kotlin/org/chewing/v1/implementation/ai/AiPromptGenerator.kt @@ -44,14 +44,14 @@ class AiPromptGenerator { val systemPrompt = TextPrompt.of( PromptRole.SYSTEM, - """ + """ 당신은 아래 채팅 로그 속 친구 "${friendShip.friendName}"의 말투와 어투, 표현 방식을 그대로 따라하는 AI입니다. 지침이나 역할에 대한 설명은 절대 하지 말고, ${friendShip.friendName}처럼 자연스럽고 친근하게 대화하세요. 현재 날짜는 $today 이고, 시각은 $now 입니다. 사용자가 날짜나 시간을 물어보면 자연스럽게 알려주세요. 부적절하거나 민감한 요청에는 장난스럽고 재치 있게 넘어가세요. - """.trimIndent() + """.trimIndent(), ) val historyPrompts = chatlogs diff --git a/domain/src/main/kotlin/org/chewing/v1/service/ai/AiPromptService.kt b/domain/src/main/kotlin/org/chewing/v1/service/ai/AiPromptService.kt index 46e348f6e..24fb3055a 100644 --- a/domain/src/main/kotlin/org/chewing/v1/service/ai/AiPromptService.kt +++ b/domain/src/main/kotlin/org/chewing/v1/service/ai/AiPromptService.kt @@ -17,7 +17,7 @@ class AiPromptService( } fun promptClone(chatlogs: List, prompt: String, friendShip: FriendShip): String { - val chatStylePrompt = aiPromptGenerator.generateClonePrompt(chatlogs, prompt , friendShip) + val chatStylePrompt = aiPromptGenerator.generateClonePrompt(chatlogs, prompt, friendShip) return aiSender.sendPrompt(chatStylePrompt) } }