LLMOps 30

LLM-as-Judge: 재현 가능한 의미 기반 평가 설계

LLM 평가자는 그냥 좋은지 물어보면 된다고 생각하기 쉽다.

근거 · 교안 p111-p116

LLM-as-Judge: 재현 가능한 의미 기반 평가 설계 대표 이미지

1장: 단순한 ‘좋냐’는 질문의 한계

솔라는 노트북 화면을 들여다보며 미간을 찌푸렸다. 화면에는 두 개의 챗봇 응답이 나란히 떠 있었다. 같은 질문에 대한 두 가지 다른 버전의 답변이었다. 솔라는 이 둘 중 어느 것이 더 ‘나은’ 답변인지 가려내기 위해 LLM 평가자를 사용해보기로 했다. 하지만 결과는 만족스럽지 않았다.

“아, 정말 답답하네. 왜 자꾸 말이 바뀌는 거야.”

솔라는 의자 등받이에 몸을 기댔다. 첫 번째 답변, ‘A’는 간결하고 핵심만 담겨 있었다. 솔라가 ‘이 답변 어때?‘라고 묻자, LLM은 ‘효율적이고 직접적인 답변입니다. 좋습니다.‘라고 평가했다. 두 번째 답변, ‘B’는 조금 더 길었지만, 친절한 말투에 추가 정보까지 포함되어 있었다. 같은 질문에 LLM은 ‘사용자에게 친절하고 상세한 정보를 제공하는 훌륭한 답변입니다.‘라고 답했다.

문제는 그 다음이었다. 솔라가 ‘A와 B 중에 어느 것이 더 나아?‘라고 결정적인 질문을 던지자, LLM은 ‘두 답변 모두 장점이 있어 어느 한쪽이 절대적으로 낫다고 말하기 어렵습니다. 평가의 목적에 따라 달라질 수 있습니다.‘라는 애매한 답변만 돌려주었다. 몇 번을 다시 물어봐도 결과는 비슷했다. 어쩔 때는 A가 더 낫다고 했다가, 질문을 조금 바꾸면 B가 더 좋다고 하는 등 일관성도 없었다. 마치 변덕스러운 점쟁이와 씨름하는 기분이었다.

“그냥 뭐가 더 좋은지만 알려주면 되는데, 왜 이렇게 복잡하게 구는 거지?”

솔라의 혼잣말을 들은 루나가 조용히 다가와 노트북 화면을 들여다보았다. 화면에는 솔라가 입력한 프롬프트와 LLM의 오락가락하는 답변들이 남아 있었다.

“뭘 평가하고 있었어?”

“이 챗봇 답변 두 개. 하나는 짧고 간결한데, 다른 하나는 길어도 친절하거든. 그래서 LLM한테 뭐가 더 좋은지 물어봤지. 근데 그냥 좋다고만 하고, 뭐가 더 나은지는 결정을 못 해. 물어볼 때마다 대답도 달라지고.”

솔라가 투덜거리며 마우스를 움직여 LLM의 답변들을 가리켰다. 루나는 챗봇의 답변이나 LLM의 평가 결과 대신, 솔라가 처음 입력했던 질문을 가만히 들여다보았다. 거기에는 이렇게 쓰여 있었다.

이거 좋아?

“솔라,” 루나가 입을 열었다. “여기서 ‘좋다’는 게 정확히 무슨 뜻이야?”

“응? 좋다는 게… 그냥 좋다는 거지. 더 나은 답변이라는 뜻이잖아.”

“그럼 ‘더 나은 답변’의 기준은 뭔데? 답변이 빨리 나오는 것? 내용이 무조건 정확한 것? 아니면 말투가 친절한 것?”

루나의 질문에 솔라는 잠시 말문이 막혔다. “음… 다 중요하지. 빠르고, 정확하고, 또 친절하면 제일 좋고.” 막상 말하고 보니 스스로도 기준이 모호하다는 것을 깨달았다. 빠르고 정확한 답변과 친절하고 상세한 답변은 때로 양립하기 어려웠다.

