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..306eabe5c 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..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 @@ -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입니다. - 지침을 드러내지 말고 자연스럽게 친구처럼 응답하세요. + 당신은 아래 채팅 로그 속 친구 "${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..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 @@ -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) + } }