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
7 changes: 5 additions & 2 deletions domain/src/main/kotlin/org/chewing/v1/facade/AiFacade.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -47,9 +49,10 @@ class AiFacade(
targetAiChatRoomId: ChatRoomId,
userPrompt: String,
): Pair<ChatAiMessage, ChatAiMessage> {
// 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)
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -32,31 +38,40 @@ class AiPromptGenerator {
}
}

fun generateClonePrompt(chatlogs: List<ChatLog>, userInput: String): List<Prompt> {
fun generateClonePrompt(chatlogs: List<ChatLog>, userInput: String, friendShip: FriendShip): List<Prompt> {
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -15,8 +16,8 @@ class AiPromptService(
return aiSender.sendPrompt(prompts)
}

fun promptClone(chatlogs: List<ChatLog>, prompt: String): String {
val chatStylePrompt = aiPromptGenerator.generateClonePrompt(chatlogs, prompt)
fun promptClone(chatlogs: List<ChatLog>, prompt: String, friendShip: FriendShip): String {
val chatStylePrompt = aiPromptGenerator.generateClonePrompt(chatlogs, prompt, friendShip)
return aiSender.sendPrompt(chatStylePrompt)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}