본문 바로가기

Backend

[Springboot] 금융 웹서비스 개발 프로젝트 - ChatGPT API 활용해서 대화해보기

반응형

기술격차해소를 위한 금융 웹서비스 프로젝트

 

새로운 웹 프로젝트를 기획중에 있다. 종강하고 개발해봤던, 다기능 게시판의 경우 Springboot,JPA의 기능들을 다시금 되새겨보기 위해 머리 말랑이 목적으로 해봤던거지만,  

https://github.com/hyeon-gyu/NoticeBoard

 

GitHub - hyeon-gyu/NoticeBoard: 토이프로젝트( 다기능 게시판 )

토이프로젝트( 다기능 게시판 ). Contribute to hyeon-gyu/NoticeBoard development by creating an account on GitHub.

github.com

 

이번 프로젝트는 무게감을 두고 여럿이서 진행한다. 큰 아이디어는 금융지식 소개 서비스 및 AI모델을 활용한 예측시뮬레이션, 중장년층의 금융서비스에 대한 기술격차를 해소시킬 수 있는 방향으로 진행해보기로 회의를 마쳤고, REST API를 활용하여 여러 서비스를 제공할 계획이다. 

 

가장 먼저 진행할 서비스는 자유질의응답 게시판과 실시간으로 GPT와 대화할 수 있는 미니대화창을 구현하는 것으로 이야기를 나누었다. 게시판은 구현해본 경험이 있다보니, 본격적으로 개발하기에 앞서 CHAT GPT API를 활용해서 대화가 잘 되는지 테스트를 진행해보고자 한다. 일단 POSTMAN으로 body에 내용을 담아 서버단으로 요청하고 대화내용을 응답하는 과정을 머릿 속으로만 구상했다. 


일단 chat gpt에서 api key부터 생성했다. 

https://platform.openai.com/api-keys 

위의 링크를 통해 무료로 이용할 수 있는 제한된 api key를 발급받았다. 약 한 달 정도 이용할 수 있는 기한제 key 이다. 

 

다음으로 gpt api 서비스를 포함하고 있는 라이브러리를 추가해야한다.

https://github.com/TheoKanning/openai-java 

 

GitHub - TheoKanning/openai-java: OpenAI Api Client in Java

OpenAI Api Client in Java. Contribute to TheoKanning/openai-java development by creating an account on GitHub.

github.com

openai 사이트에서 java 버전 library는 위의 github를 참고하라고 안내되어있었다.

 

build.gradle에 라이브러리를 추가해준다. 버전은 가장 최신은 아니고 안정적인 버전을 택했다. (24.1.29 기준 ver 0.18.1) 

// openai thrid party library
implementation 'com.theokanning.openai-gpt3-java:service:0.14.0'

 

Configuration class를 하나 생성해두고 외부 클래스 OpenAiService 생성자를 빈에 등록시켰다. 

나머지 static final 변수들은 상수화시켰다. 

Temperature : 자유로운 답변의 정도와 관련된 변수로 1에 가까울 수록 다양하고 창의적인 답변을 낸다고 한다.

Top_p : Top Probability 단어샘플링 과정에서 누적확률의 정도를 나타낸다. 누적확률이 Top_p에 도달하기 위해 필요한 단어들을 고려하는 수치 정도로 알고 넘어가고자 한다.

Chat-Model : 현재 활용 가능한 언어 모델 중 이전의 대화를 기억할 수 있는 모델을 채택했다. 이전의 대화를 기억하지 못하는 단순 텍스트 모델도 존재한다.

@Configuration
@PropertySource("classpath:openai.yml")
public class ChatgptConfig {

    @Value("${api-key}")
    private String token;

    public static final String ROLE = "user";
    public static final Double TEMPERATURE = 0.6;
    public static final Double TOP_P = 1.0;
    public static final String CHAT_MODEL = "gpt-3.5-turbo";
    public static final Integer MAX_TOKEN = 1000;

    @Bean
    public OpenAiService openAiService(){
        return new OpenAiService(token,Duration.ofSeconds(60));
    }
   
}

 

최대한 사용자의 요청은 단순하게 입력받게 하고자 했고, 모델이나 설정 변수들은 불변객체로 선언했다.

 

postman으로 request 할 때 body에 질문만 담아서 보내는 것으로 구상했다.

한 가지 문제가 있다면, gpt가 답변을 전부 다 한 다음에야 response message가 도착한다. 42초나 소요되는 점이 문제이다. gpt api를 활용한 방식 중에는 streaming 방식도 존재한다. 평소 gpt에게 질문을 할 때, 실시간으로 단어 몇 개씩 덧붙여서 보이는 방식인 것이다. 하지만 스트리밍 방식은 http 통신만으론 가능하지 않다. 서버 단에서 request가 없어라도 계속해서 response를 보내야하기 때문에 웹소켓을 구현해야한다. 일단 스트리밍 방식은 필요할 때, 구현하고자 한다. 지금은 question 문자열에 추가로 "5줄 안으로 요약해줘" 를 덧붙여서 활용할 생각이다. 구현하고자 하는 서비스에 긴 답변을 요구할 정도로 깊은 도메인 지식을 설명하지 않을 예정이기 때문이다.

 

구현한 코드를 덧붙이려고 한다. 처음에는 library 없이 http 헤더에 직접 필요한 정보들을 기입해서  HttpEntity를 api url로 보낼 생각이었지만, 어째서 인지 finish_reason : stop과 함께 답변이 돌아오지 않았다. Time duration을 설정해주었지만 여전했고, 결국 library를 활용해서 코드를 다시 작성했다.