루나는 솔라의 마음을 읽기라도 한 듯 말을 이었다. “만약 네가 식당 리뷰를 평가한다고 생각해 봐. ‘맛있다’는 평가 하나만으로는 부족하잖아. ‘재료가 신선해서 맛있다’와 ‘분위기가 좋아서 다시 오고 싶다’는 전혀 다른 차원의 이야기니까. LLM에게 그냥 ‘이거 좋아?‘라고 묻는 건, 처음 보는 사람에게 뜬금없이 ‘이 식당 어때?‘라고 묻는 것과 비슷해. 그 사람이 가성비를 중요하게 생각하는지, 분위기를 중요하게 생각하는지, 아니면 건강한 식단을 선호하는지에 따라 대답은 완전히 달라지겠지.”

루나의 비유를 듣자 솔라의 머릿속에 무언가 번쩍했다. 자신이 무엇을 놓치고 있었는지 분명해졌다. 솔라는 LLM을 무엇이든 아는 현자로 착각했다. 질문만 던지면 알아서 완벽한 기준을 세워 평가해 줄 것이라고 기대했던 것이다. 하지만 LLM은 그저 주어진 지시에 따르는 도구일 뿐이었다. 어떤 기준으로 평가해야 할지 알려주지 않으면, LLM은 그저 자신이 학습한 방대한 데이터 속에서 가장 그럴듯해 보이는 ‘좋다’의 의미를 임의로 가져와 사용할 뿐이었다. 그러니 결과가 매번 흔들릴 수밖에.

“아… 알겠다. 내가 평가자한테 어떤 역할을 해야 하는지, 어떤 규칙으로 점수를 매겨야 하는지 아무것도 알려주지 않고, 그냥 다짜고짜 판단하라고 요구한 거구나.”

솔라는 자신의 프롬프트를 다시 보았다. ‘이거 좋아?‘라는 다섯 글자가 전과 다르게 보였다. 그 질문은 명확한 요청이 아니라, 평가의 책임을 LLM에게 떠넘기는 막연한 기대에 가까웠다.

“맞아. 의미를 기반으로 하는 평가는 그냥 ‘좋다’, ‘나쁘다’로 끝날 수 없어. 평가자에게 명확한 역할을 부여하고, 구체적인 평가 기준을 제시해야만 비로소 재현 가능한 결과를 얻을 수 있어.”

루나의 말에 솔라는 고개를 끄덕였다. 막연했던 답답함의 정체가 드러나자, 오히려 해결할 수 있다는 자신감이 생겼다. 단순한 질문을 넘어서, 평가를 하나의 시스템으로 설계해야 한다는 것을 깨달은 것이다. 이제 막연하게 ‘좋냐’고 묻는 대신, 평가의 틀을 짜야 할 시간이었다.

“그럼 언니 말은, LLM 평가자한테 ‘너는 지금부터 이러이러한 기준을 가진 평가자야’라고 역할을 정해주고, 구체적인 평가 항목, 그러니까 루브릭 같은 걸 줘야 한다는 거지? 그래야 일관된 평가가 나온다는 거고.”

솔라는 확신에 찬 목소리로 말했다. 시야가 맑아진 기분이었다.

“그런데… 그런 평가자 역할이나 루브릭은 대체 어떻게 만들어야 하는 거야?“

2장: LLM 평가자에게 역할과 기준 부여하기

솔라의 노트북 화면은 이전과 사뭇 달랐다. ‘이거 좋아?’라는 단출한 질문 대신, 여러 줄의 지시사항이 빼곡히 적힌 텍스트 편집기가 열려 있었다. 지난번 루나와의 대화 이후, 평가자에게 명확한 지시를 내려야 한다는 깨달음을 얻은 솔라가 나름대로 고심해서 작성한 ‘평가 프롬프트’였다.

하지만 솔라의 표정은 여전히 개운치 않았다. 화면 속 프롬프트는 어딘가 모르게 뒤죽박죽이었다.

