Open AI API 사용법

조예빈·2024년 8월 21일
0

1. 키 발급

https://platform.openai.com/api-keys
위 링크에 들어가 키를 발급받는다

이 때 팝업 창이 뜨면서 키 값이 발급되는데, 바로 복사를 해 두어야 한다. 창을 닫으면 더 이상 조회할 수 없고, 이 때는 키를 다시 재발급 받아야 하기 때문이다.

2. postman에서 test

요청 주소

https://api.openai.com/v1/chat/completions

Key값에 Autorization, Value값에 Bearer + 발급받은 key값을 넣어주면 된다.

body

json 형식으로 넣어주면 된다. 구조는 다음과 같다.

{
    "model": "gpt-4",
    "messages": [
        {
            "role": "system",
            "content": "You are a helpful assistant that categorizes text into three categories: Reflection ('반성문'), Frugality Confirmation ('절약 인증'), and Neither ('판단 안됨'). Based on the given text, you will return a response in the format [category, content]. The content should be a message you would give to a friend: scolding for '반성문' or praise for '절약 인증'. Use emojis to make it feel like a friendly conversation, and keep the message under 200 characters. Please write it without using a comma in the content. Don't use honorifics. Pretend you're talking informally to your friend. Focus on your current spending. Don't argue about cost-effectiveness or efficiency."
        },
        {
            "role": "user",
            "content": "길가다가 귀여운 부적이 보이길래 샀어."
        },
        {
            "role": "assistant",
            "content": "[반성문, 돈 쓰는 게 취미니? 💸 너무 쓰지 말고, 저금도 좀 해! 은행에 쌓아놓은 돈으로 ‘노후 준비'라는 거 알아? 나중에 맛있는 거 먹으려면 지금 좀 아껴야 해! 😜]"
        }
    ],
    "temperature": 0.7,
    "max_tokens": 200
}
  • model : 사용하고 싶은 gpt 모델. 가장 최신 버전을 사용하려면 gpt-4o를 적어주면 됨

  • messages : 대화의 흐름을 나타내는 메세지 리스트

    • role : 메세지를 보내는 주체

      • system : 시스템 메세지나 규칙 정의
      • user : 실제 사용자 입력
      • assistnat : 모델이 사용자에게 제공할 응답 예시(사용자가 입력하면 인공지능이 어떤 식으로 답변을 작성해야 할지 이해함)
    • temperature : 응답의 창의성을 조정하는 매개변수. 최대값이 1이며, 1에 가까울수록(값이 높을수록) 응답이 더 창의적이고, 0에 가까울수록(낮을수록) 일관됨. 위의 예시에서는 창의적인 답변을 원하기 때문에 0.7로 설정했고, 질문의 답변과 가장 가까운 답을 얻고 싶으면 0으로 해주면 됨.

    • max_tokens : 생성할 수 있는 최대 토큰 수. 200으로 설정해두면 최대 200개의 토큰(단어)가 생성될 수 있는 것. 단어의 수라고 생각하면 편하지만, 한 단어가 두 개의 토큰으로 나누어 질 수 있으므로 토큰의 단위가 더 작다고 생각하면 됨(ex)chatting : chat + ting)

응답


아래와 같은 형식으로 응답이 온다. 서버에서 choices > message > content를 추출해서 사용하면 된다.

3. Spring Boot 연동

application.properties

openai.model=gpt-4.0
openai.api.key={OpenAI_Key}
openai.api.url=//api.openai.com/v1/chat/completions

OpenAIConfig

OpenAI API와 통신하기 위한 설정 클래스

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.client.RestTemplate;

//OpenAI API와 통신하기 위한 메소드
@Configuration
public class OpenAIConfig {

    @Value("${openai.api.key}") //application.properties에서 값을 읽어와 필드에 주입
    private String openAIKey;

