- engine_gateway/gateway.py: 회기당 claude -p 상주 프로세스(stream-json), 턴 직렬, budget 제한 - 세션 생성/턴/종료 HTTP API(FastAPI, :9099) - 검증: 멀티턴 컨텍스트 유지 + prompt caching 재사용(턴2 +$0.07) 실동작 확인
285 lines
18 KiB
Python
285 lines
18 KiB
Python
"""발화 라벨 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.0.0" # 라벨 코드 불변 보장 버전. 새 라벨 추가 시 minor++.
|
|
|
|
|
|
# ════════════════════════════════════════════════════════════════════════════
|
|
# 1. 회기 단계 (stage) — 결정론 상태머신이 소유 (MASTERPLAN §2.2)
|
|
# ════════════════════════════════════════════════════════════════════════════
|
|
class Stage(str, Enum):
|
|
"""상담 회기 단계. FastAPI 백엔드가 결정론적으로 전이(LLM 아님)."""
|
|
|
|
RAPPORT = "라포" # 라포 형성 — 안전감·관계 구축, 비밀보장 구조화
|
|
EXPLORE = "탐색" # 호소문제·정서·인지·위험요인 탐색
|
|
INTERVENE = "개입" # 직면·해석·인지재구성·교정적 정서체험
|
|
CLOSE = "정리" # 요약·과제·다음 회기 연결·종결
|
|
|
|
|
|
# ════════════════════════════════════════════════════════════════════════════
|
|
# 2. 화자 (speaker) — 발화의 최소 원자 출처
|
|
# ════════════════════════════════════════════════════════════════════════════
|
|
class Speaker(str, Enum):
|
|
"""발화 화자. 0615 축어록의 '상N'/'내N' 라벨에 대응."""
|
|
|
|
COUNSELOR = "counselor" # 상담자(수련생 또는 상담사 AI)
|
|
CLIENT = "client" # 내담자(가상내담자 AI)
|
|
|
|
|
|
# ════════════════════════════════════════════════════════════════════════════
|
|
# 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: "내담자 의견 확인", "주호소 문제 재확인")
|
|
|
|
# ── INTERVENTION ──
|
|
CONFRONTATION = "confrontation" # 직면 (0615: "기초자료를 활용하여 반응의 불일치에 직면시킴")
|
|
INTERPRETATION = "interpretation" # 해석 (0615: "모순의 의미를 해석함", "비언어적 자극 해석", "행동의 재해석")
|
|
|
|
# ── STABILIZING ──
|
|
STABILIZATION = "stabilization" # 안정화 (0615: "안정화")
|
|
HOPE_INSTILLATION = "hope_instillation" # 희망고취 (0615: "동기부여, 희망고취", "희망 고취, 욕구 반영")
|
|
REINFORCEMENT = "reinforcement" # 강화 (0615: "강화")
|
|
NORMALIZATION = "normalization" # 정상화 (0615: "감정 반응을 노출하는 것을 정상화함")
|
|
|
|
# ── 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,
|
|
}
|
|
|
|
# 한글 표시명(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: "과제 부여",
|
|
}
|
|
|
|
|
|
# ════════════════════════════════════════════════════════════════════════════
|
|
# 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: "방어가 서서히 풀어지고 있음")
|
|
|
|
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: "방어 완화",
|
|
}
|
|
|
|
|
|
# ════════════════════════════════════════════════════════════════════════════
|
|
# 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",
|
|
"TechniqueCategory",
|
|
"Technique",
|
|
"TECHNIQUE_CATEGORY",
|
|
"TECHNIQUE_KO",
|
|
"ClientState",
|
|
"CLIENT_STATE_KO",
|
|
"CommentKind",
|
|
"SupervisorComment",
|
|
"Annotation",
|
|
]
|