*   이 챗봇 답변은 고객 서비스 응대로서 좋은지 평가해 줘.
*   답변이 친절해야 해.
*   핵심 내용만 간결하게 전달하는지 확인해 줘.
*   1점에서 5점 사이로 점수를 매겨줘.
*   너무 길게 설명하지는 마.

솔라는 넷째 줄과 다섯째 줄의 순서를 바꿔보기도 하고, 첫째 줄의 문장을 좀 더 정중하게 다듬어보기도 했다. 하지만 어떻게 고쳐도 그저 ‘까다로운 요구사항 목록’처럼 보일 뿐, LLM이 일관된 기준으로 평가를 수행할 수 있을 거라는 확신이 들지 않았다. 좋은 재료들을 한데 섞기만 했을 뿐, 제대로 된 레시피가 없는 요리 같았다.

그때, 솔라의 어깨너머로 화면을 들여다보던 루나가 새 문서를 하나 열었다. 그리고는 아무 말 없이 네 개의 제목을 입력했다.

1. 페르소나 (Persona)
2. 루브릭 (Rubric)
3. 지시사항 (Instructions)
4. 주의사항 (Reminder)

솔라는 루나가 만든 텅 빈 구조를 물끄러미 바라보았다. 자신이 작성한 프롬프트 속에서 뒤엉켜 있던 생각의 가닥들이 네 개의 카테고리 안으로 빨려 들어가는 듯한 기분이 들었다.

“한번 네가 쓴 지시들을 이 네 가지 항목에 다시 분류해 볼래?” 루나가 제안했다.

솔라는 고개를 끄덕이고는 자신의 프롬프트를 보며 항목을 채워 넣기 시작했다.

첫 번째, ‘페르소나’. 솔라는 ‘고객 서비스 응대로서 좋은지 평가해 줘’라는 문장을 가져와 적었다. ‘너는 고객 서비스 품질을 평가하는 QA 담당자야.’라고 역할을 분명히 해주었다. 페르소나, 즉 평가자의 정체성을 정의하는 칸이었다. 이것만으로도 평가의 큰 방향성이 잡히는 느낌이었다.

다음은 ‘루브릭’. 평가 기준을 정의하는 곳이었다. 솔라는 ‘답변이 친절해야 해’와 ‘핵심 내용만 간결하게 전달하는지 확인해 줘’를 이곳으로 옮겼다. 하지만 그냥 옮겨 적는 것만으로는 부족했다. ‘좋다, 나쁘다’를 판단할 구체적인 잣대가 필요했다.

“언니, 그냥 ‘친절함’이라고 하면 지난번처럼 또 모호해지지 않을까? 점수를 매길 기준이 있어야 할 것 같아.”

“맞아. 루브릭은 채점표와 같아. 어떤 항목을, 어떤 기준으로, 어떻게 점수 매길지 명확하게 정의해야 해.”

솔라는 루나의 조언에 따라 루브릭을 훨씬 구체적으로 다듬었다.

2. 루브릭 (Rubric)
*   친절함 (Friendliness): 사용자를 존중하는 예의 바른 표현을 사용했는가? (1-5점)
*   간결성 (Conciseness): 불필요한 미사여구 없이 핵심 정보를 명확하게 전달했는가? (1-5점)
*   완결성 (Completeness): 사용자의 질문에 대한 모든 정보를 빠짐없이 포함했는가? (1-5점)

세 번째, ‘지시사항’. 평가의 절차를 설명하는 부분이었다. 솔라는 ‘1점에서 5점 사이로 점수를 매겨줘’를 이곳으로 옮기고, 평가 결과를 어떤 형식으로 받아야 할지 구체적인 절차를 덧붙였다. “각 루브릭 항목에 대해 점수와 그 이유를 한 문장으로 설명해 줘.”

