Skip to content

test: tagging eval_set_v2 평가 인프라 + v2 좁힘 제거 (v3 준비)#14

Merged
gyesswhat merged 10 commits into
mainfrom
feat/#7-tagging-v1
May 23, 2026
Merged

test: tagging eval_set_v2 평가 인프라 + v2 좁힘 제거 (v3 준비)#14
gyesswhat merged 10 commits into
mainfrom
feat/#7-tagging-v1

Conversation

@gyesswhat

@gyesswhat gyesswhat commented May 22, 2026

Copy link
Copy Markdown
Contributor

개요

v3 코드 변경 진입 전 평가 인프라 정리 + v2 정합성 fix.
새 평가셋 (test_case_v2.md 의 48케이스) 으로 must/accept/source/직군모호 분리 metric 까지
갖추고, v2 의 카테고리 좁힘이 새 평가셋과 충돌하는 문제 해소.
fixture accept 확장 + rate limit 회피 sleep 까지 포함.

변경 사항

  • 기능
  • 버그 수정 (v2 카테고리 좁힘 제거)
  • 리팩토링
  • 문서 (측정 결과 분석 2건)
  • 테스트 (v2 spec 정합 + conftest variant 핀 + cross-category 단위 테스트 추가)
  • 기타 (평가 fixture v2 / eval 스크립트 v3 / sleep)

세부 내용

7개 commit, 크게 3 갈래:

A. 새 평가 인프라

  • `experiments/tagging/fixtures/eval_set_v2.jsonl` (48케이스 · must 12 + accept 35
    • source ST/A/R + 직군 모호 플래그)
  • `experiments/tagging/eval_v3_tiebreak.py` (must/accept 분리 metric · 직군/카테고리
    /길이/모호 그룹별 집계)
  • `experiments/tagging/_template.md` 보강 (새 metric 컬럼)

B. v2 정합성 fix

  • `app/services/tagging/v2_postscore.py` 의 `_allowed_tags_for(category)` 제거
  • 프롬프트 명세 "카테고리 무관, 풀 전체 열려 있다" 와 일치
  • 관련 테스트 정합 (v2 spec 5~7 으로 갱신) + conftest variant 핀

C. 측정 baseline (v2 2회)

  • 1차 (5/22 첫): fixture v2 + 좁힘 제거 후 측정. must 72.7% · accept 30.3% · 풀외 21.2%
  • 2차 (5/22 재): fixture accept 확장 (case A 13건 흡수) + sleep 2초 추가 후 측정.
    must 65.6% · accept 32.1% · 풀외 19.4%
  • 두 측정 결과 .md 의 §요약·§관찰·§결정 작성

결정 과정 (왜 이 순서로 갔는지)

원래 plan 은 v3 알고리즘 변경 (점수 격차 + 후보 갯수 + tie-break) 으로 바로 가려
했는데, 다음 두 번 우회:

  1. 평가 인프라부터 (test_case_v2.md 가 must/accept 로 정리되어 있고, 기존
    metric "단일 expected" 가 측정 변동성 컸음)
  2. v2 좁힘 제거 (eval_v3 첫 시도에서 PARSE_ERR 폭증, 원인은 v2 의 카테고리 좁힘
    vs 새 fixture 의 cross-category accept 충돌)
  3. fixture 흡수 + sleep (1차 측정에서 풀외 21% 의 case-by-case 분석 결과 13건이
    합리적이라 accept 에 흡수, LLM_ERR 4건은 분당 토큰 rate limit 이었음을 raw 분석
    으로 확인)

v2 측정 변화 (3회 비교)

항목 5/18 v2 (eval_set_v1, 10건) 5/22 v2 1차 (eval_set_v2, 48건) 5/22 v2 2차 (fixture 확장 + sleep)
metric 단일 expected 일치율 must/accept 분리 (동일)
케이스 수 10 44/48 (LLM_ERR 4) 48/48
must 회수율 (다른 metric) 72.7% 65.6%
accept 회수율 (다른 metric) 30.3% 32.1%
풀 외 비율 (다른 metric) 21.2% 19.4%
corrective 0 3 1
LLM_ERR 0 4 (rate limit) 0
1건 비용 $0.0026 $0.0026 $0.0025

참고사항

측정 방법

# .env 의 TAGGING_VARIANT 가 측정 대상 (v1_baseline / v2_postscore / v3_*)
uv run python experiments/tagging/eval_v3_tiebreak.py
  • 48 케이스 × 약 1.7초 LLM + 2초 sleep ≈ 3~4분
  • raw json + analysis .md 자동 생성 (마커 사이 in-place 갱신, 사람이 쓴 섹션 보존)
  • 측정 후 .md 의 §요약·§관찰·§결정 작성하고 commit

주의사항

  1. LLM 비결정성: must ±5~7%p 왔다갔다 함...
  2. rate limit (분당 5만 입력 토큰): sleep 2초로 회피 중. 끝에 몰리면 또 터질 수 있음.
  3. 직군 모호 케이스 must 0%: P-PA-04, DS-DA-04. fixture 흡수로도 안 풀리고 가중치 후처리도 못 푼 상태.
  4. fixture 흡수 효과 미미: case A 13건 흡수해도 metric 1~2%p 변화. LLM 비결정성 때문. 단일 측정으로 fixture 효과 결론 X

v3 진입 시 권장 순서

v2 측정 md (`experiments/tagging/2026-05-22_v2_postscore.md`) §결정 1~6 참고.
요약:

  1. v3 한 묶음 (후보 갯수 57 → 710 + 점수 격차 H/M/L=3/2/1 → 5/2/1 + tie-break
    score - α·index)
  2. 측정 protocol 변경 (variant 당 2~3회 평균)
  3. v3 결과 보고 직군 모호 보강 (v4 의 primaryCategory 부스트 대신 별도 lever 검토)
  4. 풀외 비율은 코드 후처리 범위 밖. 추적 metric 으로만

변경 안 한 것

  • v1_baseline 의 카테고리 좁힘은 그대로 (이번 fix 는 v2 만 scope). v1 도 새 fixture
    로 측정하려면 같은 좁힘 제거 또는 별도 trial 필요
  • /api/tagging 더미 응답은 그대로
  • prod default `tagging_variant` 는 그대로 (v2_postscore)

테스트

  • 로컬 pytest 통과 (70 tests green)
  • ruff / mypy clean (변경 파일 기준)
  • LLM 실측 v2 2회 (1차/2차 결과 .md 첨부)

롤백/리스크

리스크 포인트

  • v2 의 카테고리 좁힘 제거로 풀 전체가 화이트리스트가 됨. 본문 LLM 응답에 다른 카테고리 태그가 자유롭게 섞일 수 있음 (의도된 동작, 프롬프트 명세와 일치)
  • conftest 에서 `TAGGING_VARIANT=v2_postscore` 핀 → 향후 v3 추가 시 conftest 도 같이 업데이트 필요

롤백

  • revert (각 commit 이 명확히 분리되어 있어 일부만 revert 가능)

관련 이슈

Refs #7 (이미 PR #10 에서 closed). 후속 v3 작업은 별도 issue 권장.

@gyesswhat gyesswhat added fix 버그, 오류, 잘못된 동작 수정 test 테스트 코드 추가 또는 테스트 관련 변경 status:review 작업 완료, 리뷰 또는 머지 대기 중 priority:mid 일반적인 우선순위 labels May 22, 2026
@gyesswhat gyesswhat self-assigned this May 22, 2026
@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@gyesswhat, we couldn't start this review because you've used your available PR reviews for now.

Your plan currently allows 1 review/hour. Refill in 49 minutes and 33 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6ca7720f-28d7-4100-88f5-1abfbdebea8e

📥 Commits

Reviewing files that changed from the base of the PR and between 48c1909 and a7093f3.

📒 Files selected for processing (2)
  • experiments/tagging/eval_v3_tiebreak.py
  • experiments/tagging/results/2026-05-22T151749Z_v2_postscore.json
📝 Walkthrough

Walkthrough

