LLM 엔지니어링20장 · LLM 평가와 모니터링7 / 22← 이전목차다음 →MetaContents

20장. LLM 평가와 모니터링

LLM 을 채점자로

신메뉴 시식을 손님 대신 경험 많은 선배 요리사에게 맡길 수 있습니다. 빠르고 사람보다 값싸지만, 선배에게도 버릇이 있습니다. 양이 많은 접시를 좋아하거나, 자기가 만든 요리에 후하거나요. 그래서 처음 몇 번은 손님의 평가와 견주어 봅니다.

채점 기준표 주기, 핵심 사실을 먼저 적게 하기, 판정하기, 사람의 판정과 맞춰 보기의 네 단계 흐름도

1번에서 무엇이 맞음이고 틀림인지 기준표를 줍니다. 2번에서 참조 답과 챗봇의 답의 핵심 사실을 먼저 적게 합니다. 3번에서 그 둘을 견줘 판정합니다. 4번에서 사람이 판정한 사례로 일치율을 재고, 충분히 높을 때만 씁니다. 선배 요리사가 LLM 채점자, 손님의 평가가 사람의 판정입니다.

채점자의 버릇

버릇뜻줄이는 법
참조 없이 그럴듯함에 끌림지어낸 답도 자신 있게 쓰면 맞다고 한다참조 답이나 근거 자료를 함께 준다
길이 치우침길고 자세한 답을 더 좋게 본다"길이는 보지 않는다"를 기준표에 적고, 핵심 사실만 견주게 한다
위치 치우침두 답을 견줄 때 앞에 놓인 것을 고른다순서를 바꿔 두 번 묻는다
자기 선호자기와 같은 모델의 답에 후하다다른 모델로도 채점해 본다

채점 요청 만들기

판정을 JSON 스키마로 묶으면 "맞음", "틀림" 가운데 하나만 나옵니다(17장의 format). 판정 앞에 핵심 사실 칸을 두면, 모델이 먼저 사실을 꺼낸 뒤 판정합니다.

Python
import json

schema = {"type": "object",
          "properties": {"참조의_핵심": {"type": "string"}, "답의_핵심": {"type": "string"},
                         "판정": {"type": "string", "enum": ["맞음", "틀림"]}},
          "required": ["참조의_핵심", "답의_핵심", "판정"]}      # 칸의 차례대로 쓰게 된다
user = "질문: 대출 기간은 며칠이에요?\n참조 답: 대출 기간은 14일입니다.\n챗봇의 답: 대출 기간은 21일입니다."
# 결과: Ollama /api/chat 에 보낼 본문의 생김새(여기서는 보내지 않는다)
body = {"model": "qwen2.5:3b", "format": schema, "messages": [{"role": "user", "content": user}],
        "options": {"temperature": 0}}
print(json.dumps(body, ensure_ascii=False)[:160], "...")

코드에 나오는 함수

코드쉬운 뜻
"type": "object"JSON 스키마(schema, 짜임새 설계도)에서 답이 사전 꼴이어야 한다는 뜻이다
"properties": {...}사전에 들어갈 칸과 칸마다의 꼴이다. "string" 은 글자다
"enum": ["맞음", "틀림"]enumeration(하나하나 늘어놓기)의 줄임말이다. 적어 둔 값 가운데 하나만 쓸 수 있다
"required": [...]반드시 있어야 할 칸이다
"format": schemaOllama 에 "답을 이 스키마 꼴로만 내라"고 알리는 자리다
"options": {"temperature": 0}온도를 0 으로 두어 같은 요청에 되도록 같은 답이 나오게 한다
json.dumps(...)[:160]JSON 글로 바꾼 뒤 앞 160글자만 잘라 찍는다

이 코드가 뜻하는 것

선배 요리사에게 시식 평가지를 건넨다고 생각해 봅니다. 평가지 맨 위에는 "기준 맛의 핵심", 그 아래에 "이 접시의 핵심"을 적는 줄이 있고, 맨 아래에는 "맞음, 틀림" 체크 칸만 있습니다. 여기서 평가지 양식이 schema, 체크 칸이 enum, 평가지를 건네는 일이 body 를 보내는 일입니다. 비유와 다른 점이 하나 있습니다. 사람은 평가지 여백에 낙서를 할 수 있지만, format 으로 묶으면 모델이 이 꼴을 벗어난 글을 내지 않습니다.

줄하는 일쉽게 말하면
schema = {...}핵심 두 칸과 판정 칸을 정한다평가지 양식
user = "질문: ...\n참조 답: ...\n챗봇의 답: ..."채점할 재료를 한 글로 적는다. \n 은 줄바꿈이다평가지에 붙이는 시식 메모
body = {...}모델 이름, 꼴, 메시지, 온도를 묶는다평가지와 메모를 봉투에 담기
print(json.dumps(body, ...)[:160], "...")봉투 속을 앞부분만 보여 준다봉투를 살짝 열어 보기

출력은 보낼 본문의 앞부분입니다. 이 블록은 Ollama 에 보내지 않으므로 판정은 나오지 않습니다. 실제로 보내고 받는 일은 실습 3에서 합니다.

작게 줄여서 눈으로 보기

채점자가 이 꼴로 답을 돌려주면, 받은 글을 사전으로 바꿔 판정 칸만 꺼내 씁니다. 아래 답은 설명하려고 지어낸 글이고, 모델이 만든 것이 아닙니다.

Python
import json

# 채점자가 돌려줬다고 치는 답(설명하려고 지어낸 글)
reply = '{"참조의_핵심": "14일", "답의_핵심": "21일", "판정": "틀림"}'
result = json.loads(reply)              # 글을 사전으로 바꾼다
print(result["판정"], "·", list(result))
출력
틀림 · ['참조의_핵심', '답의_핵심', '판정']
결과읽는 법
틀림판정 칸의 값이다. enum 으로 묶으면 맞음, 틀림 둘 가운데 하나만 온다
['참조의_핵심', '답의_핵심', '판정']사전의 칸 이름이다. 지어낸 답에 적은 차례 그대로 담겼다

json.loads 는 JSON 글을 파이썬 사전으로 바꿉니다. 꼴이 정해져 있으니 result["판정"] 처럼 칸 이름으로 바로 꺼낼 수 있고, 긴 설명 글에서 판정을 따로 찾아 읽을 필요가 없습니다.

주의할 점

  • 작은 모델은 채점자로도 실수가 많습니다. 사람과 맞춰 본 일치율을 함께 적고, 중요한 판단은 사람이 표본을 다시 봅니다.
  • 채점에도 호출 비용과 시간이 듭니다. 규칙으로 걸러지는 것은 규칙으로 하고, 남은 것만 채점자에게 보냅니다.
← 이전 절20장 · 뜻으로 채점하기다음 절 →20장 · RAG 는 나눠서 잰다