마지막으로 ‘주의사항’ 칸이 남았다. 솔라는 잠시 고민하다가 ‘너무 길게 설명하지는 마’라는 자신의 메모를 쳐다봤다. 이것은 평가자가 하지 말아야 할 행동이었다. 솔라는 이렇게 적었다. “루브릭에 없는 내용은 평가하지 마. 너의 주관적인 의견을 추가하지 마.” 이것은 LLM이 정해진 규칙의 경계를 넘지 않도록 묶어두는 족쇄와 같았다.

모든 칸을 채우고 나자, 뒤죽박죽이던 지시 목록은 하나의 잘 짜인 ‘평가 시스템 메시지’로 탈바꿈했다.


[평가 시스템 메시지]

1. 페르소나 (Persona)

  • 당신은 고객 서비스 챗봇의 답변 품질을 평가하는 전문 QA(Quality Assurance) 분석가입니다.

2. 루브릭 (Rubric)

  • 친절함 (Friendliness): 사용자를 존중하는 예의 바른 표현을 사용했는가? (1-5점)
  • 간결성 (Conciseness): 불필요한 미사여구 없이 핵심 정보를 명확하게 전달했는가? (1-5점)
  • 완결성 (Completeness): 사용자의 질문에 대한 모든 정보를 빠짐없이 포함했는가? (1-5점)

3. 지시사항 (Instructions)

  • 주어진 챗봇 답변을 위의 루브릭에 따라 평가하세요.
  • 각 항목에 대해 1점에서 5점까지 점수를 매기고, 점수를 매긴 이유를 한 문장으로 간결하게 작성해주세요.

4. 주의사항 (Reminder)

  • 평가는 반드시 위에 제시된 루브릭 기준에만 근거해야 합니다.
  • 당신의 개인적인 의견이나 루브릭에 없는 새로운 기준을 추가하지 마세요.

솔라는 완성된 시스템 메시지를 LLM에 입력하고, 지난번과 동일한 두 개의 챗봇 답변(간결한 A안과 친절한 B안)을 평가해달라고 요청했다. 잠시 후 나타난 결과는 놀라웠다.

[평가 결과: A안]
*   친절함: 3점 (기본적인 존대표현을 사용했으나, 감성적인 표현은 부족함)
*   간결성: 5점 (질문의 핵심에 대해 정확하고 짧게 답변함)
*   완결성: 4점 (핵심 정보는 포함되었으나, 관련 부가 정보가 없음)

[평가 결과: B안]
*   친절함: 5점 (공감하는 표현과 함께 매우 정중한 어조를 사용함)
*   간결성: 2점 (인사말과 부연 설명이 길어 핵심 파악에 시간이 걸림)
*   완결성: 5점 (질문에 대한 정보와 함께 유용한 부가 정보까지 제공함)

지난번처럼 ‘둘 다 좋다’는 식의 애매한 답변이 아니었다. 명확한 기준에 따라 각 답변의 장단점이 점수와 함께 분석되어 나왔다. 이제 솔라는 두 답변을 놓고 무엇을 더 중시할지에 따라 합리적인 결정을 내릴 수 있었다. 만약 속도가 중요하다면 A안을, 고객 만족이 더 중요하다면 B안을 선택하면 되었다. 평가 결과가 드디어 ‘활용 가능한’ 데이터가 된 것이다.

“아! 이제 알겠어. 평가자의 역할(페르소나)을 정하는 것과, 행동 규칙(루브릭, 지시사항)을 주는 건 다른 차원의 문제였구나. 단순히 ‘QA 담당자가 되어줘’라고 말하는 건 신분증만 던져주는 거였어. 신분증을 가진 사람이 무슨 일을, 어떤 순서로, 무엇을 조심하며 해야 하는지 알려주는 상세한 업무 매뉴얼이 함께 있어야 했던 거야.”

솔라는 자신이 만든 평가 시스템 메시지 템플릿을 뿌듯하게 바라보았다. 이것만 있으면 이제 어떤 의미 기반 평가든 체계적으로 설계할 수 있을 것 같았다.