v2_postscore 후보 검증 로직을 카테고리별 화이트리스트에서 전역 ALL_TAGS 기반으로 단순화합니다. 이를 검증하는 평가 프레임워크 및 48케이스 벤치마크 결과, 교차 카테고리 후보 수용을 검증하는 회귀 테스트를 추가합니다.

Changes

v2_postscore 개선 및 벤치마크 평가

Layer / File(s) Summary
후보 검증 로직 단순화
app/services/tagging/v2_postscore.py
_parse_and_validate에서 allowed_tags 인자 제거, run 내 카테고리별 화이트리스트 계산 제거. 이제 모든 후보는 ALL_TAGS 포함 여부만으로 검증되며, 카테고리 제한 없이 cross-category 후보도 수용 가능합니다.
테스트 및 fixture 업데이트
tests/conftest.py, tests/test_tagging.py
conftest에서 모든 테스트마다 TAGGING_VARIANT=v2_postscore 고정. happy path를 _HAPPY_LLM_RAW·_HAPPY_TOP3 상수로 통합, corrective 재시도와 count violation 케이스들이 top3 기반 정규화 검증. 새로운 v2_postscore_accepts_cross_category_candidates 테스트로 카테고리 혼합 후보 수용 회귀 검증.
평가 프레임워크 구축
experiments/tagging/eval_v3_tiebreak.py
_LLMTracker로 LLM 호출 토큰·지연 추적. must/accept 회수율, 풀외 비율, full_must 충족, corrective 여부를 분리 집계. 직군·카테고리·길이(ST+A+R 글자수 tertile)·모호성별 그룹화 평균, 마크다운 보고서 자동 생성·템플릿 업서트.
평가 데이터 및 템플릿
experiments/tagging/fixtures/eval_set_v2.jsonl, experiments/tagging/_template.md, experiments/tagging/results/*.json
48케이스 JSONL fixture(must/accept 태그, 모호성 플래그 포함). 메트릭 템플릿을 "일치율"에서 "must/accept 회수율 + 풀외 비율" 중심으로 개편. 두 시점(15:17Z, 16:07Z)의 벤치마크 결과 JSON으로 케이스별 히트/리콜/OOF/비용/지연 기록.
벤치마크 결과 분석 보고서
experiments/tagging/2026-05-22_v2_postscore.md
자동 채움 메트릭 구간, 케이스별 결과 테이블, 그룹별 집계. Sleep 2초 표준화로 LLM_ERR 4→0 제거 확인. Fixture 흡수 효과 평가, must 회수율 7%p 하락 원인 분해(새로 측정된 4건 + LLM 비결정성). 직군 모호 케이스의 지속적 약점, v3 진입 우선순위 및 tie-break 강화 계획.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

📊 복잡도 분석

증가 요인:

  • 로직 단순화: _parse_and_validate 함수 시그니처 변경으로 run의 1차·2차 호출 지점 2곳 동시 수정 필요, 제거된 _allowed_tags_for 유틸과 TAGS_BY_CATEGORY 사용처 확인 필수
  • 신규 평가 프레임워크: eval_v3_tiebreak.py 588줄 신규 파일, 토큰 추적·메트릭 계산·마크다운 생성 로직이 얽혀 있음
  • 테스트 재구성: happy path, corrective, count violation 시나리오가 top3 기반으로 일관되어야 하므로, 각 테스트의 의도와 LLM 응답 조작 간의 인과관계 검증 필요
  • 이종 변경: 코어 로직(v2_postscore.py) + 평가 도구(eval_v3_tiebreak.py) + 테스트(test_tagging.py) + 결과 데이터(JSON/MD) 4가지 역할

감소 요인:

  • 결과 JSON/MD는 생성 데이터로, 내용 검증보다 스키마/형식 일관성만 확인 가능
  • 카테고리 제한 제거는 비교적 직선적인 변경 (함수 시그니처 → 호출 지점 2곳)
  • 평가 프레임워크는 독립적이므로 코어 로직과 격리 검토 가능

파일 다양성: 7개 파일 (코어 로직 1 + 테스트 2 + 평가 5), 함수·클래스 추가 3개, 의존성 재구성 필요

Possibly related PRs

  • 2026-KUSITMS-GLIT/2026-KUSITMS-GLIT-AI#10: v2_postscore 초기 도입 PR로, _parse_and_validate·run 흐름이 이번 PR에서 카테고리 제한 제거로 재구성되므로 코드 레벨 선행 컨텍스트로 필요합니다.

Suggested labels

priority:high, tagging:v2_postscore, eval:benchmark, testing:regression

Suggested reviewers

  • hyhy-j
  • parkdo0918

Poem

🏷️ 카테고리의 벽을 허물고
ALL_TAGS 풀로 모두 품는다면
평가는 48케이스가 증명하네 🔍
must·accept 회수율
직군 모호함도 드러내고
v3를 향해 한 걸음 더 🚀
숫자가 말해주는 개선의 증거

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.48% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경 사항(v2 카테고리 좁힘 제거 + 평가 인프라 정리)을 명확하고 간결하게 요약하고 있음.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed PR 설명이 요약, 변경사항 체크, 테스트, 롤백/리스크, 관련 이슈까지 템플릿 항목을 거의 모두 포함하고 있으며, v3 진입을 위한 평가 인프라 정리와 v2 정합성 수정이라는 목표가 명확하고 상세히 기술됨.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#7-tagging-v1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
tests/test_tagging.py (1)

173-195: 💤 Low value

🔧 (선택) 실제 태그로 경계값 테스트 명확성 향상

8-tag 케이스(Line 178)가 "#A"~"#H" fake tags를 사용하여 count violation(8 > 7)과 pool violation을 동시에 트리거합니다. Corrective retry 메커니즘 검증에는 충분하지만, 테스트 의도를 더 명확히 하려면 실제 태그 8개를 사용하는 것도 고려할 수 있습니다(예: PROBLEM_SOLVING 카테고리에서 8개 선택).

현재 구현도 동작에는 문제없으나, "count violation 특화" 테스트임을 강조하려면 실제 태그가 조금 더 명확합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_tagging.py` around lines 173 - 195, The 8-tag case in
test_v1_tagging_count_violation_then_recovers_via_corrective uses synthetic tags
("`#A`".."`#H`") which can trigger both count and pool violations; change the third
param value in the bad_response parametrize list to a JSON string containing
eight real tags from the PROBLEM_SOLVING category (instead of "`#A`".."`#H`") so the
test isolates a count-violation and still allows the corrective retry flow via
_anthropic_msg and _HAPPY_LLM_RAW to be exercised.
tests/conftest.py (1)

25-38: 💤 Low value

📌 TAGGING_VARIANT 고정 — CI/로컬 일관성 vs v1 테스트 커버리지

TAGGING_VARIANT=v2_postscore 고정은 CI/로컬 환경 간 테스트 결과 불일치를 막는 좋은 선택입니다(도크스트링 Lines 29-31에 명시된 대로).

다만 이로 인해 v1_baseline이 CI에서 테스트되지 않게 됩니다. PR objectives가 "v3 진입 전 v2 정합성 수정"이므로 v2 집중은 의도된 것으로 보이지만, 만약 v1이 여전히 프로덕션에서 사용 중이거나 롤백 대상이라면 별도 테스트 전략(예: variant별 분리된 test suite 또는 matrix CI)이 필요할 수 있습니다.

현재 PR 범위에서는 문제없으나, v1 deprecation timeline을 고려한 테스트 전략 검토를 권장합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/conftest.py` around lines 25 - 38, The fixture _clean_settings
currently force-sets TAGGING_VARIANT="v2_postscore", which prevents running
tests for v1_baseline; change the fixture to set TAGGING_VARIANT from a
configurable test option or env var (e.g. prefer a pytest CLI option like
--tagging-variant or respect an env var TEST_TAGGING_VARIANT) with a default of
"v2_postscore", so CI can run a matrix (or local dev can override) while
retaining get_settings.cache_clear calls before and after; update the test
configuration to add pytest_addoption to expose --tagging-variant (or document
TEST_TAGGING_VARIANT) and have _clean_settings read that value instead of
hardcoding "v2_postscore".
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@experiments/tagging/eval_v3_tiebreak.py`:
- Line 572: The print call using Path.relative_to(Path.cwd()) can raise
ValueError when the current working directory differs; update the print at the
end of the block (the f"\nRaw saved → ..." statement) to use the existing helper
_rel() instead of calling relative_to directly so the path is safely rendered;
locate the print in eval_v3_tiebreak.py and replace the relative_to(Path.cwd())
usage with _rel(saved_json) (or the appropriate variable passed to _rel) to
avoid the exception.
- Around line 161-163: The code currently assigns the raw LLM exception string
to llm_err (except LLMError as e -> llm_err = f"{type(e).__name__}: {e}"), which
may leak vendor org/request_id info; replace this by creating/using a sanitizer
(e.g. sanitize_llm_error or mask_sensitive) that returns only the exception
class plus a short, masked summary (remove UUIDs, tokens like org_ or
request_id, or truncate to ~100–200 chars), and set llm_err =
sanitize_llm_error(e) instead of storing the raw exception; ensure the helper is
used wherever LLMError is caught so raw vendor responses are never written to
results.
- Around line 106-107: The code currently reads and writes files without
specifying encoding which makes it locale-dependent; update _load_fixtures to
call path.read_text(encoding="utf-8") and similarly ensure any file read/write
operations in the block referenced around lines 517-529 use explicit
encoding="utf-8" (e.g., Path.read_text(encoding="utf-8") and open(..., "w"/"r",
encoding="utf-8")) so fixtures and Korean text are correctly handled across
environments.

---

Nitpick comments:
In `@tests/conftest.py`:
- Around line 25-38: The fixture _clean_settings currently force-sets
TAGGING_VARIANT="v2_postscore", which prevents running tests for v1_baseline;
change the fixture to set TAGGING_VARIANT from a configurable test option or env
var (e.g. prefer a pytest CLI option like --tagging-variant or respect an env
var TEST_TAGGING_VARIANT) with a default of "v2_postscore", so CI can run a
matrix (or local dev can override) while retaining get_settings.cache_clear
calls before and after; update the test configuration to add pytest_addoption to
expose --tagging-variant (or document TEST_TAGGING_VARIANT) and have
_clean_settings read that value instead of hardcoding "v2_postscore".

In `@tests/test_tagging.py`:
- Around line 173-195: The 8-tag case in
test_v1_tagging_count_violation_then_recovers_via_corrective uses synthetic tags
("`#A`".."`#H`") which can trigger both count and pool violations; change the third
param value in the bad_response parametrize list to a JSON string containing
eight real tags from the PROBLEM_SOLVING category (instead of "`#A`".."`#H`") so the
test isolates a count-violation and still allows the corrective retry flow via
_anthropic_msg and _HAPPY_LLM_RAW to be exercised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 73fc339b-9d57-4b57-90f7-26cc8628ca86

📥 Commits

Reviewing files that changed from the base of the PR and between cd64eb2 and 48c1909.

📒 Files selected for processing (9)
  • app/services/tagging/v2_postscore.py
  • experiments/tagging/2026-05-22_v2_postscore.md
  • experiments/tagging/_template.md
  • experiments/tagging/eval_v3_tiebreak.py
  • experiments/tagging/fixtures/eval_set_v2.jsonl
  • experiments/tagging/results/2026-05-22T151749Z_v2_postscore.json
  • experiments/tagging/results/2026-05-22T160708Z_v2_postscore.json
  • tests/conftest.py
  • tests/test_tagging.py

Comment thread experiments/tagging/eval_v3_tiebreak.py Outdated
Comment thread experiments/tagging/eval_v3_tiebreak.py
Comment thread experiments/tagging/eval_v3_tiebreak.py Outdated
@gyesswhat
gyesswhat merged commit 45dc510 into main May 23, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix 버그, 오류, 잘못된 동작 수정 priority:mid 일반적인 우선순위 status:review 작업 완료, 리뷰 또는 머지 대기 중 test 테스트 코드 추가 또는 테스트 관련 변경

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant