한신대 피드백 개선팩 반영

This commit is contained in:
Yun Chan 2026-07-03 19:53:14 +09:00
parent 5a9c110c11
commit 6b6241f468
25 changed files with 1247 additions and 94 deletions

View file

@ -0,0 +1,130 @@
# 한신대 실사용 피드백 개선 계획 레드팀 검토
작성일: 2026-07-03
대상: `docs/ops/hanshin-feedback-improvement-plan-2026-07-03.md`
상태: 1차 적대 검토 완료 / 구현 전 차단 조건 반영 필요
## 1. 요약
계획의 방향은 맞다. 한신대 실사용 피드백을 단순 화면 문구 문제가 아니라 컨텍스트, 출력 품질, 리뷰 UX, 설명 가능성 문제로 나눈 판단은 타당하다.
다만 그대로 구현하면 위험한 지점이 있다. 특히 아래 네 가지는 구현 전 반드시 막아야 한다.
1. `persona.py` role mapping이 잘못된 상태에서 gateway history만 보존하면 품질이 악화될 수 있다.
2. `_split_messages()` 변경은 단순 보강이 아니라 gateway 계약 변경이다.
3. 리뷰 API의 마스킹 토큰을 자연어화하면 privacy proof와 기존 E2E 계약이 깨질 수 있다.
4. 품질 fallback을 실제 내담자 발화처럼 저장하면 평가·워크시트·carry-over가 오염된다.
## 2. BLOCKER
### B1. P1은 원자적으로 처리해야 한다
`persona.build_turn_messages()`의 recent turn role mapping 수정과 gateway `_split_messages()` history 소비 변경은 같은 패치에서 처리해야 한다. 현재 매핑이 잘못된 상태에서 history만 보존하면 상담자 발화를 내담자 과거 발화처럼 소비할 수 있다.
반영 문장:
> P1-1 role mapping 수정과 P1-2 gateway history 소비는 같은 패치에서 원자적으로 처리한다. history 보존만 먼저 배포하지 않는다.
### B2. Gateway history 보존은 계약 변경이다
현재 gateway 테스트는 “이전 assistant/user는 버리고 마지막 user만 보낸다”를 명시적으로 고정하고 있다. 따라서 `_split_messages()` 변경은 테스트 추가가 아니라 기존 계약과 골든 변경이다.
필수 조치:
- 기존 gateway split 테스트 기대값 갱신
- 호출부가 어떤 `ai_role`에서 history를 소비하는지 명시
- evaluator/structured output 요청에서 client history가 섞이지 않는지 회귀 테스트
### B3. Resident session은 1차 범위에서 꺼야 한다
gateway `_resolve_session()`은 같은 `session_id`가 살아 있으면 새 system prompt를 무시하고 재사용한다. evaluator도 같은 app session id를 보낼 수 있으므로 resident session을 성급히 켜면 client 컨텍스트와 evaluator 컨텍스트가 섞일 수 있다.
반영 문장:
> 이번 1차 구현은 stateless history injection으로 한정하며, resident session 재사용/prompt caching 복구는 별도 설계 없이는 켜지 않는다.
### B4. Fallback은 실제 임상 반응이 아니다
품질 게이트 실패 후 “말을 고르며 잠시 멈춘다” 같은 fallback을 실제 내담자 발화로 저장하면 deep-loop, 회기 요약, 다음 회기 carry-over, `clientFeedback`, worksheet evidence가 오염된다.
필수 조치:
- fallback 저장 시 `synthetic`/`fallback` 메타데이터 부여
- 평가·워크시트·carry-over 기본 입력에서 제외
- 학습자 UI에는 내부 reason 대신 안전한 표시 문구 제공
### B5. 리뷰 API 마스킹 토큰은 보존해야 한다
리뷰/상세 API는 privacy proof로 `[NAME]`, `[ORG]`, `[PHONE]` 같은 마스킹 토큰을 유지해야 한다. 사람용 UI에서 자연어화가 필요하더라도 API 축어록·근거 quote·worksheet evidence·export는 마스킹 불변식을 보존해야 한다.
반영 문장:
> 리뷰 API의 축어록·근거 quote는 privacy proof를 위해 `[NAME]` 등 마스킹 토큰을 보존한다. 자연어 치환은 UI 표시 전용 필드 또는 컴포넌트 렌더링에서만 적용한다.
### B6. 교수자 설명은 임상 확정처럼 쓰면 안 된다
DSM/CBT/약물·심리평가/루브릭/골든셋 설명은 “현재 구현됨”과 “임상팀 검수 필요”를 같은 톤으로 쓰면 안 된다. 특히 CBT는 현재 `theory_mode`와 evaluator prompt 연결 수준이지, 임상팀 확정 CBT 단계 chain/citation/rubric 구조가 아니다.
반영 문장:
> DSM/CBT/약물·검사/루브릭/골든셋 설명은 임상팀 검수 상태와 source/citation이 연결된 범위에서만 확정 표현을 쓴다.
## 3. MAJOR
- gateway history 직렬화는 `ai_role=client` 요청에만 적용해야 한다.
- `recent_turns(k=6)`은 레코드 수 기준이라 상담자/내담자 pair를 보장하지 않는다. 최근 완성 K쌍 기준으로 자른다.
- stream 버퍼링을 넣으면 사용자가 본 텍스트, 저장된 텍스트, 평가된 텍스트가 달라질 수 있다.
- voice 경로는 stream이 아니므로 generate 지연, TTS 시작, persistence 실패 시 learner-only 기록 방지를 따로 검증해야 한다.
- fast-loop 라벨만으로 “후속 해명을 반영하지 않았다”는 혼란이 닫히지 않을 수 있다. deep-loop/교수자 검토와 구분을 같이 보여줘야 한다.
- `clientFeedback`는 실제 피드백 산출물이 아니라 마지막 client turn이므로 라벨과 포함 조건을 재검토해야 한다.
- 오류 문구 개선은 단순 문자열 치환이 아니라 `errorKind`, role별 display copy, operator raw error 계약 분리여야 한다.
- 공유 payload에도 원문 축어록과 학습자 식별 정보가 들어가지 않는지 확인해야 한다.
## 4. MINOR
- 검증 명령은 Windows PowerShell 5.1 기준으로 `Push-Location ...; ...; Pop-Location` 또는 `py -3.11 -X utf8` 형태로 적는다.
- “컨텍스트 보존이 반복/문맥오해를 줄인다”는 표현은 필요조건으로 낮춘다. 출력 게이트와 평가 라벨까지 같이 닫아야 한다.
- 개방도는 “점수”보다 “시뮬레이션 상태값”으로 설명한다.
- seed persona 기준인지 DB catalog 기준인지 교수자 설명에서 구분한다.
- 학습자용 오류 문구는 “평가 AI 응답 형식이 맞지 않아”보다 “자동 평가를 완료하지 못했어”처럼 내부 구현을 덜 드러내는 표현이 낫다.
## 5. 구현 전 테스트 제안
Backend:
- `Push-Location apps/api; py -3.11 -X utf8 -m pytest -p no:cacheprovider engine_gateway/test_gateway_model.py app/test_orchestrator_masking.py app/test_session_turn_persistence.py app/test_evaluator_model_routing.py app/test_voice_ws.py -q; Pop-Location`
- 신규: `persona.build_turn_messages()``counselor -> user`, `client -> assistant`를 보장하는 단위 테스트.
- 신규: gateway `_split_messages()`가 client history를 라벨 포함 payload로 정확히 1회만 넣고 evaluator 요청에는 넣지 않는 테스트.
- 신규: REST/stream/voice fake engine 캡처 테스트에서 직전 상담자 질문과 내담자 답변이 실제 gateway user payload까지 도달하는지 검증.
- 신규: evaluator fast/deep이 client resident/history를 재사용하지 않는 회귀 테스트.
- 신규: quality fallback turn이 synthetic으로 표시되고 `clientFeedback`, worksheet evidence, deep eval에 무표시로 섞이지 않는지 검증.
Review/privacy:
- 신규: `/review` JSON은 raw PII 미포함 + masked token 보존, UI 표시만 humanized 되는지 검증.
- 신규: `no_structured_output`/`engine_error`/`parse_error`가 role별 display copy로 매핑되고 raw error는 learner UI에 안 나오는지 검증.
- 신규: 리뷰 API/detail/eval/export 경로에서 `[NAME]` 마스킹 불변식 보존.
- 신규: 공유 payload에 원문 축어록과 학습자 식별 정보가 실리지 않는지 검증.
Frontend:
- `Push-Location apps/web; npm run typecheck; Pop-Location`
- `Push-Location apps/web; npx playwright test e2e/session-review.spec.ts --project=chromium-desktop --workers=1; Pop-Location`
- DB/engine 가능 시 `Push-Location apps/web; npx playwright test e2e/session-persistence.spec.ts --project=chromium-single-run --workers=1 --grep "Korean PII|manual AI evaluation retry|case worksheet|selected CBT theory mode"; Pop-Location`
SSOT:
- `py -3.11 -X utf8 scripts\check-dev-dashboard-ssot.py --json`
## 6. 판정
계획은 진행 가능하다. 단, P1 내부는 표면 개선으로 쪼개면 위험하다. 대레드팀 검토 후 1차 구현 단위는 다음처럼 조정한다.
- P1: role mapping + client-only stateless history injection + evaluator 분리 focused test
- P2 1차: fallback 저장 없이 명백한 출력 결함 감지 + 1회 재생성 + 실패 시 client turn 미저장
- P3 1차: API 마스킹 불변식 + 리뷰 UI 렌더링 자연어화 + raw error display mapper + fast/deep 라벨
- P4 1차: 현재 구현과 임상팀 확정 필요 범위를 분리한 교수자 설명 패널
교수자 설명 패널은 제품 신뢰를 높일 수 있지만, 임상팀 확정 전 표현은 보수적으로 써야 한다.
후속 균형 검토: `docs/redteam/hanshin-feedback-plan-counter-redteam-2026-07-03.md`