그런데 문득 한 가지 의문이 떠올랐다. 이 평가 시스템은 이제 잘 작동하지만, 이것을 매번 복사해서 붙여넣고, 결과를 수동으로 확인해야 할까? 그리고 평가 결과가 항상 지금처럼 정해진 형식대로만 나온다고 보장할 수 있을까?

“언니, 이 멋진 평가 로직을 만들긴 했는데… 이걸 내 프로그램에 심어서 자동으로 수백, 수천 개의 답변을 평가하게 하려면 어떻게 해야 해? 그리고 LLM이 실수로라도 점수 형식을 어기지 않게 확실히 못 박을 방법은 없을까?“

3장: 재현 가능한 LLM 평가 결과 만들기

솔라의 손가락이 키보드 위에서 멈췄다. 화면에는 지난번 루나와 함께 완성한 ‘평가 시스템 메시지’가 긴 문자열로 정의되어 있고, 그 아래에는 evaluate_with_llm이라는 텅 빈 함수가 놓여 있었다. 완벽한 평가 로직을 손에 넣었다는 생각에, 솔라는 자신만만하게 함수 내부에 코드를 한 줄 추가했다. # OpenAI API 호출이라는 주석과 함께. 그녀는 일단 실행 버튼을 눌러보면 뭔가 다음 단계가 보일 것이라 기대했다.

하지만 기대와 달리, 프로그램은 즉시 붉은색 오류 메시지를 쏟아내며 멈춰 섰다. AuthenticationError: No API key provided. 명백한 실패였다. 솔라는 잠시 멍하니 화면을 바라보았다. 그렇게 공들여 만든 평가 시스템 메시지는 제대로 전달조차 되지 못한 채 그녀의 컴퓨터 안에 갇혀버렸다. 마치 상세한 레시피는 손에 쥐었지만, 불을 지피는 방법도, 재료를 주문하는 방법도 모르는 요리사처럼, 기술적인 연결고리가 끊어져 있음을 직감했다.

솔라의 굳은 표정을 본 루나가 화면을 가리켰다. “네가 만든 이 평가 시스템 메시지는 LLM에게 보낼 ‘편지’의 내용이야. 하지만 편지를 보내려면 두 가지가 더 필요해. 하나는 편지를 배달해 줄 우체국을 이용할 수 있는 ‘창구 이용권’이고, 다른 하나는 답장을 받을 때 ‘정해진 양식으로만 회신해 주세요’라고 명시하는 ‘회신 양식’이지.”

루나는 웹브라우저를 열어 LLM 서비스를 제공하는 회사의 개발자 문서를 보여주었다. 화면에는 ‘API Key’라는 단어와 함께 복잡한 문자열 예시가 보였다.

“이게 바로 창구 이용권, 즉 ‘Provider Secret’이야. 네 프로그램이 LLM 제공사의 서버에 접속해서 ‘나는 비용을 지불하고 정당하게 서비스를 이용하는 사용자입니다’라고 신원을 증명하는 비밀 열쇠지.”

솔라는 고개를 끄덕였다. “아, 그럼 이 키가 없으면 내 프로그램은 LLM과 아예 대화를 시작조차 못하는 거네. 방금 내가 겪은 오류처럼. 회원 카드 없이는 도서관에 들어갈 수 없는 것과 같구나.”

“맞아. 그리고 더 중요한 건 두 번째, 회신 양식이야.” 루나는 화면을 스크롤하여 ‘Feedback Configuration’이라는 제목의 설정 화면 예시를 띄웠다. 그 안에는 평가 결과를 어떤 형식으로 받을지 선택하는 옵션들이 있었다.

[ Feedback Configuration ]

  • Response Format:

    • Boolean: (예 / 아니오)
    • Categorical: (A / B / C 중 선택)
    • Score: (1-10점 척도)
    • JSON Object: (사용자 정의 형식)
  • Strict Mode: [ ON / OFF ]

