feat(coach): AI 코칭 버튼 및 버블 팝업 대화형 조언 기능 구현
This commit is contained in:
parent
8a34dd2afc
commit
70d7cb34a3
36 changed files with 4521 additions and 1264 deletions
|
|
@ -133,6 +133,7 @@ class LiveCoachInput(BaseModel):
|
|||
persona_code: str
|
||||
persona_name: str
|
||||
learner_text: str
|
||||
question: Optional[str] = None
|
||||
client_reply: Optional[str] = None
|
||||
recent_turns: list[dict[str, str]] = Field(default_factory=list)
|
||||
evaluation: Optional[dict[str, Any]] = None
|
||||
|
|
@ -546,12 +547,33 @@ def _fallback_suggestion(
|
|||
)
|
||||
|
||||
crisis = guardrail.classify_crisis(text)
|
||||
q = (item.question or "").strip()
|
||||
if crisis.kind != guardrail.CrisisKind.NONE:
|
||||
tone = "warn"
|
||||
focus = "risk"
|
||||
title = "안전 먼저"
|
||||
message = "위험 단서가 나온 턴이다. 방법을 캐묻지 말고 최근성, 강도, 보호요인을 차분히 확인해라."
|
||||
next_line = "그 생각이 최근에 얼마나 자주, 얼마나 강하게 올라오는지 안전을 위해 같이 확인해도 될까요?"
|
||||
elif q and any(k in q for k in ("저항", "방어", "닫", "안 열", "거부")):
|
||||
focus = "rapport"
|
||||
title = "저항 수용과 안전감"
|
||||
message = "내담자의 방어는 자연스러운 안전 전략입니다. 방어를 깨뜨리려 하지 말고 그 부담감을 먼저 수용해 주세요."
|
||||
next_line = "지금 이 이야기를 꺼내는 것 자체가 많이 조심스럽고 부담되셨을 것 같아요."
|
||||
elif q and any(k in q for k in ("공감", "라포", "위로", "마음", "감정")):
|
||||
focus = "emotion"
|
||||
title = "공감적 감정 명명"
|
||||
message = "내담자가 겪고 있는 혼란스러운 마음에 이름을 붙여주고, 타당화하는 반영을 시도하세요."
|
||||
next_line = "혼자서 그 모든 감정을 감당하느라 많이 지치고 외로우셨을 것 같습니다."
|
||||
elif q and any(k in q for k in ("탐색", "질문", "원인", "호소", "사건")):
|
||||
focus = "exploration"
|
||||
title = "열린 초점 탐색"
|
||||
message = "구체적인 촉발 사건과 그때의 신체 반응이나 생각을 묻는 열린 질문을 던져보세요."
|
||||
next_line = "그런 생각이 들었을 때, 몸에서는 어떤 느낌이나 신호가 먼저 느껴졌나요?"
|
||||
elif q and any(k in q for k in ("다음", "추천", "어떻게", "뭐라", "발화")):
|
||||
focus = "exploration"
|
||||
title = "다음 개입 추천"
|
||||
message = f"현재 [{item.stage}] 단계 흐름에 맞춰 내담자의 감정을 먼저 수용하고 열린 질문으로 이어가세요."
|
||||
next_line = stage_next_lines.get(str(item.stage), "그 상황에서 가장 크게 느껴졌던 마음은 어떤 것이었나요?")
|
||||
elif any(word in text for word in ("해야", "해봐", "괜찮아", "그냥", "왜 안")):
|
||||
tone = "warn"
|
||||
focus = "rapport"
|
||||
|
|
@ -656,6 +678,11 @@ def _messages(item: LiveCoachInput, grounding: list[LiveCoachGrounding]) -> list
|
|||
)
|
||||
or "(이번 회기 첫 코칭)"
|
||||
)
|
||||
question_block = (
|
||||
f"[상담자의 질문/요청]\n{_mask_prompt_text(item.question)}\n(상담자의 질문에 집중하여 조언과 추천 발화를 생성한다)\n\n"
|
||||
if item.question
|
||||
else ""
|
||||
)
|
||||
user = (
|
||||
f"[세션] {item.session_id} / turn {item.turn_seq}\n"
|
||||
f"[내담자] {item.persona_name} ({item.persona_code})\n"
|
||||
|
|
@ -663,6 +690,7 @@ def _messages(item: LiveCoachInput, grounding: list[LiveCoachGrounding]) -> list
|
|||
f"[이번 회기 목표 단계] {goals} — 코칭은 목표 단계 작업에 정렬하고, 목표를 이미 이뤘다면 심화를 제안한다.\n"
|
||||
f"[직전 코칭]\n{prior}\n(같은 조언을 반복하지 말고 다음 단계를 제시한다)\n\n"
|
||||
f"[최근 맥락]\n{recent}\n\n"
|
||||
f"{question_block}"
|
||||
f"[이번 상담자 발화]\n{learner_masked}\n\n"
|
||||
f"[이어진 내담자 응답]\n{client_masked or '(아직 없음)'}\n\n"
|
||||
f"[fast-loop 평가 신호]\n{evaluation}\n\n"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue