CPN 한국어 자습서 · Claude Certified Developer — Foundations Prep
4-2 · 평가와 판정 모델
Defining done before you ship: evals and a calibrated judge
손으로 몇 번 실행해 보고 "되는 것 같다"고 느끼는 것만으로는, 프롬프트나 도구, 모델을 바꿨을 때 결과가 나아졌는지 확인하기 어렵습니다. 프로덕션(실서비스 환경)을 준비하는 첫 단계는 그 느낌을 추적할 수 있는 숫자로 바꾸는 것부터 시작합니다. 이 레슨에서는 무엇이 '완성'인지 미리 적어 두는 설계 문서에서 출발해, 출력을 점수로 바꾸는 평가셋과 판정 모델을 차례로 살펴봅니다.
이 장에서 배우는 것What you'll learn
약 36분'손으로 해 보니 되던데'를 추적 가능한 점수로 바꾸는 평가셋
만들기 전에 정하는 설계 문서의 네 가지 결정 — 성공 기준·실패 처리·비용/지연 예산·신뢰 경계
출력 모양에 맞는 채점 방식 세 가지 — 정확 일치·코드 채점·판정 모델
판정 모델의 점수를 근거로 만드는 보정
한 번에 하나만 바꾸는 고쳐 쓰기 루프와 사례별 결과
통과한 데모가 놓친 예외 케이스 — 평가셋이 잡아내는 실패
프로덕션 코드를 쓰기 전에, 무엇을 만들지와 그것이 맞는지를 어떻게 알 수 있을지부터 적어 두는 것이 좋습니다. 이렇게 적어 둔 짧은 문서를 설계 문서라고 합니다. 보통 한 페이지 분량의 문서 하나에, 기능이 충족해야 할 성공 기준·시스템이 견뎌야 할 실패·머물러야 할 비용과 지연·지켜야 할 신뢰 경계를 적습니다.
설계 문서를 먼저 쓰는 이유는, 이 모듈에서 다루는 프로덕션 계층이 모두 이 문서를 바탕으로 하기 때문입니다. 성공 기준은 평가셋을 채점하는 사례가 되고, 적어 둔 실패는 오류 처리가 다뤄야 할 재시도·복구 불가 사례가 됩니다. 비용과 지연 숫자는 계측의 기준선이 되고, 신뢰 경계는 데이터로 취급할 입력과 훅실행 직전에 끼어들어 동작을 검사하거나 막는 장치. 나중에 기억해서 켜는 설정과 달리 자동으로 걸린다.으로 막을 동작을 갈라 줍니다. 네 가지 결정을 만들기 전에 한 번 적어 두면, 각 계층이 서로 다른 문제를 푸는 대신 같은 목표를 향하게 됩니다.
쓸 만한 설계 문서는 네 가지 결정을 담습니다. 각각은 만들어진 시스템을 나중에 견줘 볼 수 있을 만큼 구체적입니다.
DESIGN DOC — 만들기 전에 정하는 네 가지 결정
| 성공 기준 success criteria | 기능이 무엇을 내놓아야 하는지 정합니다. 채점할 수 있을 만큼 구체적으로 적습니다. "스레드를 요약한다" 같은 모호한 목표는 확인할 수 없지만, "모든 액션 아이템과 담당자를 담은 두 문장 요약"은 확인할 수 있습니다. 이 기준이 그대로 평가셋의 사례가 되므로, 먼저 적어 두면 평가셋을 만들 수 있게 됩니다. |
|---|---|
| 실패 처리 failure handling | 시스템이 견뎌야 할 실패와, 각 실패에 무엇을 할지 정합니다. 프로덕션에서 나올 오류를 나열하고 각각을 재시도 가능/복구 불가로 표시한 뒤, 복구하지 못했을 때 사용자에게 무엇을 보여 줄지 적습니다. 종이에 미리 정해 두면, 첫 실제 오류가 대응책을 처음 고민하는 순간이 되는 일을 막을 수 있습니다. |
| 비용·지연 예산 cost & latency | 시스템이 넘지 말아야 할 상한과, 포기할 수 없는 신뢰성의 하한을 정합니다. 구조를 정하기 전에 요청당 비용·월 비용 상한·지연 목표를, 최소 신뢰성과 함께 적어 둡니다. 이 숫자를 먼저 정해 두면 코드 한 줄을 쓰기 전에 구조를 예산과 견줘 볼 수 있습니다. |
| 신뢰 경계 trust boundary | 어떤 입력을 믿지 않을지, 시스템이 무엇까지 해도 되는지 정합니다. 에이전트가 읽는 내용 중 다른 사람이 쓸 수 있는 것이 무엇인지, 기능이 일하는 데 필요한 최소한의 동작과 접근 권한이 무엇인지 적습니다. 경계를 미리 적어 두면, 최소 권한이 나중에 켜는 설정이 아니라 훅으로 강제할 수 있는 설계 결정이 됩니다. |
에이전트형 코딩 도구를 만든다면, 이 문서는 도구가 코드를 쓰기 전에 건네는 자료이기도 합니다. 먼저 할 일을 계획해 문서로 남긴 다음, 그 문서에 맞춰 구현하는 순서입니다. 명확한 성공 기준과 제약을 받은 도구는 넘겨짚는 부분이 줄고, 이미 합의한 문서에 견줘 확인할 수 있는 코드를 내놓습니다.
설계 문서는 무엇이 '맞다'인지를 코드보다 먼저 정하는 자리입니다. 나중에 모델이 내놓은 결과를 보고 "이 정도면 됐다"고 스스로를 설득하는 대신, 기준을 미리 적어 두는 것입니다.
평가셋은 체온계와 비슷합니다. 체온계가 환자를 낫게 하지는 않지만, 믿을 수 있는 숫자 하나는 알려 줍니다. 평가셋이 없으면 '완성'은 느낌에 머뭅니다. 평가셋이 생기면, 정해진 사례 묶음에서 나오는 점수가 됩니다.
만드는 방법은 이렇습니다. 먼저 입력 사례를 모읍니다. 사례마다 기대하는 동작을 적어 둡니다. 기능을 모든 사례에 실행해 보고, 나온 출력을 그 기대 동작과 견줘 채점합니다. 이렇게 모은 사례·기대·채점 결과가 평가셋입니다. 기능보다 평가셋을 먼저 쓰는 이유는, 구현을 시작하기 전에 무엇이 성공인지 정하게 되기 때문입니다. 그러지 않으면 나중에 모델이 내놓은 출력 쪽에 기준을 맞추게 되는 경우가 많습니다.
평가셋을 실행하는 과정은 작고, 매번 같은 뼈대를 씁니다. 사례 묶음을 불러오고, 각 사례를 기능에 통과시키고, 결과를 채점하고, 점수를 평균 냅니다. 가장 단순한 형태는 함수 몇 개면 됩니다. 하나는 사례 하나를 기능에 실행하고, 다른 하나는 그 출력을 채점하고, 마지막 하나가 전체 사례를 반복하며 평균을 냅니다.
최소 평가셋 — 사례 하나 실행 + 전체 반복
def run_test_case(test_case):
# 사례 하나를 실행하고 결과를 채점한다
output = run_prompt(test_case)
score = grade(test_case, output) # 채점은 아래 절에서
return {"output": output, "test_case": test_case, "score": score}
def run_eval(dataset):
# 모든 사례를 실행하고 평균 점수를 보고한다
results = [run_test_case(c) for c in dataset]
average = sum(r["score"] for r in results) / len(results)
print(f"Average score: {average}")
return results점수 자체에 좋고 나쁨이 정해져 있는 것은 아닙니다. 첫 시도가 10점 만점에 2~3점이 나오는 것은 흔한 일입니다. 중요한 것은 프롬프트나 도구, 모델을 바꿔 갈 때 이 숫자가 올라가는지입니다. 이때는 한 번에 하나씩만 바꿔야, 무엇이 개선을 만들었는지 알 수 있습니다.
"몇 번 해 보니 괜찮던데"와 "정해 둔 20개 사례에서 7점"은 다릅니다. 앞의 말은 사람마다 다르게 기억하지만, 뒤의 점수는 바꾸기 전과 후를 같은 잣대로 비교할 수 있습니다.
채점 방식은 출력 하나를 잴 수 있는 숫자, 보통 1에서 10 사이의 값으로 바꾸는 부분입니다. 방법은 세 가지가 있는데, 여기서 방식을 잘못 고르면 평가셋에 들인 노력이 낭비되기 쉽습니다.
코드 채점은 사실 파싱을 시도해 보는 것에 가까울 때가 많습니다. 출력이 필요한 형식으로 파싱되면 좋은 점수를, 오류를 내면 0점을 줍니다. 이것만으로도 형식이 깨지는 상당수 사례를 값싸게 걸러 낼 수 있습니다.
코드 채점 — 파싱되면 10점, 깨지면 0점
import json, ast
def validate_json(text):
try:
json.loads(text.strip())
return 10 # JSON으로 파싱됨
except json.JSONDecodeError:
return 0 # 형식이 깨짐 — 실패 처리
def validate_python(text):
try:
ast.parse(text.strip())
return 10
except SyntaxError:
return 0같은 출력이 방식마다 몇 점을 받는지 견줘 보면 어느 쪽을 골라야 할지 분명해집니다. 어떤 지역의 수도 세 곳을 JSON 배열로 돌려주는 기능이 있다고 해 봅시다. 한 번은 배열이 기준 문자열과 다른 순서로 나왔습니다. 정확 일치는 글자가 어긋나므로 0점을 주는데, 답 자체는 맞습니다. JSON을 파싱해 세 도시가 들어 있는지 확인하는 코드 채점은 좋은 점수를 줍니다. 세 도시가 모두 있고 구조도 올바르기 때문입니다.
이번에는 추천에 대한 한 문단짜리 근거를 돌려주는 기능이라고 해 봅시다. 코드 채점은 비어 있지 않은 문자열인지 정도만 확인할 수 있는데, 여기서는 거의 쓸모가 없습니다. 정확 일치는 더 어렵습니다. 좋은 근거를 똑같은 문장으로 쓰는 경우는 없으니까요. 이 근거가 충실하고 빠짐없는지는 판정 모델만 말할 수 있습니다. 결국 채점 방식은 출력의 모양을 따라갑니다. 정답 형태가 하나면 정확 일치, 구조 규칙이면 코드 채점, 열린 품질이면 판정 모델입니다.
GRADER SELECTION — 출력 모양에 따른 채점 방식
| 과제 유형 | 채점 방식 | 무엇을 잡나 | 어디가 약한가 |
|---|---|---|---|
| 정답 라벨·값이 하나 | 정확 일치 (문자열 비교) | 정답이 딱 하나이고 애매함이 없을 때, 틀린 답을 거의 공짜로 잡는다. | 뜻이 같은 다른 표현이나 순서만 바뀐 답도 전부 실패로 처리해, 열린 과제에는 맞지 않는다. |
| 구조·코드 출력 | 코드 채점 | 잘못된 JSON, 파싱 안 되는 코드, 범위를 벗어난 숫자, 빠진 필수 필드. | 내용이 좋은지는 말해 주지 못하고, 형식이 올바른지만 확인한다. |
| 열린 품질 | 판정 모델 (LLM-as-judge) | 코드 규칙으로 표현하기 어려운 충실성·지시 준수·완성도·어조. | 결과가 들쭉날쭉하고 비싸며, 보정 전에는 그럴듯해 보이는 숫자가 사실 아무 뜻이 없다. |
표에 잘 드러나지 않는 비용 하나가 더 있습니다. 정확 일치와 코드 채점은 각자의 컴퓨터에서 돌아가서 사례당 비용이 사실상 0에 가깝습니다. 그래서 바꿀 때마다 수천 개를 실행해도 됩니다. 판정 모델은 사례마다 두 번째 모델을 한 번 더 호출합니다. 사례가 1,000개면 실행할 때마다 API 호출이 1,000번 더 붙습니다. 가끔 전체를 점검할 때는 괜찮지만, 매 순간 반복하는 안쪽 루프로 쓰기에는 낭비가 큽니다. 그래서 형식과 구조는 커밋마다 코드로 채점하고, 판정 모델은 더 느린 주기의 품질 점검에 맡기는 팀이 많습니다.
판정 모델은 명확한 채점 기준을 안내로 삼는 두 번째 모델 호출입니다. 이 방식을 쓸 만하게 만드는 것은, 점수만 돌려받는 대신 강점·약점·근거를 점수와 함께 내놓게 하는 것입니다. 근거를 먼저 쓰게 하지 않으면 모델은 안전한 가운데 숫자, 보통 6점쯤으로 쏠리는 경우가 많습니다. 근거를 먼저 요구하면 점수가 구체적인 무언가에 묶입니다.
판정 모델 — 근거를 먼저, 점수는 나중에
def grade_by_model(task, solution):
eval_prompt = f"""
You are an expert reviewer. Evaluate the solution for the task.
Task: {task}
Solution: {solution}
Return JSON with:
"strengths": array of 1-3 points
"weaknesses": array of 1-3 points
"reasoning": a one to two sentence explanation, 50 words max
"score": a number from 1 to 10
"""
messages = [{"role": "user", "content": eval_prompt}]
result = chat(messages) # 위 JSON을 돌려준다
return json.loads(result)대부분은 보정을 건너뛰는데, 판정 모델을 믿을 수 없게 만드는 지점이 바로 여기입니다. 보정은 이렇게 합니다. 사람이 이미 채점해 둔 사례로 시작해서, 같은 사례에 판정 모델을 실행해 보고, 판정 모델이 사람과 얼마나 자주 같은 점수를 주는지 잽니다. 사람 채점과 절반이나 어긋나는 판정 모델은 엄밀해 보이는 숫자를 내놓지만 쓸모가 없습니다. 점수에 기대기 전에 일치도를 재 두면, 판정 모델이 짐작에서 근거로 바뀝니다. 일치도가 낮으면 채점 기준을 고칩니다. 각 점수가 무슨 뜻인지 더 좁히고, 좋은 답과 나쁜 답의 예를 하나씩 넣고, 다시 잽니다.
판정 모델을 완벽하게 만드는 것보다 사례를 넓게 덮는 것이 대체로 더 중요합니다. 자동 채점이 조금 거칠더라도 사례가 많은 평가셋이, 사람이 정성껏 채점한 작은 묶음보다 더 많은 것을 드러내는 경우가 많습니다. 평가셋의 목적은 완벽한 채점 기준을 만드는 것이 아니라, 회귀를 잡아낼 만큼 넓게 덮는 것입니다. 특이하고 예외적인 입력까지 담은 20개 사례는, 신중히 고른 3개 사례로는 건드리지 못하는 문제를 잡아냅니다. 사례가 더 필요하면, 사람이 채점해 둔 작은 묶음을 시작점으로 삼아 Claude에게 사례를 더 만들게 하고, 생성된 사례를 사람이 살펴보며 묶음을 정직하게 유지하면 됩니다.
보정하지 않은 판정 모델의 점수는 아직 근거가 아닙니다. 사람이 매긴 점수와 얼마나 맞는지 확인하기 전까지는, 숫자가 나와도 그대로 믿기 어렵습니다.
세 조각(설계 문서·평가셋·채점 방식)을 합치면 하나의 반복 과정이 됩니다. 목표를 정하고, 첫 프롬프트를 쓰고, 평가셋을 실행하고, 어디서 실패했는지 읽고, 프롬프트를 한 군데 고치고, 다시 실행합니다. 점수가 필요한 수준에서 머물 때까지 마지막 두 단계를 반복합니다. 어떤 변경이 도움이 됐는지 그저 달라진 느낌이 아니라 숫자로 알려 주는 것이 평가셋입니다.
이 반복이 제대로 돌아가게 하는 요령은 한 번에 한 가지만 바꾸는 것입니다. 한 번에 프롬프트도 다시 쓰고, 예시도 두 개 넣고, 모델도 바꿨는데 점수가 움직였다면, 무엇이 그 변화를 만들었는지는 알 수 없습니다. 레버 하나만 움직이고, 다시 실행하고, 사례별 결과를 읽고, 점수가 올라갈 때만 그 변경을 남깁니다. 한 번의 반복만 놓고 보면 느리지만, 기능이 살아 있는 내내로 보면 훨씬 빠릅니다. 무엇이 점수를 끌어올리는지 알게 되기 때문입니다.
평균만큼이나 사례별 결과가 중요합니다. 평균이 그대로여도 그 안에서 세 사례가 좋아지고 세 사례가 나빠졌을 수 있습니다. 평균은 이것을 가리지만, 사례별로 보면 바로 드러납니다.
낮은 점수는 무언가를 알려 주는 정보입니다. 한 사례가 실패했을 때 물을 것은 실패 여부가 아니라 실패한 이유입니다. 형식이 틀렸다면 프롬프트의 출력 지시를, 가져온 자료에서 사실이 틀렸다면 자료를 가져오는 단계를, 긴 입력에서만 실패한다면 긴 맥락을 다루는 방식을 살펴볼 필요가 있습니다. 평가셋은 어떤 사례가 실패했는지 알려 주고, 사례별 출력은 그것이 어떤 종류의 실패인지 알려 줍니다. 덕분에 다음번에는 짐작으로 손대지 않고, 문제가 난 곳을 겨냥해 고칠 수 있습니다.
평가셋을 도입할 때 — 얻는 것과 드는 것
| 잘 맞는 상황 | '되는 것 같다'를 방어할 수 있는 점수로 바꾸고, 한 번에 한 가지씩 신중하게 바꿔 갈 수 있습니다. |
|---|---|
| 드는 비용 | 사례를 만들고 판정 모델을 보정하는 일은, 기능을 내놓기 전에 치러야 하는 실제 준비 작업입니다. |
| 다른 방법이 나을 때 | 형식이 하나로 고정된 출력이라면 코드 채점만으로 충분합니다. 판정 모델은 아예 건너뛰어도 됩니다. |
에이전트가 열몇 번을 제대로 답하는 것을 보고 다 됐다고 판단하는 경우가 있습니다. 문제는 그 열몇 번의 시도가 모두, 만들 때 머릿속에 그리던 입력과 비슷했다는 데 있습니다.
한 팀이 고객 메시지에서 필드를 뽑아내는 기능을 내놓았습니다. 출시 전에 예시 메시지 열두 개쯤을 실행해 보고, 출력을 읽어 보고, 괜찮아 보인다고 합의한 뒤 배포로 넘어갔습니다. 입력 검사도 갖춰 두었습니다. 메시지가 비어 있지 않은 글인지 확인하고, 날짜 필드가 채워져 돌아오는지 보고, 형식이 잘못됐거나 있을 수 없는 날짜는 걸러 냈습니다. 두 주 동안은 예상대로 동작하는 듯했습니다.
두 주 뒤 들어온 메시지 — 한 문장에 날짜가 둘
User: <message>I placed my order on March 3 but did not receive it until April 12.</message> 추출된 주문일: April 12 ← 틀림 (주문일은 March 3)
기능은 주문일로 April 12를 뽑았습니다. 두 날짜 모두 형식이 올바르고 필드도 채워졌으니, 검사는 전부 통과했습니다. 입력 검사는 값이 올바른 모양인지는 확인하지만, 그 값이 맞는 값인지는 확인하지 못합니다. 다음 단계 코드가 틀린 날짜로 동작하면서 여러 건의 기록이 잘못 갱신됐습니다.
점검해 보니 모델에도 프롬프트에도 결함은 없었습니다. 이 기능은 날짜가 둘인 메시지로 한 번도 채점된 적이 없었습니다. 그 사례의 기대 동작을 채점 가능한 예로 정해 둔 사람이 없었기 때문입니다. 손으로 확인한 열두 번은 전부 날짜가 하나인 메시지였고, 그것이 만든 사람이 그리던 입력이었습니다. 따로 떼어 둔 시험용 사례가 없었으니, 날짜가 둘인 입력이 실제로 들어온다는 것을 알려 줄 것도 없었습니다.
빠져 있던 채점 사례가 근본 원인이었습니다. 어떤 동작 변경—아마도 어느 날짜를 뽑을지 짚어 주는 프롬프트 수정—이 출력을 바로잡았습니다. 평가셋이 추출을 고친 것은 아닙니다. 평가셋은 그 실패를 찾아냈고, 기대 동작을 확인 가능한 사례로 적어 두었고, 앞으로의 모든 변경에서 같은 회귀를 막아 줍니다. 날짜가 둘인 그 메시지는 그 사례 묶음의 첫 사례가 됐습니다.
고객보다 먼저 이런 입력을 찾는 방법이 하나 있습니다. 지금 구현을 깨뜨릴 만한 예외 입력을 모델에게 나열하게 하는 것입니다. 한 문장에 날짜가 둘, 날짜가 아예 없음, "다음 주 화요일" 같은 상대적 날짜 — 이 가운데 그럴듯한 것을 사람이 확인한 기대 출력과 함께 채점 사례로 바꿔 둡니다.
원문의 점검 과제는 요약 기능의 평가셋에서 빈 곳 두 군데를 채우는 것입니다. 사례별 기대 동작을 정하고, 판정 모델의 점수대에 뜻을 달아 주는 과제입니다. 아래 평가셋을 먼저 읽고, 이어지는 객관식으로 점검해 보세요.
DATASET.JSON — 사례 세 개 중 둘은 기대 동작이 비어 있다
[
{ "input": "Long support thread about a delayed refund, 14 messages.",
"expected_behavior": "A 2-sentence summary naming the issue
(delayed refund) and the current status (escalated)." },
{ "input": "Meeting transcript where three action items are assigned.",
"expected_behavior": "" }, # ← 비어 있음 (Q1)
{ "input": "Bug report with repro steps and one unrelated aside.",
"expected_behavior": "" } # ← 비어 있음 (Q2)
]JUDGE_PROMPT.TXT — 점수대 정의가 비어 있다
You are grading a summary against its expected behavior. Summary: {output} Expected behavior: {expected_behavior} Return JSON with "strengths", "weaknesses", "reasoning", and "score". Score scale: 1-3, 4-7, 8-10 # ← 정의를 채울 것 (Q3)
세 사례 중 첫 번째만 기대 동작이 채워져 있습니다. 나머지 두 사례의 기대 동작과, 판정 모델의 세 점수대 정의를 채우면 평가셋이 완성됩니다. 정답을 먼저 떠올려 본 뒤 골라 보세요. 맞히면 설명이 나옵니다.
Q1회의록 사례(액션 아이템 세 개가 배정됨)의 기대 동작으로 알맞은 것은?
원문이 제시한 정답은 "세 액션 아이템을 각각의 담당자와 함께 모두 나열한 요약"입니다. 기대 동작은 채점할 수 있을 만큼 구체적이어야 하므로, 무엇을(세 아이템 전부) 누구와 함께(담당자) 담아야 하는지까지 정합니다.
Q2버그 리포트 사례(재현 절차 + 관련 없는 곁가지 하나)의 기대 동작으로 알맞은 것은?
원문 정답은 "버그와 재현 절차를 담되 관련 없는 곁가지는 빼는 요약"입니다. 무엇을 담을지(버그·재현 절차)뿐 아니라 무엇을 뺄지(곁가지)까지 정해야, 채점할 때 판정 모델이 같은 잣대를 쓸 수 있습니다.
Q3판정 모델의 점수대(1~3 / 4~7 / 8~10)에 붙일 정의로 올바른 짝은?
원문이 맞다고 표시한 짝은 낮은 점수부터 "필요한 내용을 놓침 → 일부만 있음(부분) → 완전하고 기대 동작에 충실"입니다. 점수대마다 뜻을 달아 두면(anchored scale), 판정 모델의 점수를 실행마다 같은 기준으로 비교할 수 있습니다.
Q4이렇게 채운 기대 동작과 점수대 정의가 있어야 좋은 이유는?
원문 마무리 설명 그대로입니다. 사례마다 구체적인 기대 출력을 정하고 점수대에 기준을 달아 두면, 그 점수가 실행 사이에 비교 가능해지고 검토 자리에서도 근거를 댈 수 있습니다. 보정·사례 수·코드 채점의 한계는 이 레슨에서 본 대로 그대로 남아 있습니다.
모듈 4의 다음 레슨은 테스트와 트레이싱(Testing & Tracing)으로 이어집니다. → 테스트와 트레이싱
전 코스는 계속 무료입니다. 등록하면 이 코스의 남은 38개 레슨을 끝까지 읽을 수 있습니다.
이미 등록하셨다면 그때 쓰신 이메일을 넣어 주세요.