프로젝트에서 ai 활용을 하는 기능을 개발하게 되었습니다.
따라서 gpt api를 활용하기 전 먼저 api가 어떻게 되어있는지 테스트를 진행해보았습니다.
스프링에서 지원하는 spring ai api가 있긴했지만 사용하기가 어려워서 직접 http를 전송하는 것으로 시도해보았습니다.
사용법은 비교적 간단했습니다. openai 서버 endpoint에 개인별 api-key를 헤더에 추가하여 인증을 하고, 모델을 정해서 메세지를 보내기만 하면 됩니다.
Gpt 모델에 메세지는 다음 2가지의 형식으로 보내게 됩니다.
# 단순 메세지만 전송
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "Hello!"
}
]
}'
# 옵션을 설정하여 메세지를 같이 전송
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "gpt-3.5-turbo-0613",
"system_fingerprint": "fp_44709d6fcb",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "\n\nHello there, how may I assist you today?",
},
"logprobs": null,
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 12,
"total_tokens": 21
}
}
위 명령어에 맞게 형식을 맞춰서 http 요청을 보내면 response를 받을 수 있습니다.
openai 서버로 보낼 restTemplate 만들기
먼저 http 요청을 보내기 위해선 restTemplate이 필요합니다. 따라서 restTemplate을 설정할 config 파일을 생성해줘야합니다.
@Configuration
public class GptConfig {
@Value("${gpt.api-key}")
private String apiKey;
@Bean
public RestTemplate restTemplate(){
RestTemplate template = new RestTemplate();
template.getInterceptors().add((request, body, execution) -> {
request.getHeaders().add(
"Authorization"
,"Bearer " + apiKey);
request.getHeaders().setContentType(APPLICATION_JSON);
return execution.execute(request, body);
});
return template;
}
}
gpt 서버에 보내기 위해선 인증키가 헤더에 포함되어야하기 때문에 개인 api key가 필요한데요. 본인의 api 키는 https://platform.openai.com/api-keys 에서 생성하시면 됩니다.
전송 request 생성
request안에 데이터
스프링에서는 json 구조를 dto를 생성해서 보내면 간단히 구조를 만들 수 있습니다.
@Getter
public class GptRequestDto {
private String model; // 모델명
private List<Message> messages; // 질문들
private float temperature; // 답변 다양성
private int max_tokens; // 답변 최대 길이
private int top_p; // 답변 다양성
private int frequency_penalty; // 답변 중복 방지
private int presence_penalty; // 답변 중복 방지
}
@Getter
public class Message {
private String role;
private String content;
}
저는 다음과 같은 구조로 request 객체를 만들었습니다.
model과 messages이외에 다른 옵션은 필요하지 않았지만, 이후 옵션 테스트를 해보고 싶어 추가해 두었습니다.
각각 옵션의 설명을 하겠습니다.
- model: 사용할 모델의 ID입니다. 모델의 종류는 https://platform.openai.com/docs/models 여기서 찾아보실 수 있습니다.
- messages: gpt 모델에게 물어볼 메세지 내용입니다. messages 객체가 list인 이유는 누적된 대화를 가져올 수 있게 하기 위함입니다.
- temperature: 값이 높을수록 출력이 더 무작위로 만들어지고, 값이 낮을수록 더 집중적이고 결정적이게 됩니다.
- max_tokens: 채팅 완료 시 생성할 수 있는 최대 토큰 수입니다. 반환 메세지의 길이를 제한합니다.
- top_p: 값이 높을수록 생성된 텍스트의 다양성이 증가합니다.
- frequency_penalty: 모델이 같은 단어나 구를 반복하는 것을 억제합니다. 높은 값은 모델이 동일한 내용을 반복하는 것을 더 많이 피하도록 합니다. 일반적으로 값이 1이상이면 이상한 값을 도출하기 때문에 0으로 설정하고 진행한다고 합니다.
- presence_penalty: 모델이 이미 언급된 단어나 구를 다시 사용하는 것을 억제하는 데 사용됩니다. 위 옵션과 유사하지만 단어의 존재 여부에 좀 더 중점을 두고 연산되는 옵션입니다.
role은 대화 상황에서 각 메시지가 어떤 역할을 하는지를 나타내는 것입니다. 이는 대화형 시나리오에서 메시지의 맥락을 이해하는 데 도움을 줍니다.
메세지 전송
이렇게 생성된 request는 다음과 같이 사용됩니다.
public GptResponseDto chat(String model, String prompt, String endpointCharged) {
List<Message> prompts = List.of(
new Message("user", prompt));
GptRequestDto request = new GptRequestDto(model, prompts, 1, 256, 1, 0, 0);
// OpenAI server로 restTemplate을 통해 request를 보내고 response를 받는다.
GptResponseDto gptResponse = restTemplate
.postForObject(endpointCharged, request, GptResponseDto.class);
if (gptResponse != null) {
return gptResponse;
} else {
throw new RuntimeException("Error parsing response from OpenAI Server");
}
}
메소드에서 입력받는 각 파라미터를 말씀드리겠습니다.
model: 사용할 GPT 모델을 지정합니다. 여기에는 gpt-3.5, gpt-3.5-turbo, gpt-4 등이 올 수 있습니다. 이는 모델의 성능과 특징에 따라 선택됩니다.
prompt: 사용자 또는 시스템이 모델에게 보내는 메시지 내용입니다. 이 입력값에 기반하여 모델이 응답을 생성합니다.
endpointCharged: GPT 서비스를 제공하는 서버의 API 엔드포인트 주소입니다. 이 주소는 요청을 보내고 응답을 받는 데 사용됩니다.
이제 이 입력값들을 request 객체로 감싸고 RestTemplate를 사용하여 GPT 서버로 요청을 보내게됩니다.
만약 여기서 요청을 받아온다면 GptResponseDto 객체로 매핑이 되고, 어떠한 문제가 발생한다면 예외를 발생시키도록 작성했습니다.
GptResponse는 다음과 같은 형태입니다.
각 필드별 설명은 https://platform.openai.com/docs/guides/text-generation/json-mode 에 자세히 나와있습니다.
@Getter
public class GptResponseDto {
private List<Choice> choices;
private String id;
private String model;
private String object;
}
@Getter
public class Choice {
private int index;
private Message message;
}
이렇게 생성된 response는 다음의 형태로 받게 됩니다.
그런데 쓰고 보니 글을 너무 두서없이 쓴 것같습니다..
제 레포에서 코드만 쏙 야무지게 뽑아가셔요!!
https://github.com/angelSuho/gptTest
참고
https://platform.openai.com/docs/overview
'Spring' 카테고리의 다른 글
환경변수 @ConfigurationProperties로 간단히 관리하기 (with Kotlin) (1) | 2024.01.30 |
---|---|
스프링에서 실시간 에러 로그를 Discord로 받는 방법 (1) | 2023.12.10 |
스프링에서 실시간 에러 로그를 Slack으로 받는 방법 (0) | 2023.11.22 |
JPA의 정적쿼리와 동적쿼리의 차이점 (0) | 2023.06.06 |
@Validated, @Valid와 @Column으로 나뉘는 유효성 검증 (0) | 2023.03.22 |
댓글