    @Bean(name = "openAIRestTemplate") //RestTemplate를 생성하고 반환. Http 요청을 간편하게 처리할 수 있는 유틸리티 클래스
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getInterceptors().add(((request, body, execution) -> { //인터셉터 추가
            //인터셉터 : Http 요청을 가로채 특정 작업을 수행할 수 있게 해 줌. 사용자의 요청이 컨트롤러에 가기 전에 가로채고, 서버의 응답이 사용자에게 가기 전에 가로챔
            //request : Http 요청(요청 메소드, url, 헤더 등과 같은 정보)
            //body : 요청의 본문. 필요하지 않다면 null(null일 경우에는 사용할 필요 X)
            //execution : 실제 요청을 실행하는 메소드 제공. 인터셉터에서 요청을 가로채고 수정한 후, execution.execute를 호출해 수정된 요청을 서버로 전달하고 응답을 받을 수 있음

            HttpHeaders headers = request.getHeaders(); //헤더 추가
            String apiKey = "Bearer " + openAIKey;

            headers.add("Authorization", apiKey); //헤더에 key값 설정
            headers.add("Content-Type", "application/json"); //데이터의 형식을 json으로 설정
            return execution.execute(request, body); //실제로 서버에 Http 요청을 보내는 작업 수행.
            //위의 return문은 인터셉터에서 수정된 요청이 서버로 전달된 후의 응답을 받아서 호출자에게 반환하는 역할을 함
        }));
        return restTemplate; //객체 반환. RestTemplate을 사용하여 Http 요청을 보낼 때마다 해당 인터셉터가 자동으로 요청을 가로채고 필요한 헤더를 추가한 뒤 요청 전송
    }
}

OpenAIRequestDTO

  • api로 보내는 요청값들을 담은 클래스. 이 때, 요청값 변수명이랑 request 안의 값의 변수명이 동일해야 함
  • 만약 동일하지 않다면 요청값이 동일하지 않다는 에러가 발생한다

    이 사진에서의 빨간 글씨들을 전부 DTO에 넣어 주어야 한다.
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class OpenAIRequestDTO {
    private String model;
    private List<Message> messages;
    private double temperature;
    private int max_tokens;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Message {
        private String role;
        private String content;
    }
}

OpenAIResponseDTO


마찬가지로 DTO에 위의 빨간 글씨를 전부 넣어 주어야 한다.
나는 여기에서 content값만 필요하기 때문에 응답값에서 content값만 추출해서 사용해주었다.

