Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 8 additions & 18 deletions app/services/tagging/v2_postscore.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,10 @@
JOB_ROLE_LABELS_KO,
PRIMARY_CATEGORY_LABELS_KO,
JobRole,
PrimaryCategory,
)
from app.schemas.tagging import TaggingRequest, TaggingResponse
from app.services._clients.llm_client import LLMClient, WorkloadType
from app.services.tagging.data import ALL_TAGS, TAGS_BY_CATEGORY, tag_score
from app.services.tagging.data import ALL_TAGS, tag_score
from app.services.tagging.exceptions import TaggingValidationError
from app.services.tagging.v1_baseline import _extract_text

Expand Down Expand Up @@ -95,21 +94,13 @@ def _strip_code_fence(text: str) -> str:
return text


def _allowed_tags_for(category: PrimaryCategory) -> frozenset[str]:
"""선택된 ``primaryCategory`` 안에서 허용되는 태그 셋.

카테고리 외 태그를 후보 단계에서 걸러내 contract (선택 역량 내 태그만 출력)
를 보장한다.
"""
return TAGS_BY_CATEGORY[category]


def _parse_and_validate(raw: str, allowed_tags: frozenset[str] = ALL_TAGS) -> list[str]:
def _parse_and_validate(raw: str) -> list[str]:
"""후보 검증 — 갯수 :data:`_CANDIDATE_MIN`~:data:`_CANDIDATE_MAX`, 풀 내, 중복 X.

실패 시 :class:`TaggingValidationError`. 후처리에서 ``_FINAL_TOP`` 으로 자르므로
여기서는 후보 단계 갯수만 검증한다. ``allowed_tags`` 는 카테고리별 허용 셋을
좁혀 넘길 수 있다 (기본 ``ALL_TAGS``).
여기서는 후보 단계 갯수만 검증한다. 화이트리스트는 :data:`ALL_TAGS` 전체 —
프롬프트 명세("카테고리 무관, 풀 전체 열려 있다") 와 일치하도록 카테고리
좁힘은 적용하지 않는다.
"""
text = _strip_code_fence(raw)
try:
Expand Down Expand Up @@ -137,7 +128,7 @@ def _parse_and_validate(raw: str, allowed_tags: frozenset[str] = ALL_TAGS) -> li
if len(set(str_tags)) != len(str_tags):
raise TaggingValidationError("detailTags 중복 포함")

out_of_pool = [t for t in str_tags if t not in allowed_tags]
out_of_pool = [t for t in str_tags if t not in ALL_TAGS]
if out_of_pool:
raise TaggingValidationError(f"허용 태그 풀 외 태그 포함: {out_of_pool}")

Expand Down Expand Up @@ -169,7 +160,6 @@ def _build_corrective_user_message(reason: str) -> str:
async def run(req: TaggingRequest, llm: LLMClient, model: str) -> TaggingResponse:
"""Tagging v2_postscore 실행."""
system = _render_prompt(req)
allowed = _allowed_tags_for(req.selected_competency)
log_ctx: dict[str, Any] = {
"job_role": req.job_role.value,
"primary_category": req.selected_competency.value,
Expand All @@ -185,7 +175,7 @@ async def run(req: TaggingRequest, llm: LLMClient, model: str) -> TaggingRespons
first_raw = _extract_text(first_msg.content)

try:
candidates = _parse_and_validate(first_raw, allowed)
candidates = _parse_and_validate(first_raw)
except TaggingValidationError as first_err:
logger.warning(
"tagging.validation_failed",
Expand All @@ -204,7 +194,7 @@ async def run(req: TaggingRequest, llm: LLMClient, model: str) -> TaggingRespons
)
retry_raw = _extract_text(retry_msg.content)
try:
candidates = _parse_and_validate(retry_raw, allowed)
candidates = _parse_and_validate(retry_raw)
except TaggingValidationError as retry_err:
logger.warning(
"tagging.validation_failed",
Expand Down
253 changes: 253 additions & 0 deletions experiments/tagging/2026-05-22_v2_postscore.md

Large diffs are not rendered by default.

31 changes: 21 additions & 10 deletions experiments/tagging/_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ _생성: `{{RAN_AT}}` · raw: [{{RAW_JSON_NAME}}](results/{{RAW_JSON_NAME}})_
| --- | --- |
| 모델 | `{{MODEL}}` |
| variant | `{{VARIANT}}` |
| 케이스 수 | {{N_CASES}} |
| 평균 일치율 | **{{AVG_SCORE}}%** (exact {{EXACT}} · partial {{PARTIAL}} · none {{NONE}}) |
| 케이스 수 | {{N_CASES}} (파싱 성공 {{N_OK}}) |
| 평균 must 회수율 | **{{AVG_MUST_RECALL}}%** (must 전체 충족 {{N_FULL_MUST}}/{{N_OK}}) |
| 평균 accept 회수율 | {{AVG_ACCEPT_RECALL}}% |
| 평균 풀 외 비율 | {{AVG_OOF_RATE}}% |
| 평균 응답시간 | {{AVG_LATENCY}}ms (corrective 포함 합산) |
| 평균 입력 토큰 | {{AVG_INPUT}} (corrective 포함 합산) |
| 평균 출력 토큰 | {{AVG_OUTPUT}} |
Expand All @@ -27,29 +29,38 @@ _생성: `{{RAN_AT}}` · raw: [{{RAW_JSON_NAME}}](results/{{RAW_JSON_NAME}})_

{{CASE_TABLE}}

(굵게: `expected ∩ actual` — 표면 매치)
(actual 강조: **must 정답** · *accept 정답* · 일반=풀외. `⚠️` = 직군 모호 케이스)

### 직군별 평균 일치율
### 직군별

{{ROLE_TABLE}}

### 카테고리별 평균 일치율
### 카테고리별

{{CATEGORY_TABLE}}

### 길이별 (ST+A+R 합산 글자수 tertile)

{{LENGTH_TABLE}}

### 직군 모호 vs 비모호

{{AMBIGUOUS_TABLE}}

<!-- AUTO:END METRICS -->

## 요약

> TODO (1~2줄): 결과 핵심 정리. 평균 일치율 · corrective 발생 여부 · 이전 variant 대비 변화 .
> TODO (1~2줄): must/accept 회수율 · 풀외율 · 직군 모호 영향 · 이전 variant 대비 변화 핵심 정리.

## 관찰

> TODO: 케이스별 raw response 보면서 인사이트 정리. 자주 유용한 항목:
> - 어느 케이스가 expected 와 크게 달랐는지, 의미적으로는 합리적인지
> TODO: 케이스별 raw response 보면서 인사이트 정리. 유용한 항목:
> - must miss 가 발생한 케이스 패턴 (의미적으로는 가까운 accept 로 대체했는지, 완전히 빗나갔는지)
> - 풀 외 태그가 나온 케이스 — 어떤 태그를 골랐고 왜 합리적인지/아닌지
> - 직군 모호 케이스 (P-PA-04, DS-DA-04) 의 직군 가중치 처리 흐름
> - corrective 발생 패턴 (코드블록 / 갯수 / 풀-외 / 기타)
> - 직군 / 카테고리별 편차 해석
> - 일치율 metric 의 표면적 한계 (semantic vs surface)
> - 직군 / 카테고리 / 길이별 편차 해석

## 결정

Expand Down
Loading
Loading