세션 평가·라이브코치·교수자 분석 라운드 마감 + 문서 정리 + 코드품질 리팩터

- 누적 작업트리 커밋: 회기 평가 복구·durable 저장, 라이브 코치 이력/근거, 교수자 학생분석, 음성 비언어 메타, PII 마스킹, 운영 티켓/헬스 등
- 문서: 완료 기록 docs/archive/ 냉동 보관, docs/ 단일 인덱스(docs/README.md)+통합 TODO(docs/TODO.md)로 정리
- 리팩터(행위 보존): Stage enum SSOT(taxonomy 소유·state_machine re-export), store recent/masked_turns 중복 제거, speaker_ko_label 단일 헬퍼, _list_sessions N+1 제거(state/turns 배치 + 턴평가 하이드레이션 배치)
- 검증: 백엔드 pytest 352 passed, _list_sessions E2E chromium-single-run 2 passed
This commit is contained in:
Yun Chan 2026-07-02 02:50:36 +09:00
parent 7c41c3ce79
commit 778e8526d4
108 changed files with 6457 additions and 455 deletions

View file

@ -52,6 +52,7 @@ from ..taxonomy import (
CommentKind,
Technique,
TechniqueCategory,
speaker_ko_label,
)
from . import guardrail
@ -482,7 +483,7 @@ def build_fast_messages(ctx: "TurnContext", client_reply: str) -> list[EngineMes
theory = _theory_mode(ctx)
client_reply_masked = guardrail.mask_pii(client_reply).text_masked
recent = "\n".join(
f"{('상담자' if t.get('speaker') == 'counselor' else '내담자')}: {t.get('text', '')}"
f"{speaker_ko_label(t.get('speaker'))}: {t.get('text', '')}"
for t in (ctx.memory.recent_turns or [])[-4:]
) or "(직전 맥락 없음)"
@ -537,7 +538,7 @@ def build_deep_messages(
) -> list[EngineMessage]:
"""deep-loop 평가 프롬프트(전체 회기 + 코드 집계 분포 + 골든라벨 후보)."""
transcript = "\n".join(
f"{t.get('seq', '')}{('상담자' if t.get('speaker') == 'counselor' else '내담자')}: {t.get('text', '')}"
f"{t.get('seq', '')}{speaker_ko_label(t.get('speaker'))}: {t.get('text', '')}"
for t in masked_turns
) or "(축어록 없음)"
dist_lines = ", ".join(f"{k}:{v}" for k, v in distribution.by_category.items()) or "(없음)"
@ -736,11 +737,11 @@ async def evaluate_turn(
inference_geo=resp.inference_geo,
latency_ms=latency_ms,
)
except EngineError as e:
base.error = f"engine_error: {e}"
except EngineError:
base.error = "engine_error"
return base
except Exception as e: # 방어 — 어떤 예외도 상담 루프를 막지 않게
base.error = f"eval_error: {e}"
except Exception: # 방어 — 어떤 예외도 상담 루프를 막지 않게
base.error = "eval_error"
return base
payload = structured_payload_from_response(resp)
@ -751,8 +752,8 @@ async def evaluate_turn(
result = _parse_fast(payload, turn_seq=st.turn_seq, stage=st.stage.value, theory=theory)
_evaluator_cache_put(cache_key, result.model_dump())
return result
except Exception as e: # 파싱 방어
base.error = f"parse_error: {e}"
except Exception: # 파싱 방어
base.error = "parse_error"
return base