# task-1059.1 완료 보고서: 동적 회귀 테스트(LLM 실행 기반 evals) 시스템 구축

**작성자**: 헤르메스 (dev1-team 팀장)
**작성일**: 2026-03-26
**팀원**: 불칸(백엔드 구현), 아르고스(테스트 작성)

---

## SCQA

**S**: task-1054.1에서 11개 마케팅 스킬에 74개 evals.json을 생성하여 정적 스키마 검증이 완료된 상태다.

**C**: 실제 LLM을 호출하여 eval을 실행하고 스킬 품질을 정량적으로 측정하는 "동적 회귀 테스트" 시스템이 없어, 스킬 변경 시 품질 회귀를 자동 감지할 수 없다.

**Q**: LLM 기반 eval runner를 구축하여 11개 스킬 74개 eval을 자동 실행하고 PASS/FAIL 판정을 할 수 있는가?

**A**: eval runner 스크립트를 구축 완료했다. TDD(RED→GREEN) 사이클로 개발하여 33개 테스트 전체 PASS, pyright 에러 0건, 11개 스킬 74개 eval의 dry-run 구조 검증 성공. ANTHROPIC_API_KEY 미설정으로 실제 LLM 호출(Phase 4)은 미실행 — API 키 설정 후 즉시 실행 가능.

---

## 산출물

- eval runner 스크립트: `/home/jay/workspace/tools/eval-runner/run_evals.py` (616줄)
- 테스트 파일: `/home/jay/workspace/tools/eval-runner/test_run_evals.py` (530줄)

## 사용법

```bash
# 단일 스킬 dry-run
python3 run_evals.py --skill ad-creative --dry-run

# 전체 스킬 dry-run
python3 run_evals.py --all --dry-run

# 단일 스킬 실제 실행 (API 키 필요)
ANTHROPIC_API_KEY=sk-... python3 run_evals.py --skill ad-creative --verbose

# 전체 스킬 실제 실행
ANTHROPIC_API_KEY=sk-... python3 run_evals.py --all --verbose
```

## 구현 내용

### Phase 1: eval runner 스크립트
- CLI: `--skill`, `--all`, `--verbose`, `--dry-run` 4개 옵션
- evals.json 로딩: `{"skill_name": ..., "evals": [...]}` 구조 파싱
- LLM 호출: anthropic SDK 0.84.0, claude-haiku-4-5-20251001 모델, 1초 간격 rate limit

### Phase 2: 평가 기준
- **키워드 매칭**: expected_output에서 한글 2자+/영문 3자+ 핵심 키워드 추출, 30% 이상 매칭 시 PASS
  - 한글 조사 제거, 동사/형용사 어미 필터, 부사/수량어 제외
- **금지어 체크**: boundary의 `→ 스킬명` 패턴에서 라우팅 대상 추출, 단정적 표현 감지
- **라우팅 정확성**: 위임 표현 패턴 매칭 + 직접 수행 여부 휴리스틱 판단
- 모듈화된 함수: `keyword_match()`, `check_forbidden()`, `check_routing()`, `evaluate_response()`

### Phase 3: 결과 보고서
- JSON 형식 보고서 자동 생성 (스킬별 pass_rate, 전체 pass_rate, FAIL 케이스 상세)
- `generate_report()` 함수로 파일 저장 + JSON 문자열 반환

### Phase 4: 실제 실행
- dry-run 구조 검증: 11개 스킬, 74개 eval 정상 확인
- 실제 LLM 실행: ANTHROPIC_API_KEY 환경변수 미설정으로 미실행
  - API 키 설정 시 `python3 run_evals.py --all --verbose`로 즉시 실행 가능

## 스킬별 eval 현황 (dry-run 검증 완료)

- ad-creative: 7 evals
- ai-seo: 7 evals
- analytics-tracking: 7 evals
- content-strategy: 6 evals
- copywriting: 7 evals
- marketing-psychology: 6 evals
- paid-ads: 7 evals
- pricing-strategy: 6 evals
- programmatic-seo: 6 evals
- seo-audit: 8 evals
- social-content: 7 evals
- **총계: 11개 스킬, 74개 evals**

(참고: 작업 지시에는 "8개 스킬 56개 eval"로 명시되었으나, 실제로는 task-1054.1 이후 11개 스킬 74개 eval이 존재. eval runner는 전체를 지원.)

## 테스트 결과

```
33 passed in 0.10s
```

- TestLoadEvals: 5 PASS (로딩, 필드 파싱, 잘못된 JSON, 빈 배열, 미존재 스킬)
- TestKeywordMatch: 5 PASS (한글 매칭, 미매칭, 부분 매칭, 영문 대소문자, missed 키워드)
- TestCheckForbidden: 3 PASS (라우팅 금지, 정상 응답, 단정적 표현)
- TestCheckRouting: 2 PASS (올바른 위임, 범위 밖 거부)
- TestEvaluateResponse: 4 PASS (필수 키, pass 케이스, fail 케이스, eval_id)
- TestCLIParsing: 5 PASS (skill, all, verbose, dry-run, invalid)
- TestResultSummary: 3 PASS (스킬별 rate, 전체 rate, fail 상세)
- TestDryRun: 3 PASS (API 미호출, 구조 검증, eval count)
- TestGetSkillList: 3 PASS (타입, 포함, 비어있지 않음)

## pyright 결과

```
0 errors, 0 warnings, 0 informations
```

## 발견 이슈 및 해결

### 자체 해결 (3건)

1. **키워드 매칭 임계값 조정** — 초기 70%에서 30%로 낮춤
   - 원인: 한글 NLP 키워드 추출이 불완전하여 70% 기준 시 정상 응답도 FAIL 판정
   - 해결: 30% 임계값으로 조정, 향후 형태소 분석기(konlpy 등) 도입 시 상향 가능

2. **라우팅 대상 정규식 버그** — `[→->]+` 문자 클래스에서 `→` 유니코드 처리 오류
   - 해결: `(?:→|->)` 그룹 패턴으로 교체 (`run_evals.py:181`)

3. **evals 수 불일치** — 작업 지시(8개/56개)와 실제(11개/74개) 차이
   - 해결: eval runner를 실제 파일 기준으로 동적 탐색하도록 구현 (하드코딩 없음)

### 범위 외 미해결 (1건)

1. **Phase 4 실제 LLM 실행 미완료** — 범위 외 사유: ANTHROPIC_API_KEY 환경변수 미설정. API 키 설정 후 `python3 run_evals.py --all --verbose` 실행하면 보고서 자동 생성됨.

---

## 셀프 QC

- [x] 1. 다른 파일 영향: 없음 (새 디렉토리 `/home/jay/workspace/tools/eval-runner/`에만 파일 생성)
- [x] 2. 엣지 케이스: 빈 evals, 잘못된 JSON, 미존재 스킬, API 키 미설정 — 모두 테스트됨
- [x] 3. 작업 지시 일치: Phase 1-3 완료, Phase 4는 API 키 부재로 dry-run 대체
- [x] 4. 에러 처리/보안: API 키 하드코딩 없음, ValueError/FileNotFoundError 적절히 처리
- [x] 5. 테스트 커버리지: 33개 테스트 전체 PASS, 모든 주요 함수 테스트됨
- [x] 6. 발견 이슈 해결: 자체 해결 3건, 범위 외 1건 (사유 명시)
