Skip to content

Commit

Permalink
[merge]: SpringAi GPT 연결 및 프롬프트 작성 (#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
kanguk01 authored Oct 25, 2024
2 parents 3677193 + 0c16622 commit 58b8130
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 0 deletions.
14 changes: 14 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ configurations {

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

dependencies {
Expand Down Expand Up @@ -61,6 +67,14 @@ dependencies {
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

//gpt
// implementation 'org.springframework.boot:spring-boot-starter-web'
// implementation 'com.fasterxml.jackson.core:jackson-databind'
// implementation 'org.springframework.boot:spring-boot-starter'
implementation platform("org.springframework.ai:spring-ai-bom:0.8.0")
implementation 'org.springframework.ai:spring-ai-openai'


}

tasks.named('test') {
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/splanet/splanet/gpt/Message.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.splanet.splanet.gpt;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
class Message {
private String role; // 메시지의 역할 (user, assistant 등)
private String content; // 메시지 내용
}
57 changes: 57 additions & 0 deletions src/main/java/com/splanet/splanet/gpt/OpenAiChatClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.splanet.splanet.gpt;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;

import java.util.List;

@Component
public class OpenAiChatClient {

private final WebClient webClient;
private final OpenAiProperties openAiProperties;
private final SchedulePromptGenerator promptGenerator;

public OpenAiChatClient(WebClient.Builder webClientBuilder, OpenAiProperties openAiProperties, SchedulePromptGenerator promptGenerator) {
this.webClient = webClientBuilder.baseUrl("https://api.openai.com/v1").build();
this.openAiProperties = openAiProperties;
this.promptGenerator = new SchedulePromptGenerator();
}

// 스케줄 생성 요청 처리 메소드
public ScheduleResponse createSchedule(ScheduleRequest scheduleRequest) throws JsonProcessingException {
String jsonResponse = call(scheduleRequest);
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(jsonResponse, ScheduleResponse.class);
}

private String call(ScheduleRequest scheduleRequest) throws JsonProcessingException {
String jsonRequest = new ObjectMapper().writeValueAsString(new RequestBody("gpt-4o-mini", List.of(
new Message("user", promptGenerator.generateSchedulePrompt(scheduleRequest))
)));

// OpenAI API 호출
String responseJson = webClient.post()
.uri("/chat/completions")
.header("Authorization", "Bearer " + openAiProperties.getApiKey())
.bodyValue(jsonRequest)
.retrieve()
.bodyToMono(String.class)
.block();

return responseJson;
}

// 스트리밍 메소드
public Flux<ScheduleResponse> stream(Prompt prompt) {
return webClient.post()
.uri("/chat/completions")
.header("Authorization", "Bearer " + openAiProperties.getApiKey())
.bodyValue(prompt)
.retrieve()
.bodyToFlux(ScheduleResponse.class);
}
}
14 changes: 14 additions & 0 deletions src/main/java/com/splanet/splanet/gpt/OpenAiProperties.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.splanet.splanet.gpt;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Getter
@Setter
@ConfigurationProperties(prefix = "gpt-api-key")
@Configuration
public class OpenAiProperties {
private String apiKey;
}
12 changes: 12 additions & 0 deletions src/main/java/com/splanet/splanet/gpt/Prompt.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.splanet.splanet.gpt;

import lombok.Getter;
import lombok.AllArgsConstructor;

import java.util.List;

@Getter
@AllArgsConstructor // 모든 필드를 사용하는 생성자를 자동 생성
public class Prompt {
private List<Message> messages; // 메시지 목록
}
13 changes: 13 additions & 0 deletions src/main/java/com/splanet/splanet/gpt/RequestBody.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.splanet.splanet.gpt;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.List;

@Getter
@AllArgsConstructor
public class RequestBody {
private String model; // 모델 이름
private List<Message> messages; // 메시지 리스트
}
37 changes: 37 additions & 0 deletions src/main/java/com/splanet/splanet/gpt/SchedulePromptGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.splanet.splanet.gpt;

import org.springframework.stereotype.Component;

@Component
public class SchedulePromptGenerator {

public String generateSchedulePrompt(ScheduleRequest request) {
StringBuilder prompt = new StringBuilder();

prompt.append("splanet은 사용자가 입력한 스케줄 정보를 바탕으로 맞춤형 플래너를 제공하는 서비스입니다. 사용자가 음성으로 입력한 정보를 분석하여 최적의 스케줄을 제시해야 합니다.\n\n")
.append("사용자가 다음과 같은 정보를 입력했습니다. 이 정보를 바탕으로 요청된 컨셉에 따라 스케줄을 추천해 주세요. 각 스케줄은 하루 24시간을 30분 단위로 쪼개고, 각 업무의 시작 시간과 종료 시간을 포함해야 합니다.\n\n")
.append("요청 정보:\n")
.append("- 스케줄 기간: \"10월 1일부터 10월 2일까지\"\n")
.append("- 업무 목록: ").append(request.getTaskList()).append("\n")
.append("- 업무 소요 시간: ").append(request.getTaskDurations()).append("\n")
.append("- 우선순위: ").append(request.getPriority()).append("\n")
.append("- 스케줄 컨셉: \"널널한 스케줄\", \"빡빡한 스케줄\"\n")
.append("- 하루를 30분 단위로 쪼갠 시간 목록: ").append(request.getTimeSlots()).append("\n")
.append("위 정보를 바탕으로 다음과 같은 조건을 준수하여 스케줄을 추천해주세요:\n")
.append("1. 요청된 모든 일정을 사용해야 합니다.\n")
.append("2. 각 스케줄은 요청된 두 개의 서로 다른 컨셉(사용자가 입력한다. 예시로는 널널한 스케줄, 빡빡한 스케줄이 있다.)을 따릅니다.\n")
.append("3. 추천 예시: 사용자가 선택할 수 있도록 두 개의 추천 스케줄을 제공합니다.\n")
.append("4. 응답 형식: 답변은 JSON 형식으로만 제공해야 하며, 다음과 같은 구조를 따라야 합니다:\n")
.append(" { \"schedules\": [{ \"concept\": \"스케줄 컨셉\", \"schedule\": [{ \"date\": \"MM-DD\", \"tasks\": [{ \"task\": \"업무명\", \"duration\": \"소요시간\", \"priority\": \"우선순위\", \"startTime\": \"시작시간\", \"endTime\": \"종료시간\" }] }] }] }] }\n")
.append("5. 형식 규칙:\n")
.append(" - 날짜는 MM-DD 형식으로, 시간은 24시간(30분 단위) 형식이어야 합니다.\n")
.append(" - 입력받은 업무를 지정된 일정 안에 모두 포함시켜야 합니다.\n")
.append(" - 우선순위는 고유한 정수 값이어야 합니다.\n")
.append(" - 업무 시간은 주어진 스케줄 기간 안에만 분할하여 채울 수 있습니다.\n")
.append(" - 입력받은 날짜 각각의 일정을 구현해야 합니다.\n")
.append("6. 예시: 결과값으로 각 날짜마다 널널한 스케줄과 빡빡한 스케줄 컨셉을 입력받으면, 스케줄1(빡빡한 스케줄): 10월1일+10월2일, 스케줄2(널널한 스케줄): 10월1일+10월2일 총 4개의 스케줄을 제시해야 합니다. 오직 스케줄 데이터 정보만 json으로 출력한다.\n")
.append("7. 제외할 내용: 일정 JSON 외에는 다른 내용이 포함되지 않아야 하며, 스케줄링과 관련 없는 질문에는 \"이와 관련된 질문에는 답변할 수 없습니다.\"라고 응답해야 합니다.\n");

return prompt.toString();
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/splanet/splanet/gpt/ScheduleRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.splanet.splanet.gpt;

import lombok.*;

import java.util.List;
import java.util.Map;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ScheduleRequest {
private String schedulePeriod; // 스케줄 기간
private List<String> taskList; // 업무 목록
private Map<String, String> taskDurations; // 업무 소요 시간 (업무명: 소요시간)
private Map<String, Integer> priority; // 업무 우선순위 (업무명: 우선순위)
private List<String> scheduleConcepts; // 스케줄 컨셉 목록
private List<String> timeSlots; // 30분 단위 시간 목록
}
33 changes: 33 additions & 0 deletions src/main/java/com/splanet/splanet/gpt/ScheduleResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.splanet.splanet.gpt;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

@Getter
@AllArgsConstructor
@NoArgsConstructor
public class ScheduleResponse {
private List<Schedule> schedules; // 스케줄 리스트

@Getter
@AllArgsConstructor
@NoArgsConstructor
public static class Schedule {
private String concept; // 스케줄 컨셉
private List<Task> tasks; // 업무 리스트

@Getter
@AllArgsConstructor
@NoArgsConstructor
public static class Task {
private String task; // 업무명
private String duration; // 소요 시간
private int priority; // 우선순위
private String startTime; // 시작 시간
private String endTime; // 종료 시간
}
}
}

0 comments on commit 58b8130

Please sign in to comment.