“우리가 만든 평가 시스템 메시지는 LLM에게 의미를 전달하지만, LLM은 때로 지시를 완벽하게 따르지 않을 수 있어. ‘평가 결과는 다음과 같습니다.’ 같은 불필요한 문장을 덧붙이거나, 점수만 달라고 했는데 줄글로 된 설명을 늘어놓기도 하지. 수백, 수천 개의 결과를 자동으로 처리하려면 이런 변수는 치명적이야.”

솔라는 지난번 ‘둘 다 좋다’는 애매한 답변을 받았을 때를 떠올렸다. 명확한 루브릭으로 의미의 모호함은 해결했지만, 이제는 출력 형식의 모호함이 새로운 문제로 다가왔다.

“그럼 이 설정은, LLM이 딴 길로 새지 못하게 막는 장치인 거구나. Response Format에서 내가 원하는 점수나 카테고리 형식을 지정하고, Strict Mode를 켜면 LLM은 무조건 그 형식에 맞춰서만 답을 해야 하는 거고.”

“바로 그거야.” 루나가 말했다. “예를 들어 ‘친절함’ 점수를 1점에서 5점 사이의 숫자로 받고 싶으면, Response FormatScore로 설정하고 범위를 지정하는 거지. 만약 여러 항목의 점수와 평가 이유를 한번에 받고 싶다면, 우리가 원하는 구조를 담은 JSON Object 형식을 정의하고 Strict Mode를 켜서 그 구조를 강제할 수 있어.”

솔라는 그제야 모든 조각이 맞춰지는 것을 느꼈다. 막연하게 ‘좋냐’고 묻던 첫 번째 단계에서, ‘페르소나’와 ‘루브릭’으로 평가의 의미를 구조화했던 두 번째 단계를 거쳐, 이제 ‘API 키’와 ‘출력 형식’이라는 기술적 장치로 그 평가를 자동화하고 재현 가능하게 만드는 마지막 단계에 도달한 것이다. 의미 기반 평가 역시, 명확한 설계도와 그것을 실행할 기술적 규격이 모두 있어야만 비로소 완성되는 하나의 시스템이었다.

솔라는 다시 자신의 텍스트 편집기로 돌아왔다. 그리고 evaluate_with_llm 함수 위에 새로운 주석 블록을 만들기 시작했다. 이것은 단순히 코드에 대한 메모가 아니었다. 재현 가능한 LLM 평가를 위한 완전한 설계도, ‘재현 가능한 평가 결과 구성기’의 청사진이었다.


[LLM-as-Judge 설계서: 고객 서비스 챗봇 평가]

1. 의미 설계 (Semantic Configuration): 시스템 메시지

  • Persona: 전문 QA 분석가
  • Rubric:
    • 친절함 (Friendliness): 1-5점
    • 간결성 (Conciseness): 1-5점
    • 완결성 (Completeness): 1-5점
  • Instructions: 각 항목에 점수와 근거를 작성할 것.
  • Reminder: 루브릭 외의 기준을 적용하지 말 것.

2. 기술 설계 (Technical Configuration)

  • Provider: OpenAI (gpt-4-turbo)
  • Provider Secret: 환경 변수(OPENAI_API_KEY)에서 로드
  • Output Format: JSON Object
    • { "friendliness": { "score": int, "reason": str }, "conciseness": { "score": int, "reason": str }, "completeness": { "score": int, "reason": str } }
  • Strict Mode: ON

설계서 작성을 마친 솔라는 만족스러운 미소를 지었다. 더 이상 텅 빈 함수 앞에서 막막해하던 솔라는 없었다. 이제는 명확한 페르소나, 기준, 절차, 출력 형식, 그리고 그것을 구동할 Provider Secret까지 갖춘, 재현 가능한 의미 기반 평가 시스템을 어떻게 구축해야 하는지 완벽하게 이해했다. 막연히 ‘좋은지’를 묻는 것을 넘어, 이제 솔라는 평가의 전 과정을 직접 설계하고 통제할 수 있는 힘을 갖게 된 것이다.