/**
 * 사용자로부터 request 받을 dto
 * */

@Data
@NoArgsConstructor
public class QuestionRequestDto implements Serializable {

    private String question;
}
/**
 * chat GPT에게 요청할 REQUEST DTO
 * */

@Data
@NoArgsConstructor
public class ChatgptRequestDto implements Serializable {

    private String model;
    private Integer maxTokens;
    private Double temperature;
    private Double top_p;
    private Boolean stream;
    private String messages;

    @Builder
    public static ChatCompletionRequest of(QuestionRequestDto requestDto){
        return ChatCompletionRequest.builder()
                .model(ChatgptConfig.CHAT_MODEL)
                .messages(convertChatMessage(requestDto))
                .maxTokens(ChatgptConfig.MAX_TOKEN)
                .temperature(ChatgptConfig.TEMPERATURE)
                .topP(ChatgptConfig.TOP_P)
                .build();
    }

    private static List<ChatMessage> convertChatMessage(QuestionRequestDto request) {
        return List.of(new ChatMessage(ChatgptConfig.ROLE, request.getQuestion()));
    }
}

 

// controller class

@RestController
@RequestMapping("/")
@RequiredArgsConstructor
public class ChatgptController {

    private final ChatgptService chatgptService;

    @PostMapping("/chat")
    public ResponseEntity<ChatgptResponseDto> question(@RequestBody QuestionRequestDto questionRequestDto){
        ChatgptResponseDto answer = chatgptService.question(questionRequestDto);
        return ResponseEntity.status(HttpStatus.OK).body(answer);
    }
}

 Service class 로직이 4줄만에 끝났다. HttpEntity를 만들고 필요한 헤더 정보를 다 넣고, 응답 메시지를 가공하면서 보낸 시간이 꽤 걸린 것 같았는데,, 이미 있는 라이브러리를 잘 활용하는 것도 능력이라고 자기위안을 해야겠다. 

@Service
@RequiredArgsConstructor
public class ChatgptService {


    private final OpenAiService openAiService;

    public ChatgptResponseDto question(QuestionRequestDto requestDto) {
        ChatCompletionResult chatCompletion = openAiService.createChatCompletion(ChatgptRequestDto.of(requestDto));

        return ChatgptResponseDto.of(chatCompletion);
    }
}

 

 

/**
 * gpt에게 답변 받을 dto
 * */

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ChatgptResponseDto {

    private String id;
    private String object;
    private Long created;
    private String model;
    private List<Message> messages;
    private Usage usage;

    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Message {

        private String role;
        private String message;

        public static Message of(ChatMessage chatMessage) {
            return new Message(
                    chatMessage.getRole(),
                    chatMessage.getContent()
            );
        }
    }

    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Usage {

        private Long promptTokens;
        private Long completionTokens;
        private Long totalTokens;

        public static Usage of(com.theokanning.openai.Usage usage) {
            return new Usage(
                    usage.getPromptTokens(),
                    usage.getCompletionTokens(),
                    usage.getTotalTokens()
            );
        }
    }

    public static List<ChatgptResponseDto.Message> toResponseListBy(List<ChatCompletionChoice> choices) {
        return choices.stream()
                .map(completionChoice -> Message.of(completionChoice.getMessage()))
                .collect(Collectors.toList());
    }

    public static ChatgptResponseDto of(ChatCompletionResult result) {
        return new ChatgptResponseDto(
                result.getId(),
                result.getObject(),
                result.getCreated(),
                result.getModel(),
                toResponseListBy(result.getChoices()),
                Usage.of(result.getUsage())
        );
    }
}

postman을 활용한 테스트 결과이다.

[request]

{
    "question":"피보나치 수열에 대해서 3문장으로 설명해줘 "
}
 
[response]
 
{
    "id": "chatcmpl-8mJP6M11OAGDawomJyxWyJW4cxBed",
    "object": "chat.completion",
    "created": 1706525024,
    "model": "gpt-3.5-turbo-0613",
    "messages": [
        {
            "role": "assistant",
            "message": "1. 피보나치 수열은 이전 두 항을 더하여 다음 항을 구하는 수열이다.\n2. 피보나치 수열은 0과 1로 시작하여, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...과 같이 계속해서 이어진다.\n3. 피보나치 수열은 자연계에서 다양한 현상을 모델링하는 데에도 사용되며, 컴퓨터 과학 등 다양한 분야에서도 활용된다."
        }
    ],
    "usage": {
        "promptTokens": 29,
        "completionTokens": 163,
        "totalTokens": 192
    }
}

 


여기 패키지에다가 앞으로 진행할 프로젝트의 살들을 덧붙여 나아갈 생각이다! 

코드는 깃허브에 꾸준히 업로드를 할 예정이다. 화이팅!

 

https://github.com/hyeon-gyu/Finance_Service


참고 블로그

https://joecp17.tistory.com/72

 

[Spring Boot + Chat GPT] Open AI API 적용기

서론 최근 Chat GPT가 굉장히 많은 플랫폼에서 상용화가 되고 있고 핫하다는 걸 체감을 많이 느꼈다. Chat GPT를 활용하여 현재 내가 하는 서비스 혹은 이후의 프로젝트들에서 이를 활용해 볼 수 있

joecp17.tistory.com

 

반응형