Chat GPT API 연동, 질문답변 매뉴얼 생성

YUNU·2023년 6월 22일
0
post-thumbnail

🧠 Chat GPT API 연동


원래는 Chat GPT4.0 API를 사용하려 하였으나 승인이 나는데 시간이 오래 걸려 GPT3.5를 사용하였다.


java11
springboot 2.7.12
RestTemplate
Jackson

🟦 GPT Key 생성

OpenAI 홈페이지에서 API key를 생성

key값 생성시에 key값을 복사 후 잘 보관해 두어야 한다

OpenAI API key를 확인하는 페이지에서는 생성한 key값의 일부만을 확인 가능

🟦 build.gradle

build.gradle에 아래와 같은 의존성 추가

    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'com.fasterxml.jackson.core:jackson-databind'
    implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'

🟦 Service Code

@Service
public class GPTService {
    private static final String API_URL = "https://api.openai.com/v1/chat/completions";
    private static final String API_KEY = "Key 값"; // 본인의 API 키를 사용하세요.
    private final RestTemplate restTemplate;
    private final ObjectMapper objectMapper;

    @Autowired
    public GPTService(RestTemplateBuilder restTemplateBuilder, ObjectMapper objectMapper) {
        this.restTemplate = restTemplateBuilder.build();
        this.objectMapper = objectMapper;
    }

//DB에서 수질 정보 가져오는 메소드
    public String getWaterInfo(String id) {
        String url = "http://localhost:8080/waterinfo/yearly/" + id;
       //
        ...
    }

//DB에서 급이 기록 가져오는 메소드
    public String getFoodRecord() {
        String url = "http://localhost:8080/fish-info/food-record";
        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
        return response.getBody();
    }

//DB에서 일출,일몰 시간 가져오는 메소드
    public String getSunriseSunset() {
        String url = "http://localhost:8080/weather/sunrise-sunset";
        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
        return response.getBody();
    }

//질문하기 전에 GPT에 데이터 Input
    public String prepareMessage(String waterInfo, String foodRecord, String sunriseSunset) {
        return "Here is the water info for the year: " + waterInfo + "\n"
                + "Here is the feeding record: " + foodRecord + "\n"
                + "Here is the sunrise and sunset info for today: " + sunriseSunset + "\n";
    }

//일출, 일몰 시간 String -> LocalTime으로 파싱
    private SunriseSunsetData parseSunriseSunset(String sunriseSunset) {
        // ...
    }

//질의응답 매뉴얼
    public String askGPT(String question) {
        try {
        	// Data Input
            String waterInfo = getWaterInfo("1");
            String foodRecord = getFoodRecord();
            String sunriseSunset = getSunriseSunset();
            String preMessage = prepareMessage(waterInfo, foodRecord, sunriseSunset);

            String modifiedQuestion = question.toLowerCase();
            // 다음과 같은 질문을 포함한 경우의 답변 매뉴얼 생성
            if (modifiedQuestion.contains("밥을 언제 줄까?")) {
                SunriseSunsetData sunriseSunsetData = parseSunriseSunset(sunriseSunset);
                if (sunriseSunsetData != null) {
                    LocalDateTime sunriseDateTime = sunriseSunsetData.getSunriseDateTime();
                    LocalDateTime sunsetDateTime = sunriseSunsetData.getSunsetDateTime();

                    // 밥 주는 시간 계산

                    // 밥 주는 시간을 문자열로 포맷팅 

                    // 응답에 밥 주는 시간 추가하기
                    preMessage += "광어에게 밥을 줄 시간은 아침: " + formattedFeedingTime1 + ", 저녁: " + formattedFeedingTime2 + "입니다.";
                } else {
                    // 오류 처리 로직 추가
                }
            } else if( // 조건) 
            	// 답변에 포함시킬 내용
                // ...
            }

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("Authorization", "Bearer " + API_KEY);

            List<Map<String, String>> messages = new ArrayList<>();

            // System message with preMessage
            Map<String, String> systemMessage = new HashMap<>();
            systemMessage.put("role", "system");
            systemMessage.put("content", preMessage);
            messages.add(systemMessage);

            // User message with the question
            Map<String, String> userMessage = new HashMap<>();
            userMessage.put("role", "user");
            userMessage.put("content", question);
            messages.add(userMessage);
			
            // 답변 가능한 최대 글자수, 사용 모델 설정
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("messages", messages);
            requestBody.put("max_tokens", 500);
            requestBody.put("model", "gpt-3.5-turbo");

            HttpEntity<String> entity = new HttpEntity<>(objectMapper.writeValueAsString(requestBody), headers);
            ResponseEntity<String> response = restTemplate.exchange(API_URL, HttpMethod.POST, entity, String.class);

            if (response.getStatusCode() == HttpStatus.OK) {
                System.out.println("Response: " + response.getBody());
                Map<String, Object> responseBody = objectMapper.readValue(response.getBody(), Map.class);
                if (responseBody.containsKey("choices")) {
                    List<Map<String, Object>> choices = (List<Map<String, Object>>) responseBody.get("choices");
                    if (choices != null && !choices.isEmpty() && choices.get(0).containsKey("message")) {
                        Map<String, Object> messageResponse = (Map<String, Object>) choices.get(0).get("message");
                        if (messageResponse.containsKey("content")) {
                            String answer = (String) messageResponse.get("content");
                            return answer.trim();
                        }
                    }
                }
            } else {
                throw new RuntimeException("ChatGPT API 요청 실패: " + response.getStatusCode());
            }
        } catch (Exception e) {
            throw new RuntimeException("ChatGPT API 호출 중 오류 발생", e);
        }
        throw new RuntimeException("ChatGPT API 호출에서 예상치 못한 상황 발생");
    }
}

