티스토리 뷰

 

 

6월 16일에 OpenAI가 업데이트 되었다.

 

function call 기능과 함께 토큰 수가 증가 되었다.

 

무려 4k에서 16k로 4배나 증가 되었다.

 

GPT-4도 GPT-3.5와 마찬가지로 16k 업데이트 되었다.

 

토큰 수가 업데이트되면서, 이전 대화를 기억하게 하는 기능을 적극적으로 활용할 수 있게 되었다.

(기존 4천개로는 너무 적었음...)

 

구현 방식은 여러가지가 있을 것 같다.

 

가볍게 떠오르는건 두 가지정도인데,

 

1. FE는 질문만 전달, BE가 이전 질문과 답변을 저장하고 있다가 답변 생성

 

2. FE가 어차피 화면에 그려줘야하니까, 질문과 답변을 모두 보내주기

 

상용화될 앱이라면 1번이 맞다고 생각되어 1번으로 구현해봤다.

 

시작!

 

chat API 연동

먼저 fegin client로 chat API를 연동했다.

 

Configuration

public class OpenAIHeaderConfiguration {
    @Value("${spring.openAI.APIKey}")
    String openAIAPIKey;

    @Bean
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            requestTemplate.header("Authorization", openAIAPIKey);
            requestTemplate.header("USER-AGENT", "Mozilla/5.0");
            requestTemplate.header("Content-Type", "application/json");
        };
    }
}

Client

@FeignClient(name = "OpenAIClient", url = "https://api.openai.com/v1", configuration = {OpenAIHeaderConfiguration.class})
public interface OpenAIFeignClient {

    @RequestMapping(method = RequestMethod.POST, value = "/chat/completions")
    ChatResponseDto chatCompletion(@RequestBody ChatRequestDto chatRequestDto);

}

Impl

public ChatResponseDto getChatCompletion(List<ChatMessage> messages) {
	ChatResponseDto chatResponseDto = openAIFeignClient.chatCompletion(
			ChatRequestDto.builder().model(CHAT_GPT_MODEL).messages(messages).maxTokens(maxTokens).temperature(temperature).build());
	return chatResponseDto;
}

 

 

API 구현

요청 객체

@Data
public class RequestMessageDto {
    private String cacheId;
    @NotNull @NotEmpty
    private String message;
}

API

@PostMapping("/question/memorize")
public ResponseEntity<Object> saveMessageSingle(@Valid @RequestBody RequestMessageDto requestMessageDto) {

	List<ChatMessage> chatList = new ArrayList<>();
	RequestMemorizeMessageDto requestMemorizeMessageDto = new RequestMemorizeMessageDto();
	if(ObjectUtils.isEmpty(requestMessageDto.getCacheId())) {
		chatList.add(new ChatMessage("system", "당신은 중국집 요리사입니다."));
	} else {
		requestMemorizeMessageDto = cacheService.getMemorizeMessageCache(requestMessageDto.getCacheId());
		chatList = requestMemorizeMessageDto.getChatList();
        cacheService.clearCache(requestMemorizeMessageDto.getCacheId());
	}

	chatList.add(new ChatMessage("user", requestMessageDto.getMessage()));

	ChatResponseDto chatResponseDto = pilotService.getGptAnswer(chatList);

	chatList.add(new ChatMessage(chatResponseDto.getChoices().get(0).getMessage().getRole(), chatResponseDto.getChoices().get(0).getMessage().getContent()));
	requestMemorizeMessageDto.setChatList(chatList);

	String id = cacheService.saveMemorizeMessageCache(requestMemorizeMessageDto);
	chatResponseDto.setCacheId(id);
    
    	// Usage에 totalTokens를 보고 16k가 넘어갔을 때 처리

	return new ResponseEntity<>(chatResponseDto, HttpStatus.OK);
}

요청 메시지는 POST 전달 받았다. 질문이 엄청나게 길 수도 있고, 특수 문자가 포함될 수 있기 때문이다.

 

그리고 만약 이전에 답변이 나간 이력이 있다면, cache에서 데이터를 꺼내와야하므로 cacheId도 받는다.

 

캐시는 이전 게시글 참고

 

API 를 정리해보면,

 

1. 첫 요청은 cacheId가 없기 때문에, 당신은 중국집 요리사라는 프롬프트를 작성해줬다.

2. 요청 객체에서 질문을 받아와서, "user"에 값을 채워 넣어준다.

3. openAI에 요청 getGptAnswer는 내부에는 openAI에게 Chat API를 요청한다.

4. 응답을 받고, chatList에 role과 content를 저장하고 캐시를 갱신한다.

5. 응답

 

 

잘되나 확인



 

위 대화를 보면 서로 대화가 지속되고 있다는 것을 확인할 수 있다.

 

또한 total_tokens가 계속 증가하기 때문에 토큰 갯수 제한도 핸들링할 수 있다.

 

다만, 위 구현에서 마지막 대화의 캐시 삭제가 빠져있는데, 캐시 삭제는 FE에서 대화가 끝났다는 신호를 던져주면 된다.

(나라면 별도의 API를 구현할 것 같다.)

 

혹은 토큰 수가 16k를 오버했을 때 리스트를 통째로 삭제하는 등의 전략을 세울 수 있다.

 

마치며

이번 포스팅은 openAI의 새로운 기술 소개보다는 앞서 포스팅한 걸 조합해 구현하는 방식을 공유해봤다.

 

다음은 openAI의 진짜 새로운 기술인 function call을 사용해 볼 것 같음

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함