import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class OpenAIResponseDTO {
    private String id;
    private String object;
    private long created;
    private String model;
    private List<Choice> choices;
    private Usage usage;
    private String system_fingerprint;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Choice {
        private int index;
        private Message message;
        private Object logprobs;
        private String finish_reason;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Message {
        private String role;
        private String content;
        private Object refusal;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Usage {
        private int prompt_tokens;
        private int completion_tokens;
        private int total_tokens;
    }
}

OpenAIService

import com.choikang.poor.the_poor_back.dto.OpenAIRequestDTO;
import com.choikang.poor.the_poor_back.dto.OpenAIResponseDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;

@Service
public class OpenAIService {
    private final RestTemplate restTemplate;

    @Autowired
    public OpenAIService(@Qualifier("openAIRestTemplate") RestTemplate restTemplate) { //특정 타입의 빈을 주입받기 위해 Qualifier 사용
        this.restTemplate = restTemplate; //클래스 내의 필드에 저장해 필요할 때마다 꺼내서 사용할 수 있도록 함
    }

    //응답값에서 메세지만 추출
    public String[] getResponseMessage(OpenAIRequestDTO requestDTO) {
        //Http 요청을 보낼 때 사용되는 HttpEntity 객체 생성
        //requestDTO 객체는 Http 요청 본문에 들어갈 데이터를 가지고 있음
        HttpEntity<OpenAIRequestDTO> requestEntity = new HttpEntity<>(requestDTO);

        // API 호출
        String openAIUrl = "https://api.openai.com/v1/chat/completions"; //API를 호출할 url
        try {
            ResponseEntity<OpenAIResponseDTO> responseEntity = restTemplate.exchange(
                    openAIUrl, //요청을 보낼 url
                    HttpMethod.POST, //사용할 Http 메서드
                    requestEntity, //요청 본문과 헤더를 포함하는 객체. 요청 본문이 필요 없는 경우에는 HttpEntity<Void> 사용
                    OpenAIResponseDTO.class //응답 본문을 DTO 클래스의 인스턴스로 변환
            );

            OpenAIResponseDTO response = responseEntity.getBody(); //exchange 메소드에서 반환된 객체의 body를 반환
            if (response != null && response.getChoices() != null && !response.getChoices().isEmpty()) {
                String responseStr = response.getChoices().get(0).getMessage().getContent();
                return responseStr.split(",", 2);
            }
            return new String[]{"판단 안됨", null};
        } catch (HttpClientErrorException e) {
            System.err.println("응답 바디: " + e.getResponseBodyAsString());
            throw e;
        }
    }
}

여기서 @Autowired을 활용하여 의존성을 주입받는다. 이 때, 의존성을 주입받는 것을 리모컨에 비유할 수 있다.

만약, 집에 있는 리모컨을 가지고 TV를 켜고 싶은 상황을 가정해보자. 이 때, 리모컨을 RestTemplate라고 생각하자. 누군가가 리모컨을 건네주고, 이것을 내가 손에 들고 있으면 언제든지 TV를 켜거나 채널을 바꿀 수 있는 것이다.
@Autowired를 통해 의존성을 주입하는 것은 리모컨을 건네받는 행동이라고 생각하면 된다. 이제 의존성 주입을 통해 리모컨을 건네받았으므로 필요할 때마다 사용할 수 있다. 필요 시마다 사용하기 위해 RestTemplate을 클래스 내의 필드에 저장해 두는 것이다.

testOpenAIRequest

더미데이터를 넣고 gpt의 응답이 제대로 오는지 확인하기 위한 test code

package com.choikang.poor.the_poor_back.OpenAITest;

import com.choikang.poor.the_poor_back.dto.OpenAIRequestDTO;
import com.choikang.poor.the_poor_back.service.OpenAIService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class OpenAIServiceTest {

    @Autowired
    private OpenAIService openAIService;

    @Test
    void testOpenAIRequest() {
        // 더미데이터 넣기
        OpenAIRequestDTO requestDTO = new OpenAIRequestDTO();
        requestDTO.setModel("gpt-4");
        requestDTO.setMessages(List.of(
                new OpenAIRequestDTO.Message("system",
                        "You are a helpful assistant that categorizes text into three categories: Reflection ('반성문'), Frugality Confirmation ('절약 인증'), and Neither ('판단 안됨'). Based on the given text, you will return a response in the format category, content. The content should be a message you would give to a friend: scolding for '반성문' or praise for '절약 인증'. Use emojis to make it feel like a friendly conversation, and keep the message under 200 characters. Please write it without using a comma in the content. Don't use honorifics. Pretend you're talking informally to your friend. Focus on your current spending. Don't argue about cost-effectiveness or efficiency."),
                new OpenAIRequestDTO.Message("user", "당근마켓에서 100만원주고 노트북 샀어")
        ));
        requestDTO.setTemperature(0.7);
        requestDTO.setMax_tokens(200);

        //응답 받아와서 추출 및 출력하기
        try {
            String[] response = openAIService.getResponseMessage(requestDTO);
            System.out.println(response);
            String gptAnswerType = response[0];
            String gptAnswerContent = response[1];

            System.out.println("타입: " + gptAnswerType);
            System.out.println("내용: " + gptAnswerContent);
        } catch (Exception e) {
            System.out.println("예외 발생: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

profile
컴퓨터가 이해하는 코드는 바보도 작성할 수 있다. 사람이 이해하도록 작성하는 프로그래머가 진정한 실력자다. -마틴 파울러

0개의 댓글