"""발화 라벨 taxonomy + annotation 데이터 모델 (0615 파란색 라벨 → 구조화 스키마). 근거: - 데이터/README.md: 0615 축어록 파란색(#3057B9) 주석 = 3종 혼재 (기법 태그 / 내담자 상태 태그 / 슈퍼바이저 논평). - MASTERPLAN.md §3.1: 0615.hwpx 직접 파싱 결과 파랑 라벨 89개·77종, 한 셀에 3종 혼재 -> 학습 신호 오염 방지를 위해 **3개 분리 필드로 격상**이 스키마 설계의 출발점. - MASTERPLAN.md §3.2: technique_label_def / client_state_def / stage_def 코드테이블 + turn_technique / turn_client_state 다대다 + supervisor_comment(kind: rationale|critique). - MEMORY_KNOWLEDGE_PERSONA_DESIGN.md §3.6: kb.chunk.label_id 가 taxonomy 라벨을 FK 참조. 이 모듈은 평가 AI 정답셋(golden label) + 페르소나 상태전이 정답의 **단일 코드 원천**이다. DB 코드테이블(technique_label_def 등)과 동기화 대상 -> seed 시 enum.value 를 code 컬럼에 적재. 설계 원칙: - 위계적(hierarchical): 기법은 군집(category) 아래에 둔다 -> 평가 분포·집계·과다/과소 판정용. - 조작적 정의(operational definition)는 docs/taxonomy.md 가 SoT, 여기엔 enum + 메타만. - 라벨은 **버전드**(코드 불변, 새 라벨 추가는 append-only) -> 골든셋 재현성 보존. 원본(0615) 자체는 절대 적재하지 않는다(미성년·자살사고, .gitignore data/raw). 합성 변형만 예시화. """ from __future__ import annotations from dataclasses import dataclass, field from enum import Enum from typing import Optional TAXONOMY_VERSION = "1.1.0" # 라벨 코드 불변 보장 버전. 새 라벨 추가 시 minor++. # 1.1.0 (2026-07-15): 연구팀 태깅 축어록 4종(첫회기 BPS·게슈탈트 3회기·자해 DBT·우울 CBT) # 태그 정합을 위해 Technique 9종·ClientState 9종 append-only 추가. 기존 코드 불변. # ════════════════════════════════════════════════════════════════════════════ # 1. 회기 단계 (stage) — 라벨 enum 단일 정의(SoT). 전이 로직은 상태머신 소유(MASTERPLAN §2.2). # services.state_machine 이 이 Stage 를 re-export 한다(중복 정의 제거). # ════════════════════════════════════════════════════════════════════════════ class Stage(str, Enum): """상담 회기 단계 라벨(SoT). 결정론 전이는 services.state_machine 이 소유(LLM 아님).""" RAPPORT = "라포" # 라포 형성 — 안전감·관계 구축, 비밀보장 구조화 EXPLORE = "탐색" # 호소문제·정서·인지·위험요인 탐색 INTERVENE = "개입" # 직면·해석·인지재구성·교정적 정서체험 CLOSE = "정리" # 요약·과제·다음 회기 연결·종결 # ════════════════════════════════════════════════════════════════════════════ # 2. 화자 (speaker) — 발화의 최소 원자 출처 # ════════════════════════════════════════════════════════════════════════════ class Speaker(str, Enum): """발화 화자. 0615 축어록의 '상N'/'내N' 라벨에 대응.""" COUNSELOR = "counselor" # 상담자(수련생 또는 상담사 AI) CLIENT = "client" # 내담자(가상내담자 AI) def speaker_ko_label(speaker: str | None) -> str: """화자 코드 → 프롬프트용 한글 라벨(SoT). counselor→상담자, 그 외→내담자.""" return "상담자" if speaker == Speaker.COUNSELOR.value else "내담자" # ════════════════════════════════════════════════════════════════════════════ # 3. 기법 군집 (TechniqueCategory) — 위계 상위층 # ════════════════════════════════════════════════════════════════════════════ class TechniqueCategory(str, Enum): """상담자 기법의 상위 군집. 평가 분포·과다/과소 집계·리뷰 사이드 게이지의 축.""" RELATIONAL = "relational" # 관계 형성: 공감·반영·타당화·홀딩·자기공개·유머 EXPLORATORY = "exploratory" # 탐색: 촉진질문·탐색·위험사정·동의/동기 확인 INTERVENTION = "intervention" # 개입: 직면·해석·인지재구성·교정적 정서체험 STABILIZING = "stabilizing" # 안정/지지: 안정화·희망고취·강화·정상화 STRUCTURING = "structuring" # 구조화: 상담원칙/비밀보장 설명·과제·심리교육 # ════════════════════════════════════════════════════════════════════════════ # 4. 기법 라벨 (Technique) — 위계 하위층 (0615 파란색 기법 태그 origin) # ════════════════════════════════════════════════════════════════════════════ class Technique(str, Enum): """발화별 상담 기법 라벨(복수 부착 가능). 값(value)=DB code, 0615 표면 라벨에서 정규화. 조작적 정의·예시는 docs/taxonomy.md 참조. 여기 값은 안정적 머신 코드. """ # ── RELATIONAL ── EMPATHY = "empathy" # 공감 (0615: "공감") REFLECTION = "reflection" # 반영 (0615: "반영", "정서 반영", "내담자의 반응을 반영함") VALIDATION = "validation" # 타당화 (0615: "타당화", "감정 노출의 타당화") HOLDING = "holding" # 홀딩 — 침묵 견디기/기다려 줌 (0615: "홀딩", "탐색과 홀딩") SELF_DISCLOSURE = "self_disclosure" # 자기공개 (0615: "라포형성을 위한 상담자의 자기 개방") AFFECT_CONVEYANCE = "affect_conveyance" # 상담자 정서 전달 (0615: "상담자의 정서 전달") HUMOR = "humor" # 유머 (0615: "유머") # ── EXPLORATORY ── EXPLORATION = "exploration" # 탐색 (0615: "탐색", "탐색질문") FACILITATIVE_QUESTION = "facilitative_question" # 촉진/개방 질문 (0615: "촉진", "참여 촉진 질문") RISK_ASSESSMENT = "risk_assessment" # 위험요인/자해·자살 탐색 (0615: "위험요인 탐색", "자해 위험 및 경험 탐색") CONSENT_MOTIVATION_CHECK = "consent_motivation_check" # 동의/동기 확인 (0615: "동의확인, 동기수준 확인") OPINION_CHECK = "opinion_check" # 내담자 의견·반응 확인 (0615: "내담자 의견 확인", "주호소 문제 재확인") # 1.1.0 추가 (연구팀 태깅 자료 정합) CLARIFICATION = "clarification" # 명료화 질문 (cbt: "이해를 명확하게 하기 위해 질문", ch3 다수) SCALING = "scaling" # 감정/증상 수치화·척도 질문 (cbt: 감정 강도 0~100) CHAIN_ANALYSIS = "chain_analysis" # 행동 체인분석 — 사건→감정→행동 사슬 탐색 (dbt) # ── INTERVENTION ── CONFRONTATION = "confrontation" # 직면 (0615: "기초자료를 활용하여 반응의 불일치에 직면시킴") INTERPRETATION = "interpretation" # 해석 (0615: "모순의 의미를 해석함", "비언어적 자극 해석", "행동의 재해석") # 1.1.0 추가 COGNITIVE_RESTRUCTURING = "cognitive_restructuring" # 인지재구성 — 이분법·파국화 확인, 연속선 사고, 핵심신념 작업 (cbt 55건) BEHAVIORAL_ALTERNATIVE = "behavioral_alternative" # 대안행동 탐색·계획 (cbt) COMMITMENT_STRATEGY = "commitment_strategy" # 서약/전념 전략 — 악마의 옹호자, 방해물 예상 (dbt) HERE_AND_NOW_FOCUS = "here_and_now_focus" # 지금-여기 초점화 (gestalt: 현전·트래킹) # ── STABILIZING ── STABILIZATION = "stabilization" # 안정화 (0615: "안정화") HOPE_INSTILLATION = "hope_instillation" # 희망고취 (0615: "동기부여, 희망고취", "희망 고취, 욕구 반영") REINFORCEMENT = "reinforcement" # 강화 (0615: "강화") NORMALIZATION = "normalization" # 정상화 (0615: "감정 반응을 노출하는 것을 정상화함") # 1.1.0 추가 RESTATEMENT = "restatement" # 재진술 — 내용을 되돌려 확인 (cbt: 재진술 후 내담자 확인 대기) SKILLS_COACHING = "skills_coaching" # 기술 코칭 — 고통감내·위기대처 기술 안내 (dbt) # ── STRUCTURING ── PRINCIPLE_EXPLANATION = "principle_explanation" # 상담원칙/비밀보장 설명 (0615: "상담원칙에 대한 설명", "비밀보장 제외 원칙 설명") PSYCHOEDUCATION = "psychoeducation" # 심리교육/보편교범 전달 (0615: "보편적 교범 전달, 주관적 해석과 구별") HOMEWORK = "homework" # 과제 부여 (0615: "다음 회기까지 해 와야 하는 과제를 설명") # 기법 -> 군집 매핑 (위계). 집계·분포·과다/과소 판정에 사용. TECHNIQUE_CATEGORY: dict[Technique, TechniqueCategory] = { Technique.EMPATHY: TechniqueCategory.RELATIONAL, Technique.REFLECTION: TechniqueCategory.RELATIONAL, Technique.VALIDATION: TechniqueCategory.RELATIONAL, Technique.HOLDING: TechniqueCategory.RELATIONAL, Technique.SELF_DISCLOSURE: TechniqueCategory.RELATIONAL, Technique.AFFECT_CONVEYANCE: TechniqueCategory.RELATIONAL, Technique.HUMOR: TechniqueCategory.RELATIONAL, Technique.EXPLORATION: TechniqueCategory.EXPLORATORY, Technique.FACILITATIVE_QUESTION: TechniqueCategory.EXPLORATORY, Technique.RISK_ASSESSMENT: TechniqueCategory.EXPLORATORY, Technique.CONSENT_MOTIVATION_CHECK: TechniqueCategory.EXPLORATORY, Technique.OPINION_CHECK: TechniqueCategory.EXPLORATORY, Technique.CONFRONTATION: TechniqueCategory.INTERVENTION, Technique.INTERPRETATION: TechniqueCategory.INTERVENTION, Technique.STABILIZATION: TechniqueCategory.STABILIZING, Technique.HOPE_INSTILLATION: TechniqueCategory.STABILIZING, Technique.REINFORCEMENT: TechniqueCategory.STABILIZING, Technique.NORMALIZATION: TechniqueCategory.STABILIZING, Technique.PRINCIPLE_EXPLANATION: TechniqueCategory.STRUCTURING, Technique.PSYCHOEDUCATION: TechniqueCategory.STRUCTURING, Technique.HOMEWORK: TechniqueCategory.STRUCTURING, # 1.1.0 추가 Technique.CLARIFICATION: TechniqueCategory.EXPLORATORY, Technique.SCALING: TechniqueCategory.EXPLORATORY, Technique.CHAIN_ANALYSIS: TechniqueCategory.EXPLORATORY, Technique.COGNITIVE_RESTRUCTURING: TechniqueCategory.INTERVENTION, Technique.BEHAVIORAL_ALTERNATIVE: TechniqueCategory.INTERVENTION, Technique.COMMITMENT_STRATEGY: TechniqueCategory.INTERVENTION, Technique.HERE_AND_NOW_FOCUS: TechniqueCategory.INTERVENTION, Technique.RESTATEMENT: TechniqueCategory.RELATIONAL, Technique.SKILLS_COACHING: TechniqueCategory.STABILIZING, } # 한글 표시명(UI 라벨 칩·리뷰 화면). docs/taxonomy.md 와 일치. TECHNIQUE_KO: dict[Technique, str] = { Technique.EMPATHY: "공감", Technique.REFLECTION: "반영", Technique.VALIDATION: "타당화", Technique.HOLDING: "홀딩", Technique.SELF_DISCLOSURE: "자기공개", Technique.AFFECT_CONVEYANCE: "정서 전달", Technique.HUMOR: "유머", Technique.EXPLORATION: "탐색", Technique.FACILITATIVE_QUESTION: "촉진질문", Technique.RISK_ASSESSMENT: "위험사정", Technique.CONSENT_MOTIVATION_CHECK: "동의·동기 확인", Technique.OPINION_CHECK: "의견 확인", Technique.CONFRONTATION: "직면", Technique.INTERPRETATION: "해석", Technique.STABILIZATION: "안정화", Technique.HOPE_INSTILLATION: "희망고취", Technique.REINFORCEMENT: "강화", Technique.NORMALIZATION: "정상화", Technique.PRINCIPLE_EXPLANATION: "상담원칙 설명", Technique.PSYCHOEDUCATION: "심리교육", Technique.HOMEWORK: "과제 부여", # 1.1.0 추가 Technique.CLARIFICATION: "명료화", Technique.SCALING: "수치화 질문", Technique.CHAIN_ANALYSIS: "체인분석", Technique.COGNITIVE_RESTRUCTURING: "인지재구성", Technique.BEHAVIORAL_ALTERNATIVE: "대안행동 탐색", Technique.COMMITMENT_STRATEGY: "전념 전략", Technique.HERE_AND_NOW_FOCUS: "지금-여기 초점화", Technique.RESTATEMENT: "재진술", Technique.SKILLS_COACHING: "기술 코칭", } # ════════════════════════════════════════════════════════════════════════════ # 5. 내담자 상태 태그 (ClientState) — 0615 파란색 상태 태그 origin # ════════════════════════════════════════════════════════════════════════════ class ClientState(str, Enum): """내담자 발화/비언어에 부착되는 상태 태그(복수 가능). 페르소나 상태전이(저항 엔진) 정답 신호 + 평가 AI 의 '반응 읽기' 채점 근거. 0615 파란색 상태 태그에서 정규화. """ INVOLUNTARY = "involuntary" # 비자발적 태도 (0615: "(아니오)는 비자발적 태도", "(쓴웃음)은 비자발적 태도") DEFENSIVE = "defensive" # 방어 (0615: "방어", "부모자녀 관계에 대한 방어적 태도") SUICIDAL_IDEATION_ADMIT = "suicidal_ideation_admit" # 자살사고 인정 (0615: "자살사고 인정") NEGATIVE_SELF_PERCEPTION = "negative_self_perception" # 부정적 자기인식 (0615: "부정적 자기인식", "부정적인 자기인식") CONFLICTED = "conflicted" # 갈등 상태 (0615: "갈등 상태") LACK_OF_CONFIDENCE = "lack_of_confidence" # 확신/자신감 부족 (0615: "확신의 부족") AFFECT_CONTACT = "affect_contact" # 정서와 접촉/표현 (0615: "정서와 접촉함", "정서와 사고의 표현") THOUGHT_ORGANIZING = "thought_organizing" # 생각을 정리함 (0615: "생각을 정리함") RESPONDS_TO_EXPLORATION = "responds_to_exploration" # 탐색에 반응함 (0615: "상담자의 탐색에 반응함") EXPRESSES_PLAN = "expresses_plan" # 자기 계획/욕구 표현 (0615: "자신의 계획을 표현함") DEFENSE_LOOSENING = "defense_loosening" # 방어가 서서히 풀림 (0615: "방어가 서서히 풀어지고 있음") # 1.1.0 추가 (연구팀 태깅 자료 정합) COMPLIANT_SURFACE = "compliant_surface" # 표면 순응 — 겉으로 동의하나 내면 미변화 (ch2) EXTERNALIZING = "externalizing" # 문제 외재화 — "아이가 학교만 가면"/"문제는 아내" (ch2·cbt) SEEKS_GUIDANCE = "seeks_guidance" # 상담자 의견/지시를 구함 (cbt) APPARENT_COMPETENCE = "apparent_competence" # 겉보기 유능함 — 회기 내 유능, 위기 시 붕괴 (dbt) ACTIVE_PASSIVITY = "active_passivity" # 적극적 수동성 — 해결을 타인에게 위임 (dbt) SELF_HARM_DISCLOSURE = "self_harm_disclosure" # 자해 보고 — 자살사고 인정과 구분 (dbt) SOMATIC_COMPLAINT = "somatic_complaint" # 신체증상 호소 — 숨막힘·두근거림 (ch2) AFFECT_MASKING = "affect_masking" # 감정 가림 — 웃음·무덤덤함으로 덮기 (ch3) FOCUS_DRIFT_FUSION = "focus_drift_fusion" # 초점 이탈/융합 — 자기 문제가 타인 이야기로 회귀 (ch2·ch3) CLIENT_STATE_KO: dict[ClientState, str] = { ClientState.INVOLUNTARY: "비자발적 태도", ClientState.DEFENSIVE: "방어", ClientState.SUICIDAL_IDEATION_ADMIT: "자살사고 인정", ClientState.NEGATIVE_SELF_PERCEPTION: "부정적 자기인식", ClientState.CONFLICTED: "갈등 상태", ClientState.LACK_OF_CONFIDENCE: "확신 부족", ClientState.AFFECT_CONTACT: "정서 접촉/표현", ClientState.THOUGHT_ORGANIZING: "생각 정리", ClientState.RESPONDS_TO_EXPLORATION: "탐색에 반응", ClientState.EXPRESSES_PLAN: "계획/욕구 표현", ClientState.DEFENSE_LOOSENING: "방어 완화", # 1.1.0 추가 ClientState.COMPLIANT_SURFACE: "표면 순응", ClientState.EXTERNALIZING: "문제 외재화", ClientState.SEEKS_GUIDANCE: "지시 요청", ClientState.APPARENT_COMPETENCE: "겉보기 유능함", ClientState.ACTIVE_PASSIVITY: "적극적 수동성", ClientState.SELF_HARM_DISCLOSURE: "자해 보고", ClientState.SOMATIC_COMPLAINT: "신체증상 호소", ClientState.AFFECT_MASKING: "감정 가림", ClientState.FOCUS_DRIFT_FUSION: "초점 이탈/융합", } # ════════════════════════════════════════════════════════════════════════════ # 6. 슈퍼바이저 논평 (SupervisorComment) — 0615 논평 origin # ════════════════════════════════════════════════════════════════════════════ class CommentKind(str, Enum): """슈퍼바이저 논평 종류 (MASTERPLAN §3.1, §3.2: rationale vs critique 분리). 윤찬 지시: '의도와 다른 부분'(intent_deviation)은 1급 시민. """ RATIONALE = "rationale" # 근거/의도 설명 — 왜 이 반응이 적절한가 (0615 회색 '·' 논평 + 일부 파랑) CRITIQUE = "critique" # 개선점/주의 — 과도/부족/평가적 시각 등 (0615: "과도한 자기개방일 수 있음") @dataclass(slots=True) class SupervisorComment: """발화에 달린 슈퍼바이저 논평 한 건. critique 인 경우 intent_deviation 으로 '의도(expected) vs 실제(actual)' 격차를 구조화. 예) 0615 "비언어적 반응의 반영이 더 되었으면" -> dimension=reflection, severity=minor. """ kind: CommentKind text: str # critique 전용(선택): 평가 차원·기대·실제·심각도. README/§3.2 intent_deviation JSONB 대응. dimension: Optional[str] = None # 관련 평가 차원 (예: "reflection", "self_disclosure", "pacing") expected: Optional[str] = None # 권장된 반응/의도 actual: Optional[str] = None # 실제 나타난 반응 severity: Optional[str] = None # "minor" | "moderate" | "major" author: str = "ai" # "ai"(자동 1R) | "supervisor"(교수 검수 2R) # ════════════════════════════════════════════════════════════════════════════ # 7. 발화 annotation (Annotation) — 3축 분리 최종 레코드 (golden 단위) # ════════════════════════════════════════════════════════════════════════════ @dataclass(slots=True) class Annotation: """발화 1개의 구조화 annotation = golden_schema.jsonl 한 줄에 대응. README 스키마: {"발화ID", "화자", "단계", "발화텍스트", "기법라벨"[], "내담자상태"[], "슈퍼바이저논평"} 를 3축 분리(기법/내담자상태/논평)로 격상한 머신 표현. 부착 규칙(평가 AI 정답셋 일관성): - techniques 는 speaker=COUNSELOR 발화에만 부착(내담자 발화엔 빈 리스트). - client_states 는 speaker=CLIENT 발화(또는 비언어 표현)에만 부착. - comments 는 두 화자 모두 가능(슈퍼비전 관점). - nonverbal: 괄호 표기 비언어 단서("(쓴웃음)","(침묵 10초)") -> 검증/상태판정 보조. """ utterance_id: str # 예 "0615-C-001"(상1), "0615-K-003"(내3). 화자+seq 인코딩 speaker: Speaker stage: Stage text: str # 발화 텍스트 (예시는 합성 변형, 원문 미적재) seq: int # 회기 내 발화 순서(0-based) techniques: list[Technique] = field(default_factory=list) # 기법 라벨(복수) client_states: list[ClientState] = field(default_factory=list) # 내담자 상태(복수) comments: list[SupervisorComment] = field(default_factory=list) # 슈퍼바이저 논평(복수) nonverbal: list[str] = field(default_factory=list) # 비언어 단서 원문 표기 source: str = "0615_synthetic" # provenance: 0615 합성 변형(원문 미적재, F-05) def __post_init__(self) -> None: # 정합성: 기법은 상담자, 상태는 내담자에만 (평가 신호 오염 방지). if self.techniques and self.speaker is not Speaker.COUNSELOR: raise ValueError( f"techniques 는 상담자 발화에만 부착 가능: {self.utterance_id}" ) if self.client_states and self.speaker is not Speaker.CLIENT: raise ValueError( f"client_states 는 내담자 발화에만 부착 가능: {self.utterance_id}" ) def technique_categories(self) -> set[TechniqueCategory]: """부착된 기법들의 군집 집합 (분포 집계용).""" return {TECHNIQUE_CATEGORY[t] for t in self.techniques} __all__ = [ "TAXONOMY_VERSION", "Stage", "Speaker", "speaker_ko_label", "TechniqueCategory", "Technique", "TECHNIQUE_CATEGORY", "TECHNIQUE_KO", "ClientState", "CLIENT_STATE_KO", "CommentKind", "SupervisorComment", "Annotation", ]