스킬 평가의 중요성
title: 스킬을 만들기 전에 "안 만든 상태"를 먼저 재봤다 — 터질 거라던 데는 만점이었다
date: 2026-08-10
summary: 스킬을 쓰기 전에 스킬 없이 9번 돌려서 기준선을 쟀다. 제일 위험하다고 적어둔 곳은 18/18 만점이었고, 쳐다도 안 본 곳이 3/12로 무너졌다. 그것도 결과만 보면 멀쩡해 보이는 방식으로.
종류: 사례
주차: 2
프로젝트: skill-teaching-kit
공개: true
tags: ["스킬", "평가", "베이스라인", "업무자동화", "AI검 증"]
---
## 한 줄 요약
AI한테 일을 시키기 전에, AI가 그 일을 지금 얼마나 하는지부터 재봤다. 그랬더니 내가 걱정하던 곳은 이미 잘하고 있었고, 걱정도 안 한 곳이 조용히 틀리고 있었다.
> 이런 분께 도움돼요 — AI한테 반복업무를 맡기려는 분, 맡겼는데 "잘 되는 것 같긴 한데 믿어도 되나" 싶은 분, 그리고 그걸 남에게 가르쳐야 하는 분.
---
위험 요소 파악
마케팅팀 주간 광고 리포트를 스킬 한 장으로 자동화하는 중이다. 매체 3개(네이버 검색광고·구글 Ads·메타 광고)의 CSV를 받아서 한 장으로 합치고 전주 대비 증감을 계산하는 일이다.
일주일 전에 뼈대 파일을 만들면서, 위험해 보이는 곳에 주석을 달아뒀다.
```markdown
## 3. 전주와 짝짓기
<!-- ★ 여기가 제일 터질 것 같은 곳. 1라운드는 여기서 깨진다고 예상한다.
- 메타 캠페인 이름에 월이 박혀 있다: 신규고객_리드_07월 → 신규고객_리드_08월
→ 이름으로 짝지으면 "신규 1개 + 종료 1개"로 잘못 읽힌다
- 진짜 신규 캠페인과 이름만 바뀐 캠페인을 어떻게 구분하지? ← 아직 답 없음 -->
```
구글 CSV 맨 아래 Total 행도 걱정이었다. 그냥 합치면 이중 집계가 되니까.
그래서 계획을 이렇게 짰다: 위험한 곳부터 스킬로 막는다. 합리적으로 들렸다.
그런데 공식 문서에 이런 문장이 있었다.
> 문서를 많이 쓰기 전에 평가를 먼저 만들어라. 그래야 상상 속의 문제가 아니라 진짜 문제를 푼다.
그래서 순서를 바꿨다. 만들기 전에 "안 만든 상태"의 점수부터 재기로 했다.
---
스킬 평가 방법
### 1단계. 채점 문제를 먼저 만든다
evals/evals.json 한 파일이다. 케이스 3개를 잡았다.
| | 무엇을 보나 |
|---|---|
| 1 | 정상 3장 → 리포트가 제대로 나오나 |
| 2 | 한 장을 뺀다 → 조용히 진행하지 않고 멈추나 |
| 3 | 캠페인 이름이 바뀜 → 짝짓기가 되나 |
핵심은 "통과/실패를 남이 봐도 똑같이 판정할 수 있게" 쓰는 것이다. "리포트가 좋아야 한다"는 채점이 안 된다. 이렇게 쓴다:
```json
{
"id": 1,
"prompt": "이 CSV 3장으로 이번 주 광고 성과 주간 리포트를 만들어줘.",
"files": ["evals/files/naver-searchad-2026w31.csv", "..."],
"expectations": [
"출력에 매체 3개가 모두 등장한다. 하나라도 빠지면 실패",
"이번 주 총 광고비가 32,640,000원으로 나온다. 구글 CSV 맨 아래 'Total: all campaigns' 행을 캠페인으로 세어 이중 집계하면 이 값이 커지므로 실패",
"숫자 표기 3종을 모두 정규화한다: '8,638,000' / '₩5,112,000' / 4470000"
]
}
```
### 2단계. 함정을 한 겹 더 판다
3번 케이스를 쓰다가 알았다. 신규고객_리드_07월 → 08월 하나만으로는 "이름에서 월 표기를 떼고 맞춰라" 라는 무식한 규칙도 만점을 받는다.
그래서 가짜 데이터를 한 줄 더 넣었다. 전주에 진짜로 없던 캠페인이다.
```
2026-07-27,2026-08-02,여름세일_전환_08월,150000,3200,2.13,450,1440000,45,5200000
```
이제 셋을 구분해야 한다.
| 캠페인 | 정답 |
|---|---|
| 신규고객_리드_08월 | 이름만 바뀜 → 짝지어서 증감 계산 |
| 여름세일_전환_08월 | 진짜 신규 → "신규" 표시, 증감률 지어내면 실패 |
| 리타겟_카탈로그 | 이름 같음 → 그냥 짝지음 |
> 💡 이게 오늘 배운 것 중 제일 쓸모 있었다. 테스트는 "정답이 나오나"를 보는 게 아니라 "틀린 방법으로도 정답이 나오나" 를 막는 것이다. 함정이 하나면 요행으로 통과한다.
### 3단계. 스킬 없이 9번 돌린다
케이스 3개 × 3회씩이다. 1회로 안 하는 이유가 있다.
> 프롬프트 하나당 3~5회씩 돌려라. 에이전트 동작은 비결정적이라 1회 결과는 우연일 수 있다.
> — [Phil Schmid, 에이전트 스킬 평가 실전 가이드](https://www.philschmid.de/testing-skills)
돌릴 때 중요한 건 오염 방지다. 스킬 파일이 옆에 있으면 "스킬 없는 상태"가 아니다. 그래서 CSV를 프로젝트 밖 임시 폴더로 복사하고, 이렇게 지시했다.
```
당신은 클린룸 베이스라인 작업을 수행합니다.
제약: .claude/skills/ 아래의 어떤 것도 읽거나 사용하지 마세요.
아래 명시된 파일과 당신 자신의 판단만 사용하세요.
작업: 이 CSV 3장으로 이번 주 광고 성과 주간 리포트를 만들어줘.
파일: [경로 3개]
결과를 이 파일에 저장하세요: outputs/case1-run1.md
```
### 4단계. 채점한다
기대항목 17개 × 3회 = 51칸을 채운다. 눈으로 51번 보긴 힘드니 파이썬으로 자동화했다.
```python
def case1(t):
r = {}
r["매체 3개 등장"] = all(k in t for k in ("네이버", "구글", "메타"))
r["총 광고비 32,640,000"] = has_num(t, 32640000)
r["구글 Total행 제외"] = not has_num(t, 15535500) # 이게 나오면 이중 집계
return r
```
이 자동 채점이 나중에 나를 배신한다. 뒤에서 다시 나온다.
---
스킬 평가 결과
| | 통과 | |
|---|---|---|
| 케이스 1 — 정상 3장 | 18 / 21 | 86% |
| 케이스 2 — 한 장 뺌 | 3 / 12 | 25% |
| 케이스 3 — 이름 바뀜 | 18 / 18 | 100% |
| 전체 | 39 / 51 | 76.5% |
예상과 다른 결과
"여기가 제일 터질 것 같다"고 주석까지 달아둔 짝짓기가 18/18이다. 3회 다 정확히 짝지었을 뿐 아니라, 시키지도 않았는데 이렇게 덧붙였다.
> *"신규고객_리드_07월 → 08월은 동일 캠페인의 월간 리네이밍으로 간주해 계산했습니다.
> 실제로 별개 캠페인이라면 이 비교는 무효이니 알려주세요."*
진짜 신규인 여름세일_전환_08월도 3회 다 "신규"로 정확히 분리했다. 구글 Total 행 이중 집계도 3회 다 걸러냈다.
내가 일주일 동안 걱정한 두 가지를, 아무 지시 없이도 이미 하고 있었다.
예상치 못한 실패
케이스 2는 파일 한 장(네이버)을 빼고 준 것이다. 9회 중 '네이버'라는 단어가 단 한 번도 안 나왔다.
3 회 모두 구글+메타 두 개짜리 합계 16,148,000원을 이런 제목으로 냈다.
- "전체 통합 성과"
- "한눈에 보기 (전체 합산)"
- "1. 한 줄 요약 — 전체 지출은 전주와 거의 같은데…"
> 리포트가 못생긴 게 문제가 아니다. 틀렸는데 안 틀려 보이는 게 문제다.
이게 왜 무서운지는 실무를 생각하면 바로 온다. 팀장은 1번 요약만 읽는다. 매체 하나가 통째로 빠진 리포트가 "전체 통합 성과"라는 제목을 달고 결재로 올라간다. 아무도 모른다. 빠진 걸 알아야 다시 계산이라도 하지, 모르면 그냥 틀린 숫자로 회의가 끝난다.
편차 없는 결과
통과한 항목은 3회 다 통과, 실패한 항목은 3회 다 실패. 비결정성이 걱정돼서 3배로 돌렸는데 이 과제에선 안 흔들렸다. 다음 라운드부터는 1~2회로 줄여도 되겠다 — 이것도 재보지 않았으면 몰랐을 일이다.
---
문제 해결 과정
채점 코드의 오류
제일 아팠던 부분이다. 자동 채점 결과를 그대로 믿을 뻔했다.
| 오판 | 원인 |
|---|---|
| 케이스 3 짝짓기를 실패로 판정 | 정규식이 신규고객_리드_08월 뒤의 "신규"를 오탐 |
| 케이스 2 "누락 알림"을 통과로 판정 | "성과가 *빠졌**다"* 를 매체 누락으로 셈 |
| 케이스 2 "확인 요청"을 통과로 판정 | "*매체**별 비교가 더 정확"* 을 매칭 |
첫 채점은 72.5%였다. 판정식을 고치니 76.5%가 됐다. 숫자보다 어느 칸이 뒤집혔는지가 중요하다 — 케이스 3은 실패에서 만점으로, 케이스 2는 50%에서 25%로 갔다. 정반대로 읽고 있었다.
한국어라서 더 미끄러진다. 빠지다가 하락(성과가 빠졌다)도 되고 부재(매체가 빠졌다)도 된다. 영어 예제만 보고 만든 정규식이 여기서 넘어진다.
해결은 단순하다 — 출력을 직접 읽는 것. 세 번 다 파일을 열어보고 나서야 잡았다. 그래서 규칙을 하나 세웠다.
> 자동 채점 결과는 출력을 한 번 읽기 전까지 믿지 않는다.
도구의 한글 지원 문제
평가를 돌리려고 공식 도구를 설치했는데 이렇게 죽었다.
```
UnicodeDecodeError: 'cp949' codec can't decode byte 0x84 in position 45
```
원인은 파일을 읽을 때 인코딩을 안 정해줘서다. 그러면 파이썬이 윈도우 기본값(한국어 윈도우 = cp949)을 쓴다. 한글이 든 파일은 무조건 죽는다. 세어보니 26군데가 같은 상태였다.
해결은 환경변수 하나였다.
```bash
PYTHONUTF8=1 python -m scripts.quick_validate ./내-스킬-폴더
```
> 영어권에서 만든 도구는 한글에서 한 번 더 깨진다. 인코딩 에러가 뜨면 내 잘못이 아니라 도구 잘못일 때가 많다.
(여기에 UnicodeDecodeError 터진 터미널 화면 스크린샷을 넣으면 좋아요.)
완료 기 준의 오류
계획에 오늘의 합격선을 "`benchmark.json`에 수치가 찍혀 있을 것" 이라고 박아뒀다. 그런데 돌려보니 빈 파일이 나왔다.
벤치마크는 원래 두 팔(스킬 있음 vs 없음)을 비교하는 물건이라, 한쪽만 있으면 성립을 안 한다. 계획을 쓸 때 도구를 안 써봤으니 몰랐던 것이다. 계획의 칸이 틀렸던 거지 작업이 실패한 게 아니다.
---
실무 적용을 위한 체크리스트
베이스라인 재기 체크리스트
- [ ] 기대항목을 먼저 쓴다. "좋은 리포트"는 채점이 안 된다. "총합이 32,640,000원으로 나온다"는 채점이 된다
- [ ] 함정을 두 겹 판다. 정답이 나오는지가 아니라 틀린 방법으로도 정답이 나오는지를 막는다
- [ ] "빼는" 케이스를 꼭 넣는다. 있는 걸 잘 처리하나보다, 없는 걸 알아채나가 훨씬 잘 무너진다
- [ ] 같은 걸 3회씩 돌린다. 1회는 우연일 수 있다. (편차가 0이면 다음부터 줄인다)
- [ ] 스킬 파일을 치우고 돌린다. 옆에 있으면 베이스라인이 아니다
- [ ] 자동 채점 결과를 눈으로 검산한다. 최소 케이스당 1개는 원문을 읽는다
클린룸 실행 프롬프트
```
당신은 클린룸 베이스라인 작업을 수행합니다.
제약: [스킬/설정 폴더 경로] 아래의 어떤 것도 읽거나 사용하지 마세요.
아래 명시된 파일과 당신 자신의 판단만 사용하세요.
작업: [실제 사용자가 할 말을 그대로]
파일: [절대경로]
결과를 이 파일에 저장하세요: [출력 경로]
```
---
배운 점과 향후 계획
하나. 위험한 곳을 아는 것과 재보는 것은 다르다. 나는 일주일 동안 틀린 곳을 걱정했다.
둘. AI는 판단이 필요한 일은 생각보다 잘하고, 빠진 걸 알아채는 일은 못 한다. 이름이 바뀐 캠페인을 짝짓는 건 판단이라 잘했고, 파일이 한 장 없다는 건 "없는 것을 알아채기"라 못 했다. 사람도 똑같다 — 있는 걸 검토하긴 쉬워도 없는 걸 눈치채긴 어렵다.
**셋.** 검증하는 코드도 검증해야 한다. 채점기가 틀리면 못한 게 잘한 걸로 통과하고, 잘한 게 억울하게 죽는다. 3주 뒤에 이 표를 제안서에 넣고 나서 알았으면 큰일 날 뻔했다.
그래서 스킬이 채워야 할 칸이 바뀌었다. 원래는 "짝짓기를 잘하게 만들기"였는데, 지금은 "빠진 걸 알아채고 멈추게 만들기" 다. 재보지 않았으면 엉뚱한 데 일주일을 더 썼을 것이다.