8.6 KiB
한신대 실사용 피드백 개선 계획 레드팀 검토
작성일: 2026-07-03
대상: docs/ops/hanshin-feedback-improvement-plan-2026-07-03.md
상태: 1차 적대 검토 완료 / 구현 전 차단 조건 반영 필요
1. 요약
계획의 방향은 맞다. 한신대 실사용 피드백을 단순 화면 문구 문제가 아니라 컨텍스트, 출력 품질, 리뷰 UX, 설명 가능성 문제로 나눈 판단은 타당하다.
다만 그대로 구현하면 위험한 지점이 있다. 특히 아래 네 가지는 구현 전 반드시 막아야 한다.
persona.pyrole mapping이 잘못된 상태에서 gateway history만 보존하면 품질이 악화될 수 있다._split_messages()변경은 단순 보강이 아니라 gateway 계약 변경이다.- 리뷰 API의 마스킹 토큰을 자연어화하면 privacy proof와 기존 E2E 계약이 깨질 수 있다.
- 품질 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:
- 신규:
/reviewJSON은 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-LocationPush-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