욕설 필터링 기능 구현하기 - 2

상추·2024년 10월 1일
1
post-thumbnail

본 글은 주변 쓰레기통을 찾아주는 서비스 binder를 개발하며 발생한 이슈를 서술하였습니다.

내 주변 쓰레기통 찾으러 가기



GPT의 잦은 오류

댓글 필터링 기능을 구현하고 검증을 위해 몇번의 테스트를 하던 중 아래와 같은 문제가 발생하였다. "댓글" 이라는 내용으로 댓글을 작성했는데, GPT가 댓글에 욕설이 포함되어 있다고 판단해버리는 오류가 발생한 것이다. 저렴한 GPT-4o-mini에서만 해당 오류가 발생하는줄 알았는데 가장 비싼 GPT-4o 에서도 같은 문제가 발생하니... 참 환장할 노릇이었다.

GPT에 대한 것은 GPT가 잘 알 것이라 생각하여 도대체 왜 이러는건지 한번 물어봤다.

그렇다고 한다.

GPT를 한번 믿어보기로 했다. 그리고 제안해준 프롬프트를 코드에 적용해보았다.

private String getContent(String target) {
        return String.format(
                "다음 문장에서 비속어가 포함되어 있는지 여부(isCurse)와, 비속어로 판단된 단어 목록(words)을 원본 그대로 포함하여 백틱이 없는 순수한 JSON 형식으로 응답해 주세요. \"%s\"",
                target);
    }

오 단순히 프롬프트만 변경했는데 여러번의 시도에서 테스트를 성공하는 것을 확인할 수 있었다. 검증이 필요한 문장을 프롬프트의 앞이 아니라 뒤에 배치하는 것이 핵심이었던 것 같다.

하지만 이것만으로는 부족했다. GPT는 언제든지 오작동할 수 있다. 만약 정상적으로 글을 작성했는데 비속어가 포함되어 있다고 글 작성이 거부당하면 사용자는 상당한 불쾌감을 느낄 것이고, 더 나아가 이탈까지 이어질 수 있다.

후처리

위 상황을 해결하고자 GPT로부터 받은 응답을 가지고 후처리를 진행해주기로 했다.

아이디어는 이렇다.

1. isCurse가 false이면 그대로 반환한다. 
2. 만약 true일 경우에는 words 목록과 사용자가 입력한 본문을 대조한다.
3. 만약 사용자가 입력한 본문에 words가 포함되어 있지 않다면,
  3-1. words 목록을 지우고 isCurse를 false로 변경한 후 반환한다.
  3-2. 혹은 GPT에 욕설 검증을 재요청한다. 

몇 번의 테스트에서 발생한 GPT의 오류 대부분은 본문에 욕설이 존재하지 않는데 욕설로 판단하는 경우였다. 더불어, GPT에 다시 검증을 요청하는건 성능, 비용적으로도 문제가 많을 것이라 판단했다. 따라서 3-1의 방법을 채택하기로 하였다.

GPT 판단 결과 욕설이 없을 경우에는 결과를 그대로 반환한다. 아니라면 위의 아이디어대로 로직을 진행한다. target(본문)이 AI가 찾아낸 욕설을 포함하고 있을 경우 기존 응답을 반환하고, 포함하지 않을 경우에는 GPT가 오류를 발생시킨 것이므로 결과를 수정해서 반환하였다. 그리고 GPT 오류가 발생하였을때 로그를 찍어보았다.

public CurseCheckResult checkCurse(String target) throws JsonProcessingException {

        // 기존 코드 
        
        CurseCheckResult curseCheckResult = getCurseCheckResult(body); // GPT의 검증 결과

		// GPT 판단 결과 욕설이 없을 경우
        if (!curseCheckResult.getIsCurse()) {
            return curseCheckResult;
        }

        List<String> words = curseCheckResult.getWords(); // GPT가 감지한 욕설 목록

        if (isMatched(target, words)) { // target(본문)이 AI가 찾아낸 욕설을 포함하고 있는 경우
            return curseCheckResult;
        }
        // 포함하지 않을 경우
        log.error("GPT 오류 target = {}, words = {}", target, String.join(",", words));
        return new CurseCheckResult(false, List.of());
    }


private boolean isMatched(String target, List<String> words) {

        for (String word : words) {
            if (target.contains(word)) { // 본문에 욕설 포함할 경우
                return true;
            }
        }
        return false;
}

그리고 테스트를 수행해보자. 원활한 테스트 진행을 위해 GPT에 전달하는 프롬프트는 기존에 오류를 발생시키던 프롬프트로 잠시 변경하였다.
(주의: 욕설이 포함되어 있습니다.)

원하던대로 GPT가 오류를 발생시켰을 때 false를 반환한다! 후처리가 성공적으로 진행되었음을 알 수 있다.

갈길이 멀다

테스트 시간을 눈여겨 보신 분이라면 벌써 눈치 채셨겠지만, 고작 20번의 요청을 처리하는데 걸리는 시간이 17초이다. 이 문제를 반드시 해결해야 한다!

profile
백엔드 개발자 상추입니다.

0개의 댓글