8074be6b6d
Phase 4-B v1 첫 검증 결과 자료 부족 토픽인데도 모델이 confidence='high'
박는 케이스 발견. 정의 (high = 자료 + 다른 ai_explanation 으로 패턴 명확)
보다 과신 — UX 신뢰도 위험. 자동 cap 보정 + 운영 관찰 SQL 추가.
confidence calibration (services/study/session_summary_guard):
- calibrate_confidence(c, ctx_docs_count, ready_explanation_count) 신규
· ctx_docs_count == 0 AND ready_explanation_count == 0 → 'low' cap
· ctx_docs_count == 0 (ready 만 있음) → 'medium' cap
· ctx_docs_count >= 1 → 모델 값 그대로
- 모델이 정의보다 더 보수적인 값 박은 경우 (모델 'low' + cap 'medium') 는
보존 — 더 보수적인 값을 절대 올리지 않음
worker 적용 (study_session_analysis_worker):
- ctx_docs_count = len(ctx_docs)
- ready_explanation_count = sum(1 for a in prompt_attempts if a.get('ai_explanation'))
- calibrate_confidence 호출 → study_quiz_session_analysis.confidence 박힘
- job.payload 에 운영 분석 metadata 보존:
· ctx_docs_count / ready_explanation_count
· model_confidence_raw (모델 응답) vs calibrated_confidence (cap 후)
· prompt_attempts / valid_attempts_total / summary_len
→ SQL 4 번 쿼리가 cap 작동 빈도 측정
scripts/phase4_health.sql (신규 운영 점검 SQL 7 섹션):
1. 4-A study_question_jobs status × error_code 분포
2. 4-B study_quiz_session_jobs status × error_code 분포
3. 4-B confidence 분포 (calibrated)
4. 4-B model_confidence_raw vs calibrated 차이 (cap 작동 빈도)
5. 4-A/4-B 최근 7일 처리 지연 p50/p95/max/avg
6. 4-A/4-B skipped 사유 분포
7. 4-B guard_fail / parse_fail / llm_timeout 비율
ship gate (단위 테스트):
- test_calibrate_confidence_no_evidence_caps_to_low (3 케이스)
- test_calibrate_confidence_only_explanations_caps_to_medium (3 케이스)
- test_calibrate_confidence_with_documents_passthrough (3 케이스)
- test_calibrate_confidence_normalizes_invalid_first (2 케이스)
Plan: ~/.claude/plans/nifty-sparking-spindle.md (Phase 4-B v1 후속)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
69 lines
2.4 KiB
Python
69 lines
2.4 KiB
Python
"""Phase 4-B v1 환각 가드 단일 source — worker + 단위 테스트가 같은 정규식을 import.
|
|
|
|
GUARD_PATTERN: AI 응답 본문에서 차단해야 할 패턴.
|
|
- % 기호 (정답률 N% 추정 위험)
|
|
- "최근 N일" / "지난 N일" (추세 표현)
|
|
- "X~Y 문항|개|문제" (범위 추천 — "5~10문항")
|
|
- "N회차" (회차 카운트 추정)
|
|
- 날짜 표현 (YYYY-MM-DD / N월 N일)
|
|
|
|
prompt 박힌 정량 정수 (`오답 1건`, `모르겠음 83건`) 는 통과.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
GUARD_PATTERN = re.compile(
|
|
r"(\d+\s*%" # 정답률 16%, 50% 등
|
|
r"|최근\s*\d+\s*일" # "최근 5일"
|
|
r"|지난\s*\d+\s*일" # "지난 7일"
|
|
r"|\d+\s*~\s*\d+\s*(문항|개|문제)" # "5~10문항"
|
|
r"|\d+\s*회차" # "7회차"
|
|
r"|\d{4}-\d{2}-\d{2}" # "2026-05-02"
|
|
r"|\d+\s*월\s*\d+\s*일" # "5월 2일"
|
|
r")"
|
|
)
|
|
|
|
_VALID_CONFIDENCE = {"high", "medium", "low"}
|
|
_CONFIDENCE_ORD = {"low": 0, "medium": 1, "high": 2}
|
|
|
|
|
|
def normalize_confidence(value: object) -> str:
|
|
"""모델이 'unknown'/'mid'/'maybe' 같은 비표준 값 박는 케이스 방어.
|
|
|
|
표준 (high/medium/low) 외 값은 'low' 로 보정 (보수적).
|
|
"""
|
|
if not isinstance(value, str):
|
|
return "low"
|
|
v = value.strip().lower()
|
|
return v if v in _VALID_CONFIDENCE else "low"
|
|
|
|
|
|
def calibrate_confidence(
|
|
confidence: object,
|
|
*,
|
|
ctx_docs_count: int,
|
|
ready_explanation_count: int,
|
|
) -> str:
|
|
"""Phase 4-D: 자료 부족 토픽에서 모델이 high 박는 과신 방지.
|
|
|
|
cap 정책 (보수적):
|
|
- 문서 evidence 0건 + ready ai_explanation 0건 → 'low' 로 cap
|
|
- 문서 evidence 0건 (ready ai_explanation 만 있음) → 'medium' 으로 cap
|
|
- 그 외 (문서 evidence 1건 이상) → 모델이 박은 값 그대로
|
|
|
|
모델이 정의보다 더 보수적인 값 박은 경우 (예: 모델 'low' 인데 cap 'medium')
|
|
는 그대로 유지 — 더 보수적인 값을 절대 올리지 않음.
|
|
"""
|
|
base = normalize_confidence(confidence)
|
|
if ctx_docs_count == 0 and ready_explanation_count == 0:
|
|
cap = "low"
|
|
elif ctx_docs_count == 0:
|
|
cap = "medium"
|
|
else:
|
|
return base
|
|
if _CONFIDENCE_ORD[base] > _CONFIDENCE_ORD[cap]:
|
|
return cap
|
|
return base
|