feat(study): Phase 4-D 운영 관찰 + confidence calibration
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>
This commit is contained in:
@@ -12,7 +12,11 @@ from pathlib import Path
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(ROOT / "app"))
|
||||
|
||||
from services.study.session_summary_guard import GUARD_PATTERN, normalize_confidence # noqa: E402
|
||||
from services.study.session_summary_guard import ( # noqa: E402
|
||||
GUARD_PATTERN,
|
||||
calibrate_confidence,
|
||||
normalize_confidence,
|
||||
)
|
||||
|
||||
|
||||
# ─── GUARD_PATTERN 허용 케이스 (search() == None 이어야 함) ───
|
||||
@@ -63,10 +67,43 @@ def test_normalize_confidence_nonstandard_values():
|
||||
assert normalize_confidence(v) == "low"
|
||||
|
||||
|
||||
# ─── Phase 4-D calibrate_confidence — 자료 부족 토픽 cap ───
|
||||
|
||||
def test_calibrate_confidence_no_evidence_caps_to_low():
|
||||
# 문서 0 + ready ai_explanation 0 → cap 'low'
|
||||
assert calibrate_confidence("high", ctx_docs_count=0, ready_explanation_count=0) == "low"
|
||||
assert calibrate_confidence("medium", ctx_docs_count=0, ready_explanation_count=0) == "low"
|
||||
assert calibrate_confidence("low", ctx_docs_count=0, ready_explanation_count=0) == "low"
|
||||
|
||||
|
||||
def test_calibrate_confidence_only_explanations_caps_to_medium():
|
||||
# 문서 0 + ready ai_explanation 있음 → cap 'medium'
|
||||
assert calibrate_confidence("high", ctx_docs_count=0, ready_explanation_count=3) == "medium"
|
||||
assert calibrate_confidence("medium", ctx_docs_count=0, ready_explanation_count=3) == "medium"
|
||||
assert calibrate_confidence("low", ctx_docs_count=0, ready_explanation_count=3) == "low"
|
||||
|
||||
|
||||
def test_calibrate_confidence_with_documents_passthrough():
|
||||
# 문서 1건 이상 → 모델 값 그대로 통과
|
||||
assert calibrate_confidence("high", ctx_docs_count=2, ready_explanation_count=0) == "high"
|
||||
assert calibrate_confidence("medium", ctx_docs_count=1, ready_explanation_count=0) == "medium"
|
||||
assert calibrate_confidence("low", ctx_docs_count=5, ready_explanation_count=10) == "low"
|
||||
|
||||
|
||||
def test_calibrate_confidence_normalizes_invalid_first():
|
||||
# 비표준 값은 normalize_confidence 가 'low' 로 박은 후 cap 적용 — 결과는 'low'
|
||||
assert calibrate_confidence("unknown", ctx_docs_count=0, ready_explanation_count=0) == "low"
|
||||
assert calibrate_confidence(None, ctx_docs_count=5, ready_explanation_count=5) == "low"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 직접 실행 시 모든 케이스 빠른 점검
|
||||
test_guard_pattern_allows_normal_summary()
|
||||
test_guard_pattern_blocks_numeric_hallucination()
|
||||
test_normalize_confidence_standard_values()
|
||||
test_normalize_confidence_nonstandard_values()
|
||||
test_calibrate_confidence_no_evidence_caps_to_low()
|
||||
test_calibrate_confidence_only_explanations_caps_to_medium()
|
||||
test_calibrate_confidence_with_documents_passthrough()
|
||||
test_calibrate_confidence_normalizes_invalid_first()
|
||||
print("OK")
|
||||
|
||||
Reference in New Issue
Block a user