Skip to content
Open
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
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ configurations {

repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
}

sourceSets {
Expand All @@ -37,6 +38,8 @@ sourceSets {
}

dependencies {
implementation platform('org.springframework.ai:spring-ai-bom:0.8.1')
implementation 'org.springframework.ai:spring-ai-openai-spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/com/space/server/common/config/OpenAiConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.space.server.common.config;

import org.springframework.ai.openai.OpenAiChatClient;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OpenAiConfig {
@Value("${spring.ai.openai.api-key}")
private String openAiApiKey;

@Bean
public OpenAiApi openAiApi() {
return new OpenAiApi(openAiApiKey);
}

@Bean
public OpenAiChatClient openAiChatClient(OpenAiApi openAiApi) {
return new OpenAiChatClient(openAiApi);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
import com.space.server.domain.ai.service.implementation.ChatTuner;
import com.space.server.domain.chat.presentation.dto.request.CreateChatRequest;
import com.space.server.domain.chat.presentation.dto.request.ReadQuizAndUserRequest;
import com.space.server.domain.chat.presentation.dto.response.ChatResponse;
import com.space.server.domain.chat.presentation.dto.response.CountChatResponse;
import com.space.server.domain.chat.presentation.dto.response.ReadKeyWordsResponse;
import com.space.server.domain.chat.presentation.dto.response.ReadSuccessChatResponse;
import com.space.server.domain.chat.presentation.dto.response.*;
import com.space.server.domain.chat.service.CommandChatService;
import com.space.server.domain.chat.service.QueryChatService;
import com.space.server.domain.state.service.QueryStateService;
Expand Down Expand Up @@ -60,6 +57,11 @@ public ReadKeyWordsResponse readMostKeyWords() {
);
}

@GetMapping("/chats/typo")
public VocabularyFeedbackResponse readTypo() {
return queryChatService.findTypo(getMemberId());
}

@PostMapping("/chats/count")
public CountChatResponse countChatByQuiz(@RequestBody ReadQuizAndUserRequest request) {
return CountChatResponse.of(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.space.server.domain.chat.presentation.dto.response;

public record VocabularyFeedbackResponse(
String vocabularyAnalysis,
String aiGeneratedFeedback
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.space.server.domain.chat.domain.Chat;
import com.space.server.domain.chat.presentation.dto.response.ChatResponse;
import com.space.server.domain.chat.presentation.dto.response.VocabularyFeedbackResponse;
import com.space.server.domain.chat.service.implementation.ChatAnalyzer;
import com.space.server.domain.chat.service.implementation.ChatReader;
import com.space.server.domain.quiz.service.implementation.QuizReader;
Expand All @@ -10,18 +11,22 @@
import com.space.server.domain.state.service.implementation.StateReader;
import com.space.server.domain.user.service.implementation.UserReader;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.openai.OpenAiChatClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class QueryChatService {

private final OpenAiChatClient chatClient;
private final ChatReader chatReader;
private final StateReader stateReader;
private final QuizReader quizReader;
Expand Down Expand Up @@ -59,6 +64,50 @@ public Map<String, Long> readMostKeyWords(Long userId) {
));
}

public VocabularyFeedbackResponse findTypo(Long userId) {
// 사용자의 상태와 채팅 내용 조회
List<State> states = stateReader.findByUserId(userReader.findById(userId));
List<Chat> chats = chatReader.findAllChatByStates(states);

// 모든 채팅 내용 병합
String mergedChats = chats.stream()
.map(Chat::getUserChat)
.filter(Objects::nonNull)
.collect(Collectors.joining(" "));

// 어휘 오류 분석 프롬프트
String analysisPrompt = String.format(
"다음 텍스트에서 가장 많이 틀린 어휘 상위 5개를 찾아주세요. " +
"각 어휘에 대해 몇 번 잘못 썼는지, 올바른 표현은 무엇인지 알려주세요. " +
"초등학생의 글입니다. 텍스트: %s", mergedChats
);

// 어휘 오류 분석
String analysisResponse = chatClient.call(analysisPrompt);

// 피드백 생성 프롬프트
String feedbackPrompt = String.format(
"다음은 초등학생의 어휘 사용 분석입니다: %s\n\n" +
"이 분석을 바탕으로 부모님과 선생님을 위한 상세하고 구체적인 어휘력 향상 피드백을 만들어주세요. " +
"다음 사항을 포함해주세요:\n" +
"1. 발견된 어휘 오류의 구체적인 교정 방법\n" +
"2. 학생의 어휘력을 높이기 위한 맞춤형 학습 전략\n" +
"3. 연령에 적합한 어휘 학습 접근 방식\n" +
"4. 부모님과 선생님이 함께할 수 있는 실천 가능한 활동\n" +
"5. 장기적인 어휘력 향상을 위한 지속 가능한 방법",
analysisResponse
);

// AI 생성 피드백 요청
String aiGeneratedFeedback = chatClient.call(feedbackPrompt);

// 결과 객체 생성
VocabularyFeedbackResponse feedback = new VocabularyFeedbackResponse(analysisResponse, aiGeneratedFeedback);

return feedback;
}



public Integer countChats(Long quizId, Long userId) {
State state = stateReader.findByQuizIdAndUserId(quizReader.findById(quizId), userReader.findById(userId))
Expand Down
7 changes: 7 additions & 0 deletions src/main/resources/application-common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ spring:
username: ${DB_USER}
password: ${DB_PASSWORD}

ai:
openai:
api-key: ${GPT_API_KEY}
chat:
options:
model: gpt-4o

logging:
level:
org:
Expand Down
Loading