Gemini로 똑똑한 CS 분류 봇 만들기 (feat. 자동 프롬프트 개선과 오버피팅의 함정)

Osol2·2025년 7월 16일
0

요즘 많은 팀이 반복적인 업무를 자동화하기 위해 LLM(거대 언어 모델) 도입을 고민하고 계실 텐데요. 저 역시 쏟아지는 고객의 '오류 및 불편 제보'를 효율적으로 분석하기 위해 Gemini를 활용한 자동 분류 시스템을 개발해 보았습니다.
결과는 성공적이었을까요? 반은 맞고 반은 틀립니다. 오늘은 똑똑한 봇을 만들려다 겪었던 현실적인 문제들과, LLM을 LLM으로 개선하려다 마주친 신기한 함정까지, 저의 생생한 개발 후기를 공유해 드리고자 합니다.

(모두 직접 작성한 후, AI의 리뷰를 받아 수정한 글입니다!)

개발 과정에서 발견한 몇몇 꿀팁

역할에 따라 프롬프트를 분리하세요.

초기 버전에서는 데이터 가져오기, 파싱, 분류에 이르는 모든 과정을 하나의 거대한 프롬프트에 담았습니다. 하지만 이는 LLM에게 너무 큰 부담이었습니다. 컨텍스트가 길어지자, 모델이 종종 지시의 일부를 잊어버리거나 단계를 건너뛰는 문제가 발생했습니다.

해결책은 '분업'이었습니다. 각 기능(데이터 가져오기, 파싱, 분류 등)을 별개의 프롬프트로 나누고, 각 프롬프트를 독립적인 컨텍스트에서 실행하도록 구조를 변경했습니다. 이렇게 하니 LLM은 한 번에 하나의 명확한 작업에만 집중할 수 있었고, 복잡한 지시를 잊어버리는 문제가 말끔히 해결되었습니다.

Gemini SDK의 '구조화된 출력' 기능

프롬프트를 여러 개로 쪼개면 필연적으로 한 단계의 결과물을 다음 단계로 넘겨야 합니다. 이때 데이터 형식이 어긋나면 전체 시스템이 멈추게 됩니다.

이 문제를 해결해 준 것이 바로 Gemini SDK의 ‘구조화된 출력’ 기능입니다. 이 기능을 사용하면 LLM의 응답을 우리가 정의한 특정 JSON 구조로 고정할 수 있습니다. 덕분에 여러 프롬프트와 스크립트 사이를 데이터가 오갈 때, 정확하게 정보를 전달할 수 있어 일정한 결과물을 내는 데 도움이 되었습니다.

모든 것을 LLM에게 맡길 필요는 없습니다.

LLM은 만능 도구가 아닙니다. 단순히 CSV에서 값을 뽑거나 데이터 개수를 세는 것처럼 추론이 필요 없는 작업까지 LLM에게 시키는 것은 소 잡는 칼로 닭을 잡는 격입니다. 이런 단순 반복 작업은 간단한 Python 스크립트로 처리하는 것이 속도와 비용 면에서 압도적으로 유리합니다. LLM은 ‘추론’과 ‘생성’이라는 가장 잘하는 일에만 집중시켜 주세요.

LLM에게 프롬프트 엔지니어링을 맡겨보세요.

좋은 프롬프트를 짜는 것은 어렵습니다. 이때도 LLM은 훌륭한 파트너가 될 수 있습니다. 저희는 직접 작성한 프롬프트를 LLM에게 보여주며 "더 효율적인 구조로 개선해 줘"라고 요청해 봤고, 결과는 기대 이상이었습니다. 사람이 쓴 어색한 문장을 다듬어주거나, <ThoughtProcess> 와 같은 태그를 활용해 기계가 더 이해하기 좋은 구조로 바꿔주는 등 프롬프트의 품질을 한 단계 높일 수 있었습니다.

분류의 모호함과 평가의 어려움

프로젝트를 진행하며 두 가지 큰 난관에 부딪혔습니다.

첫째는 '분류 기준'을 정하는 것이었습니다. 실제 데이터를 사용할 현업 담당자의 의견 없이는 명확한 카테고리를 설정하기 어려웠습니다.

둘째, 이 문제로 인해 LLM이 잘못 분류한 사례들을 살펴보면, 사람이 보기에도 분류하기 애매한 경우가 많았습니다. 이런 애매한 데이터는 계속해서 분류에 실패할 가능성이 높아, '분류 불가'와 같은 별도 카테고리의 필요성에 대해 고민하게 되었습니다.

또한, 미리 만들어진 정답 데이터셋이 없어 LLM의 분류 결과가 맞았는지 틀렸는지 정량적으로 평가하고 기록하기가 매우 번거로웠습니다.

그리고 프롬프트를 여러 번 수정하면서 어떤 변화가 결과에 긍정적인 영향을 미쳤는지 파악하기 어려웠고, 이를 자동으로 처리 및 기록해주는 장치가 있었으면 좋겠다고 생각했습니다.

해결책: '평가'와 '개선'을 자동화하는 프롬프트 시스템

이러한 평가의 어려움을 해결하기 위해, 저는 LLM을 활용한 자동화 시스템을 구축했습니다.

  1. 평가 프롬프트: 먼저 '데이터 품질 검수 전문가'라는 페르소나를 가진 '평가 프롬프트' 를 만들었습니다. 이 프롬프트는 분류가 끝난 결과물을 보고, 정의한 규칙에 따라 '오분류', '중복 분류 누락' 등의 문제를 찾아내 JSON 형태로 정리해 주며, 왜 잘못되었다고 판단했는지까지 이유까지 적어 줍니다.
  2. 개선 프롬프트: 다음으로 '프롬프트 엔지니어' 페르소나를 가진 '프롬프트 개선 프롬프트' 를 만들었습니다. 이 프롬프트는 '평가 프롬프트'가 찾아낸 오류가 일정 개수(n개) 이상일 때만 작동합니다. 발견된 문제점들을 바탕으로 기존의 '분류 프롬프트'를 자동으로 수정하도록 설계했습니다.

이 두 프롬프트를 통해 분류 → 평가 → 개선 → 분류 → ... 과정이 자동으로 반복되는 루프를 만들었고, 오류가 기준치 이하로 떨어지면 성공적으로 개선된 것으로 판단하고 멈추도록 했습니다.

자동 개선의 성과와 한계: '오버피팅' 문제

이 자동화 시스템은 실제로 의미 있는 개선을 이뤄냈습니다. 명확하지 않았던 규칙을 구체화해주거나, 분류에 도움이 될 만한 예시를 프롬프트에 스스로 추가하기도 했습니다.

하지만 반복 횟수가 늘어나자 예상치 못한 문제가 발생했습니다. 개선 프롬프트가 특정 규칙을 지나치게 강조하기 위해 "이 규칙은 매우 중요함", "반드시 준수해야 함"과 같은 문구를 남발하기 시작했습니다. 또한, 특정 실패 사례에만 과도하게 집중하면서 프롬프트가 점점 지엽적으로 변하는, 일종의 '오버피팅(overfitting)' 현상이 나타났습니다.

이러한 오버피팅의 근본적인 원인은 '오류가 n건 이상일 경우에만 개선'이라는, 프롬프트의 품질을 명확히 대변하지 못하는 지표를 사용했기 때문으로 보입니다. 앞서 언급했듯 데이터에는 사람이 판단하기에도 애매한 사례들이 포함되어 있었고, LLM이 이런 사례를 처리할 때마다 일정 확률로 실패할 수밖에 없습니다. 자동화 시스템은 이러한 실패를 '프롬프트 자체의 결함'으로 잘못 판단했고, 모호한 결과에 프롬프트를 억지로 끼워 맞추려 시도한 결과 지엽적인 규칙만 가득한 오버피팅으로 이어진 것입니다.

profile
프론트엔드 개발자

0개의 댓글