전 저장소 리팩터링과 SSOT 정비
This commit is contained in:
parent
14ecbd4e7d
commit
3dfddcac6f
173 changed files with 19679 additions and 6952 deletions
|
|
@ -136,6 +136,10 @@ class LiveCoachInput(BaseModel):
|
|||
client_reply: Optional[str] = None
|
||||
recent_turns: list[dict[str, str]] = Field(default_factory=list)
|
||||
evaluation: Optional[dict[str, Any]] = None
|
||||
# 이번 회기 목표 단계(P1 준비 페이지 선택) — 코칭을 회기 목표에 정렬한다.
|
||||
goal_stages: list[str] = Field(default_factory=list)
|
||||
# 직전 코칭 요약(title/focus) — 같은 조언 반복을 막는다.
|
||||
prior_coach: list[dict[str, str]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class LiveCoachGrounding(BaseModel):
|
||||
|
|
@ -529,7 +533,17 @@ def _fallback_suggestion(
|
|||
focus: CoachFocus = "exploration"
|
||||
title = "다음 탐색"
|
||||
message = "내담자 표현을 한 번 반영한 뒤, 방금 말한 장면을 더 구체적으로 물어봐라."
|
||||
next_line = "방금 말한 그 장면이 언제부터 특히 힘들게 느껴졌는지 조금만 더 들려줄래요?"
|
||||
# 단계별 기본 다음 발화 — 폴백에서도 회기 흐름에 맞는 제안을 낸다.
|
||||
stage_next_lines = {
|
||||
"라포": "오늘 이렇게 시간 내줘서 고마워요. 지금 마음이 어떤지 편한 만큼만 들려줄래요?",
|
||||
"탐색": "방금 말한 그 장면이 언제부터 특히 힘들게 느껴졌는지 조금만 더 들려줄래요?",
|
||||
"개입": "그 생각이 올라올 때 몸이나 행동은 어떻게 반응하는지 같이 한번 살펴볼까요?",
|
||||
"정리": "오늘 나눈 이야기 중에 가장 마음에 남는 것 하나를 같이 정리해 볼까요?",
|
||||
}
|
||||
next_line = stage_next_lines.get(
|
||||
str(item.stage),
|
||||
"방금 말한 그 장면이 언제부터 특히 힘들게 느껴졌는지 조금만 더 들려줄래요?",
|
||||
)
|
||||
|
||||
crisis = guardrail.classify_crisis(text)
|
||||
if crisis.kind != guardrail.CrisisKind.NONE:
|
||||
|
|
@ -633,10 +647,21 @@ def _messages(item: LiveCoachInput, grounding: list[LiveCoachGrounding]) -> list
|
|||
"영역의 구체 행동으로 연결한다. DSM/지침 근거는 상담자 판단을 정렬하는 내부 참조이며, "
|
||||
"학습자에게는 관찰 가능한 상담 행동과 다음 발화로만 번역한다."
|
||||
)
|
||||
goals = ", ".join(item.goal_stages) if item.goal_stages else "(미지정)"
|
||||
prior = (
|
||||
"\n".join(
|
||||
f"- {entry.get('title', '')} (focus: {entry.get('focus', '')})"
|
||||
for entry in item.prior_coach[-2:]
|
||||
if entry.get("title")
|
||||
)
|
||||
or "(이번 회기 첫 코칭)"
|
||||
)
|
||||
user = (
|
||||
f"[세션] {item.session_id} / turn {item.turn_seq}\n"
|
||||
f"[내담자] {item.persona_name} ({item.persona_code})\n"
|
||||
f"[단계] {item.stage} / openness {item.effective_openness:.2f} / 이론 {item.theory_mode}\n\n"
|
||||
f"[단계] {item.stage} / openness {item.effective_openness:.2f} / 이론 {item.theory_mode}\n"
|
||||
f"[이번 회기 목표 단계] {goals} — 코칭은 목표 단계 작업에 정렬하고, 목표를 이미 이뤘다면 심화를 제안한다.\n"
|
||||
f"[직전 코칭]\n{prior}\n(같은 조언을 반복하지 말고 다음 단계를 제시한다)\n\n"
|
||||
f"[최근 맥락]\n{recent}\n\n"
|
||||
f"[이번 상담자 발화]\n{learner_masked}\n\n"
|
||||
f"[이어진 내담자 응답]\n{client_masked or '(아직 없음)'}\n\n"
|
||||
|
|
@ -654,13 +679,14 @@ def _messages(item: LiveCoachInput, grounding: list[LiveCoachGrounding]) -> list
|
|||
async def _record_llm_audit(
|
||||
audit_hook: Optional["LlmAuditHook"],
|
||||
**payload: Any,
|
||||
) -> None:
|
||||
) -> bool:
|
||||
if audit_hook is None:
|
||||
return
|
||||
return True
|
||||
try:
|
||||
await audit_hook(payload)
|
||||
result = await audit_hook(payload)
|
||||
return result is not False
|
||||
except Exception:
|
||||
return
|
||||
return False
|
||||
|
||||
|
||||
async def generate_live_coaching(
|
||||
|
|
@ -691,7 +717,7 @@ async def generate_live_coaching(
|
|||
)
|
||||
resp = await engine.generate(req)
|
||||
latency_ms = int((time.perf_counter() - started) * 1000)
|
||||
await _record_llm_audit(
|
||||
audit_ok = await _record_llm_audit(
|
||||
audit_hook,
|
||||
session_id=item.session_id,
|
||||
provider=resp.provider,
|
||||
|
|
@ -702,6 +728,12 @@ async def generate_live_coaching(
|
|||
inference_geo=resp.inference_geo,
|
||||
latency_ms=latency_ms,
|
||||
)
|
||||
if not audit_ok:
|
||||
return _fallback_suggestion(
|
||||
item,
|
||||
grounding=all_grounding,
|
||||
reason="응답 검증 기록을 남기지 못했다",
|
||||
)
|
||||
payload = structured_payload_from_response(resp)
|
||||
if payload is None:
|
||||
return _fallback_suggestion(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue