318 lines
25 KiB
Markdown
318 lines
25 KiB
Markdown
# 한신대 실사용 피드백 개선 계획
|
|
|
|
작성일: 2026-07-03
|
|
상태: 1차 구현 완료 / 레드팀·대레드팀 반영
|
|
근거 자료: 한신대 실사용 오류분석 DOCX/HWP, 저장소 정적 조사, 서브에이전트 4축 조사, 레드팀 2축 적대 검토, 대레드팀 2축 균형 검토
|
|
|
|
> 이 문서는 구현 계획이다. 현재 상태·검증 수치·로드맵 권위 기준은 `docs/dev_dashboard.html`이고, 열린 작업 인덱스는 `docs/TODO.md`가 소유한다.
|
|
|
|
## 1. 결론
|
|
|
|
한신대 자료에서 나온 문제는 단일 원인이 아니다. 가장 큰 축은 다음 네 가지다.
|
|
|
|
1. **대화 컨텍스트가 엔진 입력까지 안정적으로 전달되지 않는 구조**
|
|
2. **내담자 응답 출력 품질 게이트 부재**
|
|
3. **리뷰 화면에서 저장/AI용 마스킹·평가 오류 코드가 그대로 노출되는 UX 문제**
|
|
4. **교수자에게 설명해야 할 작동 원리와 남은 임상팀 게이트가 제품 안에서 충분히 드러나지 않는 문제**
|
|
|
|
구현 순서는 대화 컨텍스트 안정화가 먼저다. 다만 레드팀 검토 결과, 컨텍스트 보존은 단독 패치가 아니라 **role mapping 수정과 gateway history 소비 변경을 같은 패치에서 원자적으로 처리**해야 한다. 현재 매핑이 잘못된 상태에서 history만 보존하면 상담자 발화를 내담자 과거 발화처럼 먹이는 역효과가 난다.
|
|
|
|
표시 UX도 같은 원칙이다. `[NAME]`을 없애 보이게 만드는 것이 목표가 아니다. **API·저장값·평가 입력·export의 privacy proof는 마스킹 토큰을 유지**하고, 사람에게 보이는 UI 표시층만 별도로 부드럽게 만드는 것이 목표다.
|
|
|
|
대레드팀 검토 결과, 레드팀 경고는 유지하되 사용자 가치 기준으로 등급화한다. **P1 내부의 role mapping + history injection은 원자 패치**로 유지한다. 반면 **P2/P3/P4는 독립적으로 빠르게 배포 가능한 1차 개선**으로 분리한다. 안전 조건이 “최종형을 한 번에 닫는 것”으로 비대해지면 한신대 사용자가 당장 겪은 불편을 줄이는 본래 목적을 잃는다.
|
|
|
|
2026-07-03 1차 구현 결과:
|
|
|
|
- P1: `persona.build_turn_messages()` role mapping을 `counselor -> user`, `client -> assistant`로 수정하고, gateway는 `ai_role=client` 요청에만 `[직전 대화]` history를 주입한다. evaluator/structured 요청은 history를 받지 않는다.
|
|
- P2: 역할 메타발화, 반복 인사, 직전 내담자 near-duplicate를 저장 전 차단한다. generate는 1회 재생성 후 실패 시 client turn을 저장하지 않고 `client_reply_quality_retryable`로 반환한다. SSE stream은 응답 전체를 buffer한 뒤 품질 가드 통과 시에만 token을 방출한다.
|
|
- P3: 리뷰 일반 transcript 표시층만 `[NAME]`/`[ORG]` 등을 자연어화하고, quote/evidence/API/export 마스킹 증거는 유지한다. raw 평가 오류는 학습자/교수자 UI에서 재시도 가능 문구로 매핑한다. fast-loop 턴 노트와 deep-loop 요약 라벨을 구분하고 `clientFeedback` 라벨을 “마지막 내담자 반응”으로 바꿨다.
|
|
- P4: 교수자 리뷰 카드 안에 “AI 평가 범위” 패널을 추가해 현재 구현과 임상팀 확정 필요 범위를 분리했다.
|
|
- 검증: backend focused 84 passed, `npm run typecheck`, `npm run check:api-types`, session-review focused E2E 8 passed, `layout-visual-gate.spec.ts` 12 passed, `session-layout.spec.ts` 8 passed.
|
|
|
|
## 2. 안전 조건과 MVP 등급
|
|
|
|
레드팀 차단 조건은 사용자 가치 기준으로 등급화한다.
|
|
|
|
| 영역 | 등급 | 적용 |
|
|
|---|---|---|
|
|
| P1 컨텍스트 | 1차 하드 게이트 | `persona.build_turn_messages()` role mapping 수정과 gateway client-only history injection은 같은 패치로 처리한다. history 보존만 먼저 배포하지 않는다. |
|
|
| Gateway 계약 | 1차 하드 게이트 | `_split_messages()` 변경은 기존 테스트 계약 변경이다. 다만 P1 차단 테스트는 persona role mapping, gateway client-only history injection, evaluator no-history 계약으로 한정한다. |
|
|
| Evaluator 분리 | 1차 하드 게이트 | gateway history 직렬화는 `ai_role=client` 요청에만 적용한다. evaluator/deep-loop/structured output 요청은 client history를 받지 않는다. |
|
|
| Resident session | 2차 안정화 | 1차 구현은 stateless history injection으로 한정한다. resident session 재사용/prompt caching 복구는 client/eval namespace, cleanup, restart recovery 설계 뒤에만 켠다. |
|
|
| History window | 1차 단순 구현 + 2차 정교화 | 1차는 최근 대화를 라벨링해 주입하되, 가능하면 완성 turn pair를 우선한다. K-pair window 정교화는 P1 차단 조건이 아니라 2차 안정화다. |
|
|
| Quality fallback | 1차는 저장하지 않음 | 1차 품질 게이트는 fallback 저장을 목표로 하지 않는다. 재생성 후에도 역할 메타발화가 남으면 client turn을 저장하지 않고 재시도 가능한 오류로 표시한다. 저장형 fallback은 synthetic/fallback 메타데이터와 평가 제외 규칙이 준비된 뒤 도입한다. |
|
|
| Stream | 1차 안전 구현 완료 + 2차 UX 정교화 | SSE stream은 전체 응답을 buffer한 뒤 품질 가드 통과 시에만 token을 방출한다. 나쁜 출력은 token leak 없이 `client_reply_quality_retryable`로 끝낸다. 2차는 지연을 줄이는 안전한 부분 버퍼링/초기문장 정책이다. |
|
|
| Review privacy | 1차 하드 게이트 + UX 허용 | 마스킹 토큰 보존은 API·저장·export 계약이다. 리뷰 화면의 일반 transcript 표시는 동일 데이터를 렌더링 단계에서 자연어화할 수 있다. |
|
|
| Error copy | 1차 UX | DB/schema급 정비 전이라도 raw error를 학습자 UI에 직접 노출하지 않는 display mapper를 먼저 둔다. |
|
|
| fast-loop 라벨 | 1차 UX | “이 턴 직후 기준” 라벨은 완전한 reconciliation이 아니라 즉시 혼란을 줄이는 1차 개선이다. 후속 해명 반영 배지는 deep-loop 확장으로 분리한다. |
|
|
| 임상 설명 | 1차 신뢰 회복 | 교수자 설명은 “못 한다”가 아니라 현재 제품이 하는 것과 임상팀 확정이 필요한 것을 분리해 답한다. 확정되지 않은 진단·처방·루브릭 표현만 금지한다. |
|
|
| SSOT | 항상 유지 | 상태와 검증 수치는 `docs/dev_dashboard.html`만 소유하고, TODO/가이드/계획서는 링크와 요약만 둔다. |
|
|
|
|
## 3. 현상 정리
|
|
|
|
| 현상 | 분류 | 1차 판단 |
|
|
|---|---|---|
|
|
| 내담자가 같은 말을 2~3번 반복 | 대화 컨텍스트 / 출력 품질 | 최근 턴 보존과 중복 응답 게이트 필요 |
|
|
| 상담자가 질문했는데 내담자가 답하지 않고 다시 상담자 순서 | 대화 컨텍스트 / 턴 처리 | 엔진 입력 맥락과 스트림 완료 저장 경로 재검증 필요 |
|
|
| 이미 인사했는데 다시 인사 | 출력 품질 | 2턴 이후 반복 인사 차단 가능 |
|
|
| “내담자 역할로 응답하겠습니다” | 출력 품질 | 역할 메타발화 하드 차단 필요 |
|
|
| 문맥 오해 후 나중에 해명했는데 피드백은 이전 기준처럼 보임 | 평가 UX | fast-loop는 해당 턴 직후 기준임을 표시하고, 회기 전체 평가는 별도 라벨 필요 |
|
|
| `[NAME]`이 리뷰 화면에 표시 | 표시 계층 / privacy proof | API 증거는 유지하고 UI 표시층만 자연어화 |
|
|
| `no_structured_output` 평가 실패 | 평가 UX / 운영 관측 | 내부 오류 코드와 사용자 설명 문구 분리 |
|
|
| 진단기준·CBT·개방도 산식 질문 | 설명 가능성 / 임상 게이트 | 구현된 구조와 임상팀 확정 잔여를 분리해 설명 |
|
|
|
|
## 4. 근거가 된 코드 관찰
|
|
|
|
### 4.1 대화 컨텍스트
|
|
|
|
- `apps/api/engine_gateway/gateway.py`의 `_split_messages()`는 system 메시지를 합치고 마지막 `user`만 남긴다. 중간 `assistant`/`user` 히스토리는 현재 payload에서 사라진다.
|
|
- `_split_messages()`의 기존 테스트는 “마지막 user만 보낸다”를 계약처럼 고정하고 있다. 변경 시 테스트 추가가 아니라 계약 갱신이다.
|
|
- `apps/api/engine_gateway/gateway.py`의 `_resolve_session()`은 같은 `session_id`가 살아 있으면 새 system prompt를 무시하고 resident session을 재사용한다.
|
|
- `apps/api/app/routes/sessions.py`의 `start_session()`은 DB/app 세션은 만들지만 gateway `/session`을 호출해 resident session을 만들지는 않는다.
|
|
- `apps/api/app/services/orchestrator.py`는 앱 세션 ID를 `GenerateRequest`/`StreamRequest`의 `session_id`로 넘긴다. 그러나 이 ID는 gateway resident ID와 명시 매핑되어 있지 않다.
|
|
- `apps/api/app/services/persona.py`는 `recent_turns`를 `EngineMessage`로 추가하지만, 현재 gateway split 구조에서는 그 히스토리가 소비되지 않는다.
|
|
- 같은 파일의 recent turn role 매핑은 주석과 반대로 보인다. 내담자 AI 관점에서는 `counselor -> user`, `client -> assistant`가 맞다.
|
|
|
|
### 4.2 출력 품질
|
|
|
|
- `persona.py`의 L0 프롬프트는 “끝까지 내담자”, “메타 대화 금지”, “말투 유지”를 지시한다.
|
|
- `guardrail.py`의 `sanitize_client_reply()`는 자살수단 차단, ideation cap, 일부 placeholder 자연어화 중심이다.
|
|
- 역할 메타발화, 반복 인사, 직전 답변 중복, 너무 이른 회복/감사 표현은 실행 후 검사로 막고 있지 않다.
|
|
- generate 경로는 저장 전 전체 응답을 보고 정제할 수 있다. stream 경로는 이미 token을 내보내는 구조라 첫 문장 또는 일정 문자 수 버퍼링을 넣을 경우 화면/저장/평가 텍스트 동등성을 별도 검증해야 한다.
|
|
- voice 경로는 stream이 아니다. generate 전체 응답 후 TTS를 시작하므로 검증 포인트는 버퍼링보다 generate 지연, TTS 시작, persistence 실패 시 learner-only 기록 방지다.
|
|
|
|
### 4.3 평가와 리뷰
|
|
|
|
- fast-loop 평가는 “해당 상담자 발화 + 이어진 내담자 응답 + 제한된 직전 맥락” 중심의 턴 직후 평가다.
|
|
- deep-loop 평가는 회기 종료 후 전체 축어록을 본다.
|
|
- fast-loop 평가는 해당 학습자 발화 직후의 스냅샷이며 후속 해명을 소급 반영하지 않는다. 회기 전체 판단은 deep-loop 또는 교수자 검토에서 별도 표시한다.
|
|
- 리뷰 read-model은 턴 텍스트에 `text_masked`를 사용하므로 `[NAME]`이 표시 화면까지 올라올 수 있다.
|
|
- 평가 오류는 `no_structured_output`, `engine_error`, `parse_error` 같은 내부 코드가 사용자에게 그대로 보일 수 있다.
|
|
- `clientFeedback`는 실제 별도 피드백 산출물이 아니라 마지막 client turn으로 읽히는 경로라, 라벨과 포함 조건을 재검토해야 한다.
|
|
|
|
## 5. 실행 계획
|
|
|
|
### P0. 재현과 계측
|
|
|
|
목표: 고친 것처럼 보이는 패치를 막고, 한신대 자료의 주요 현상을 테스트로 고정한다.
|
|
|
|
작업:
|
|
|
|
- gateway 단위 테스트로 새 `_split_messages()` 계약을 고정한다.
|
|
- persona 메시지 테스트로 `counselor -> user`, `client -> assistant` 매핑을 검증한다.
|
|
- sessions/orchestrator fake engine 테스트로 generate/stream 주요 경로의 gateway payload에 최근 대화가 들어가는지 검증한다.
|
|
- evaluator/structured output 요청에는 client history가 들어가지 않는지 검증한다.
|
|
- 리뷰 read-model/API 테스트로 raw PII 미포함과 masked token 보존을 동시에 검증한다.
|
|
- fast-loop 오류 테스트로 내부 오류 코드가 학습자용 문구로 매핑되는지 검증한다.
|
|
- P2 1차는 fallback 저장이 아니라 명백한 출력 결함 감지/재시도/미저장 오류 응답을 검증한다.
|
|
|
|
검증 후보:
|
|
|
|
- `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_evaluator_model_routing.py -q; Pop-Location`
|
|
- 통합 검증 단계: `app/test_session_turn_persistence.py`, `app/test_voice_ws.py`, session-review/session-persistence focused E2E
|
|
- `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`
|
|
|
|
### P1. 대화 컨텍스트 안정화
|
|
|
|
목표: 반복, 재인사, 문맥 오해, 질문 맥락 미반영을 일으킬 수 있는 구조적 원인을 먼저 닫는다.
|
|
|
|
작업:
|
|
|
|
1. `persona.build_turn_messages()`의 recent turn role 매핑을 수정한다.
|
|
- `counselor -> user`
|
|
- `client -> assistant`
|
|
|
|
2. gateway `_split_messages()`가 client history를 버리지 않게 한다.
|
|
- 단기 구현은 `ai_role=client` 요청에만 non-system history를 current user payload 앞에 `[직전 대화]` 블록으로 직렬화한다.
|
|
- evaluator/deep-loop/structured output 요청에는 client history 직렬화를 적용하지 않는다.
|
|
- 1차는 최근 대화를 라벨링해 주입하고, 가능하면 완성 turn pair를 우선한다.
|
|
- 예시:
|
|
- `상담자: 왜 무슨 일 있어?`
|
|
- `내담자: 엄마가 가보라고 해서요...`
|
|
- `[이번 상담자 발화] ...`
|
|
|
|
3. resident session 매핑은 2차로 미룬다.
|
|
- 바로 도입하면 evaluator/deep-loop가 client resident를 재사용할 위험이 있다.
|
|
- `client:{session_id}`, `eval:{session_id}` 네임스페이스, 프로세스 정리, gateway 재시작 복구가 함께 필요하다.
|
|
|
|
검증:
|
|
|
|
- gateway payload에 직전 상담자 질문과 내담자 답변이 정확히 1회 포함되는지 unit test.
|
|
- generate/stream fake engine에서 최근 대화 주입 확인.
|
|
- fast/deep evaluator 요청이 client history resident를 재사용하지 않는지 회귀 테스트.
|
|
- `_split_messages()` 기존 골든을 새 계약으로 갱신하고, 계약 변경을 문서화한다.
|
|
|
|
### P2. 내담자 출력 품질 게이트
|
|
|
|
목표: 모델이 역할을 깨거나 반복·재인사를 출력했을 때 저장/표시 전에 막는다.
|
|
|
|
1차 하드 차단:
|
|
|
|
- 역할 메타발화
|
|
- 예: “내담자 역할로 응답하겠습니다”, “AI로서”, “상담자 입장에서”, “제 핵심신념은”
|
|
- 반복 인사
|
|
- 2턴 이후 “안녕하세요”, “처음 뵙겠습니다”, “반갑습니다”로 시작하는 응답
|
|
|
|
1차 보수적 차단 또는 재생성:
|
|
|
|
- 직전 내담자 발화와 거의 같은 응답
|
|
- exact/subsequence/높은 유사도만 차단한다.
|
|
- 너무 이른 회복/감사 표현
|
|
- 라포 초기, 낮은 openness, 회복/종결 뉘앙스가 함께 있을 때만 retryable로 둔다.
|
|
|
|
구현 원칙:
|
|
|
|
- retry는 1회만 허용한다.
|
|
- 1차에서는 fallback 저장을 하지 않는다. 재시도 후에도 실패하면 client turn을 저장하지 않고 재시도 가능한 오류로 표시한다.
|
|
- 저장형 fallback은 2차에서만 도입한다. 이때 `synthetic`/`fallback` 메타데이터를 남기고 평가·워크시트·carry-over·`clientFeedback` 기본 입력에서 제외한다.
|
|
- 사용자 화면에는 내부 raw reason 대신 “응답을 안정적으로 생성하지 못했어. 다시 시도해줘.”처럼 학습자용 문구를 표시한다.
|
|
- stream 첫 문장 또는 80~120자 버퍼링 전면 도입은 2차 안정화로 둔다. 1차는 generate 경로와 명백한 출력 결함 차단부터 닫는다.
|
|
- 품질 게이트 reason은 audit 또는 운영자용 payload에 남긴다. 단, 사용자에게 내부 raw reason을 그대로 노출하지 않는다.
|
|
|
|
검증:
|
|
|
|
- `guardrail` 또는 신규 품질 모듈의 table-driven unit test.
|
|
- generate fake engine retry 1회 테스트.
|
|
- 재생성 후 실패 시 client turn 미저장 + 재시도 가능 오류 응답 테스트.
|
|
- stream/voice 버퍼링·persistence 경로는 통합 검증 또는 2차 안정화에서 별도 테스트.
|
|
- 필요 시 Playwright로 역할 메타발화가 화면에 보이지 않는지 focused 확인.
|
|
|
|
### P3. 리뷰·평가 UX 개선
|
|
|
|
목표: 안전한 저장/평가 경로를 유지하면서 사람에게 보이는 리뷰는 자연스럽고 설명 가능하게 만든다.
|
|
|
|
작업:
|
|
|
|
1. 리뷰 표시 계층을 분리한다.
|
|
- 저장값, 상세 API, 평가 입력, 데이터셋 export, privacy proof에는 `[NAME]`/`[ORG]`/`[PHONE]` 유지.
|
|
- 리뷰 화면의 일반 transcript 표시는 동일 데이터를 렌더링 단계에서 자연어화할 수 있다.
|
|
- 축어록, 근거 quote, worksheet evidence에는 마스킹 토큰 보존을 기본으로 둔다.
|
|
- `humanize_pii_placeholders()`는 내담자 응답 표시용 helper라는 현재 목적을 넘어서 남용하지 않는다.
|
|
|
|
2. 내부 오류 코드와 사용자 문구를 분리한다.
|
|
- `errorKind`: `no_structured_output`, `engine_error`, `parse_error`
|
|
- 학습자용 문구:
|
|
- “자동 평가를 완료하지 못했어. 다시 시도할 수 있어.”
|
|
- 교수자/운영자용 문구:
|
|
- 오류 종류와 raw detail을 별도 접힘/관리자 영역에서 확인
|
|
- DB/log에는 운영 진단용 raw error를 유지한다.
|
|
|
|
3. 평가 범위 라벨을 명확히 한다.
|
|
- 턴 노트: “이 턴 직후 기준”
|
|
- 회기 전체 평가: “회기 전체 기준”
|
|
- 사용자가 후속 해명을 했더라도 fast-loop 카드가 소급 변경되지 않는다는 점을 UI에 반영한다.
|
|
- 라벨은 완전한 reconciliation이 아니라 즉시 혼란을 줄이는 1차 UX 개선이다. deep-loop 또는 교수자 검토와의 구분을 같이 보여준다.
|
|
|
|
4. `clientFeedback` 라벨을 조정한다.
|
|
- 완전한 의미 재설계 전에도 “내담자가 남긴 것” 대신 “마지막 내담자 반응”처럼 사실에 가까운 표현으로 바꾼다.
|
|
|
|
2차 후보:
|
|
|
|
- deep-loop schema에 `turn_reconciliations`를 추가한다.
|
|
- fast-loop 노트 옆에 “후속 회복됨 / 부분 회복 / 미해결” 배지를 붙인다.
|
|
- 이 작업은 임의 휴리스틱으로 처리하지 않는다. 평가 신뢰도가 떨어질 수 있다.
|
|
|
|
검증:
|
|
|
|
- `/review` JSON은 raw PII 미포함 + masked token 보존.
|
|
- UI 표시만 humanized 되는지 별도 테스트.
|
|
- raw 오류 코드 미노출.
|
|
- `clientFeedback` 표시 라벨 변경.
|
|
- session-review E2E의 평가 실패 문구 기대값 갱신.
|
|
- 저장/상세 API/평가 입력/export에서는 마스킹이 유지되는지 별도 테스트로 보존.
|
|
- 공유 payload도 원문 축어록과 학습자 식별 정보를 싣지 않는지 확인.
|
|
|
|
### P4. 교수자 설명 패널과 SSOT 반영
|
|
|
|
목표: 한신대 질문에 제품 안에서 답하고, 구현 완료와 임상팀 게이트를 섞지 않는다. 설명 패널은 “못 한다”는 방어문이 아니라 현재 구현, 교육적 의도, 임상팀 미검수 범위를 분리해 설명하는 신뢰 회복 장치다.
|
|
|
|
교수자 질문별 답변 초안:
|
|
|
|
1. 진단기준이 입력됐는가?
|
|
- 진단명을 붙이는 용도는 아니다.
|
|
- DSM-5-TR 기반 요약과 페르소나별 증상 차원은 seed persona/DB catalog에 연결되어 있다.
|
|
- seed persona 기준인지 DB catalog 기준인지 UI와 문서에서 구분한다.
|
|
- 내담자 발화나 학습자 피드백에는 진단명·정답·내부 수치를 직접 노출하지 않고, 관찰 가능한 말·정서·행동으로 번역한다.
|
|
|
|
2. 약물치료나 심리평가 권유에 왜 주저하는가?
|
|
- Vignette는 교육용 시뮬레이션이지 처방·진단·공식 평가 대체 도구가 아니다.
|
|
- 약물/검사 권유는 바로 제시하기보다 라포, 동의, 안전, 기능 손상 확인을 먼저 하도록 설계한다.
|
|
- 약물 권유 기준·문안은 임상팀 확정 전 자동화하지 않는다.
|
|
|
|
3. CBT는 어디서 오는가?
|
|
- 계약 원천문서 요구, 페르소나 `theory_target`, 세션 `theory_mode`, 평가 프롬프트에 연결되어 있다.
|
|
- 현재는 CBT 반응 프레이밍 수준이다.
|
|
- 임상팀 확정 CBT 단계 프롬프트 체인, citation 구조, 이론부합 루브릭은 아직 남은 게이트다.
|
|
|
|
4. 개방도 점수는 어떻게 계산되는가?
|
|
- LLM 감이 아니라 결정론 상태머신 값이다.
|
|
- “점수”보다 “시뮬레이션 상태값”으로 설명한다.
|
|
- 단계 기본값 + 라포 크레딧 * unlock rate - 저항 * decay를 0~1로 clamp한다.
|
|
- 공감·반영·개방질문은 라포를 올리고, 성급한 조언·평가·유도질문은 저항을 올린다.
|
|
|
|
반영 위치:
|
|
|
|
- 제품: `SessionReview` 교수자 검토 카드 아래 “AI 평가/코칭 작동 원리와 한계” 패널
|
|
- 권장 구조: “현재 구현” / “임상팀 확정 필요” 2열 또는 “현재 구현됨” / “제품 한계” / “임상팀 검수 필요” 3단
|
|
- 문서:
|
|
- `docs/dev_dashboard.html`: 상태·검증 수치·로드맵 권위 기준
|
|
- `docs/TODO.md`: 열린 작업 링크/요약
|
|
- 이 계획 문서: 구현 순서와 차단 조건
|
|
- `docs/redteam/hanshin-feedback-plan-redteam-2026-07-03.md`: 적대 검토 결과
|
|
- `docs/redteam/hanshin-feedback-plan-counter-redteam-2026-07-03.md`: 대레드팀 균형 검토 결과
|
|
|
|
검증:
|
|
|
|
- `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`
|
|
- `Push-Location apps/web; npx playwright test e2e/layout-visual-gate.spec.ts --project=chromium-single-run --workers=1; Pop-Location`
|
|
- `py -3.11 -X utf8 scripts\check-dev-dashboard-ssot.py --json`
|
|
|
|
## 6. 레드팀에 맡긴 질문과 반영 상태
|
|
|
|
| 질문 | 1차 반영 |
|
|
|---|---|
|
|
| 컨텍스트 보존 패치가 실제 반복/문맥오해를 줄인다는 가정이 과도하지 않은가? | 컨텍스트 보존은 필요조건일 뿐이라고 명시했다. role mapping, gateway 계약, 출력 품질 게이트, 평가 라벨을 같이 닫는다. |
|
|
| `_split_messages()`에 history를 넣으면 resident context와 중복되어 역효과가 나지 않는가? | 1차는 stateless injection으로 제한하고 resident session은 별도 설계 전까지 켜지 않는다. |
|
|
| stream 첫 문장 버퍼링이 UX 지연이나 token 순서 버그를 만들 가능성은? | 화면/저장/평가 텍스트 동등성과 차단 문구 미노출을 검증 조건으로 추가했다. |
|
|
| `[NAME]`을 표시 계층에서 자연어화하면 개인정보 보호 증거가 약해졌다고 오해될 위험은? | 리뷰 API/quote/export는 masked token을 유지하고 UI 표시만 분리한다. |
|
|
| fast-loop “이 턴 직후 기준” 라벨이 실제 사용자의 혼란을 충분히 줄이는가? | 라벨만으로 부족할 수 있음을 인정하고 deep-loop/교수자 검토와의 구분을 같이 표시한다. |
|
|
| 교수자 설명 패널이 임상팀 확정 전 내용을 과장하지 않는가? | DSM/CBT/약물/루브릭/골든셋은 임상팀 검수와 source/citation 범위 안에서만 확정 표현을 쓴다. |
|
|
| 문서와 SSOT/TODO가 중복되어 어긋날 위험은? | 상태·검증 수치는 dev_dashboard만 소유하고 계획/TODO/가이드는 링크와 요약만 둔다. |
|
|
|
|
## 7. 대레드팀 반영 상태
|
|
|
|
| 대레드팀 질문 | 반영 |
|
|
|---|---|
|
|
| 레드팀이 P2/P3/P4까지 P1처럼 묶어 본래 목적을 약화시켰는가? | P1 내부 원자성만 하드 게이트로 유지하고, P2/P3/P4는 독립 배포 가능한 1차 개선으로 분리했다. |
|
|
| P1 테스트 범위가 과도한가? | P1 차단 테스트를 persona role mapping, gateway client-only history injection, evaluator no-history 계약으로 한정했다. voice/Playwright/전체 review 회귀는 통합 검증 단계로 내렸다. |
|
|
| privacy proof가 UI 자연어화를 막는가? | API·저장·export 마스킹 불변식은 유지하되, 리뷰 화면 일반 transcript는 렌더링 단계 자연어화를 허용했다. |
|
|
| synthetic fallback 요구가 품질 게이트를 늦추는가? | 1차 품질 게이트는 fallback 저장을 하지 않는다. 재생성 실패 시 client turn 미저장 + 재시도 가능 오류로 처리한다. |
|
|
| 임상 안전 문구가 교수자 설명을 무력화하는가? | 교수자 패널은 현재 구현과 임상팀 확정 필요 범위를 분리해 답하는 신뢰 회복 장치로 먼저 배포한다. |
|
|
|
|
## 8. 현재 결정
|
|
|
|
구현 완료한 1차 범위:
|
|
|
|
- P0/P1a: persona role mapping + client-only stateless history injection + evaluator no-history focused test
|
|
- P3a: API 마스킹 불변식 보존 + 리뷰 UI 표시 자연어화 + raw error display mapper + fast/deep 라벨 + `clientFeedback` 라벨 변경
|
|
- P2a: 역할 메타발화, 2턴 이후 재인사, 직전 내담자 발화와 거의 같은 응답만 먼저 차단. fallback 저장 없이 재시도 가능 오류로 처리. SSE stream은 full-response buffer로 token leak을 막음.
|
|
- P4a: 교수자 설명 패널을 “현재 구현 / 임상팀 확정 필요” 구조로 먼저 배포
|
|
|
|
후속 설계 확인 필요:
|
|
|
|
- 지연을 줄이는 안전한 partial stream 버퍼링 방식
|
|
- 저장형 fallback synthetic 메타데이터와 평가 제외 규칙
|
|
- fast-loop/deep-loop reconciliation
|
|
- resident session namespace와 prompt caching 복구
|
|
- 교수자 설명 패널의 최종 임상 문구
|
|
|
|
임상팀 게이트:
|
|
|
|
- CBT 단계 프롬프트 체인
|
|
- 이론부합 루브릭
|
|
- 약물/심리평가/의뢰 권유 기준과 문안
|
|
- C1 루브릭 anchor·가중치·컷오프
|
|
- H2 평가 골든셋
|