🟦 Contoller Code

@CrossOrigin
@RestController
@RequestMapping("/gpt")
public class GPTController {
	// Service의 class file 명 : GPTService
    private final GPTService gptService;

    @Autowired
    public GPTController(GPTService gptService) {
        this.gptService = gptService;
    }

    @PostMapping("/chat/completions")
    public String askGPT(@RequestBody String question) {
        return gptService.askGPT(question);
    }
}

🟦 Html Code

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>GPT Chat</title>
  <style>
   // CSS 생략
  </style>
</head>
<body>
<div class="container">
  <div class="header">
    <img class="logo" src="https://upload.wikimedia.org/wikipedia/commons/0/04/ChatGPT_logo.svg">
    <h2 class="title">GPT 질문 내역</h2>
  </div>
  <div class="input-container">
    <input type="text" id="question-input" class="input-field" placeholder="질문을 입력하세요">
    <button id="question-button" class="button">질의문 버튼</button>
  </div>
  <div class="header">
    <img class="logo" src="https://upload.wikimedia.org/wikipedia/commons/0/04/ChatGPT_logo.svg">
    <h2 class="title">GPT 답변 내역</h2>
  </div>
  <div id="answer-container" class="answer-container"></div>
</div>
  <script>
	//...
  </script>
</body>
</html>

🟦 Javascript Code

<script>
    // 질의문 버튼 클릭 이벤트 핸들러
    document.getElementById('question-button').addEventListener('click', async () => {
      const questionInput = document.getElementById('question-input');
      const question = questionInput.value.trim();
      questionInput.value = '';

      if (question) {
        const response = await fetch('http://localhost:8080/gpt/chat/completions', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            question: question
          })
        });

        if (response.ok) {
          const answer = await response.text();
          const answerContainer = document.getElementById('answer-container');
          answerContainer.innerHTML = answer;
        } else {
          console.error('Failed to fetch GPT answer');
        }
      }
    });
</script>

🟦 결과 화면

"오늘 밥 언제 줄까?" 라고 질문한 결과

사진에서 볼 수 있듯이 의도한 답과는 차이가 있는 결과를 내는 경우 생기며

같은 질문을 하더라도 답변이 조금씩 다름

내가 예상한 넙치의 먹이 활동이 활발한 시간은 일몰, 일출시간 30분 전인데 GPT는 일출 후 1~2시간 후와 일몰 직전을 먹이 활동이 활발하다고 판단하였다.

나는 GPT에게 내가 계획한 시간 대에 급이를 하라는 답변 매뉴얼을 설정하였으나 그와는 다른 답변을 도출한다.

내가 예상한 넙치의 먹이 활동이 활발한 시간은 넙치 양식장에서 급이를 하는 시간과 낚시 카페에서 넙치가 잘 잡힌다는 시간을 비교하여 추정한 수치이기에 정확하지 않을 수 있으나 GPT의 답변 기준은 무엇을 기준으로 한 것인지 알 수 없다.

profile
DDeo99

0개의 댓글