Jev 내담자 평가·표현 v2와 속마음 공개
상담자 발화 판정(A)·감정(B)·표현(C) 20문항 질문 세트, 감쇠 없는 이번 턴 반응과 비대칭 기분 전이, 개방도 게이트로 생성 지시를 만들고 ccd.coping_strategy 전달 누락을 고친다. trace v2와 고정 문구 속마음 요약(migration 24, AI 경로 차단 RLS)을 같은 트랜잭션에 저장하고 피드백 정책이 켜진 경우에만 done·TurnResponse·음성 reply·리뷰로 노출한다. 회기 화면 속마음 보기 토글, 리뷰 접힘 블록, 관리자 감정 관측 v2 표시를 추가한다.
This commit is contained in:
parent
36cb847e38
commit
29c406d89f
39 changed files with 5726 additions and 343 deletions
|
|
@ -23,7 +23,7 @@ import time
|
|||
from dataclasses import dataclass, field, replace
|
||||
from typing import Any, AsyncIterator, Awaitable, Callable, Optional
|
||||
|
||||
from ..contracts.client_affect import ClientAffectTraceV1
|
||||
from ..contracts.client_affect import ClientAffectTraceV1, ClientAffectTraceV2, ClientInnerReactionV1
|
||||
from ..config import settings
|
||||
from ..engine_client import (
|
||||
EngineClient,
|
||||
|
|
@ -95,8 +95,12 @@ class TurnContext:
|
|||
scenario_directive: Optional[rupture_scenario_director.ScenarioDirective] = None
|
||||
# 외부 감정 평가의 안전한 provenance. 원문·점수·확률은 넣지 않는다.
|
||||
client_affect_metadata: Optional[dict[str, Any]] = None
|
||||
# 관리자 관측 전용 Jev 전이 trace. 공개 결과나 provider event에는 넣지 않는다.
|
||||
client_affect_trace: ClientAffectTraceV1 | None = None
|
||||
# 관리자 관측 전용 Jev 전이 trace(v1|v2). 공개 결과나 provider event에는 넣지 않는다.
|
||||
client_affect_trace: ClientAffectTraceV1 | ClientAffectTraceV2 | None = None
|
||||
# v2 생성 지시(§7). L3 '정서 연기 지시' 줄을 대체한다. legacy·v1 경로에선 None.
|
||||
client_affect_directive: Optional[str] = None
|
||||
# 학습자·교수자용 속마음 요약(§8.2). 이 패킷에서는 저장·노출하지 않고 조립만 한다.
|
||||
client_inner_reaction: ClientInnerReactionV1 | None = None
|
||||
|
||||
def to_state_context(self) -> PersonaStateContext:
|
||||
st = self.state_after or self.state_before
|
||||
|
|
@ -107,6 +111,7 @@ class TurnContext:
|
|||
rapport_credit=st.rapport_credit,
|
||||
ideation_stage=st.ideation_stage,
|
||||
affect_state=st.affect_state,
|
||||
affect_directive=self.client_affect_directive,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -322,20 +327,24 @@ def _rebuild_persona_messages(ctx: TurnContext) -> None:
|
|||
)
|
||||
|
||||
|
||||
def _minimal_persona_context(card: PersonaCard) -> dict[str, Any]:
|
||||
"""Jev가 반응을 해석할 최소 페르소나 단서만 고른다."""
|
||||
def _client_profile_inputs(card: PersonaCard) -> dict[str, Any]:
|
||||
"""v2 Jev state의 client_profile 원자료(마스킹 전)를 카드에서 고른다.
|
||||
|
||||
v1은 ccd["coping"]을 읽었지만 카드는 coping_strategy를 쓰므로 대처 방식이
|
||||
한 번도 전달되지 않았다(§1 근거 5). client_profile.coping_strategy로 고친다.
|
||||
"""
|
||||
ccd = card.ccd or {}
|
||||
triggers = card.triggers or {}
|
||||
return {
|
||||
"big5": card.big5,
|
||||
"resistance": card.resistance,
|
||||
"speech_style": card.speech_style,
|
||||
"presenting": card.presenting,
|
||||
"history": card.history,
|
||||
"ccd": {
|
||||
key: card.ccd.get(key)
|
||||
for key in ("core_belief", "automatic_thought", "coping")
|
||||
if key in card.ccd
|
||||
},
|
||||
"triggers": card.triggers,
|
||||
"core_belief": ccd.get("core_belief"),
|
||||
"automatic_thought": ccd.get("automatic_thought"),
|
||||
"coping_strategy": ccd.get("coping_strategy"),
|
||||
"big5": card.big5,
|
||||
"sore_spots": list(triggers.get("sore_spots") or []),
|
||||
"forbidden": list(triggers.get("forbidden") or []),
|
||||
"speech_style": card.speech_style,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -363,14 +372,20 @@ async def _apply_client_affect(
|
|||
*,
|
||||
audit_hook: Optional[LlmAuditHook],
|
||||
) -> None:
|
||||
"""활성 Jev 평가를 1회 적용하고 생성 요청 직전 L3를 갱신한다."""
|
||||
"""활성 Jev 평가(v2)를 1회 적용하고 생성 요청 직전 L3를 갱신한다.
|
||||
|
||||
① 이번 턴 반응 ② 기분 비대칭 전이 ③ 표현 계획(개방도 게이트)을 조합해
|
||||
trace v2·속마음 요약·생성 지시 v2를 만든다(§6~§8.1). 속마음은 이 패킷에서
|
||||
아직 저장·노출하지 않고 ctx에만 보존한다.
|
||||
"""
|
||||
if settings.client_affect_provider != "jev":
|
||||
return
|
||||
assert ctx.state_after is not None
|
||||
state = client_affect.build_appraisal_state(
|
||||
affect_baseline=ctx.persona.affect_baseline,
|
||||
client_profile=_client_profile_inputs(ctx.persona),
|
||||
affect_state=ctx.state_after.affect_state,
|
||||
persona_context=_minimal_persona_context(ctx.persona),
|
||||
affect_baseline=ctx.persona.affect_baseline,
|
||||
stage=ctx.state_after.stage.value,
|
||||
resistance=ctx.state_after.resistance,
|
||||
effective_openness=ctx.state_after.effective_openness,
|
||||
counselor_utterance=ctx.learner_text_masked,
|
||||
|
|
@ -382,19 +397,23 @@ async def _apply_client_affect(
|
|||
)
|
||||
appraisal = await jev_client.appraise(state)
|
||||
state_before_transition = ctx.state_after
|
||||
transition = client_affect.transition_emotions(
|
||||
transition = client_affect.transition_mood(
|
||||
state_before_transition.affect_state,
|
||||
ctx.persona.affect_baseline,
|
||||
appraisal,
|
||||
min_confidence=settings.jev_min_confidence,
|
||||
)
|
||||
ctx.state_after = replace(ctx.state_after, affect_state=transition.affect_state)
|
||||
ctx.client_affect_trace = client_affect.build_client_affect_trace(
|
||||
expression = client_affect.build_expression_plan(
|
||||
appraisal, effective_openness=ctx.state_after.effective_openness
|
||||
)
|
||||
ctx.client_affect_trace = client_affect.build_client_affect_trace_v2(
|
||||
affect_state_before=state_before_transition.affect_state,
|
||||
affect_baseline=ctx.persona.affect_baseline,
|
||||
affect_state_after=ctx.state_after.affect_state,
|
||||
appraisal=appraisal,
|
||||
transition=transition,
|
||||
expression=expression,
|
||||
turn_seq=ctx.state_after.turn_seq,
|
||||
stage=ctx.state_after.stage.value,
|
||||
resistance=ctx.state_after.resistance,
|
||||
|
|
@ -402,6 +421,15 @@ async def _apply_client_affect(
|
|||
rapport_credit=ctx.state_after.rapport_credit,
|
||||
min_confidence=settings.jev_min_confidence,
|
||||
)
|
||||
ctx.client_inner_reaction = client_affect.build_inner_reaction(
|
||||
appraisal, expression, turn_seq=ctx.state_after.turn_seq
|
||||
)
|
||||
ctx.client_affect_directive = client_affect.render_affect_directive_v2(
|
||||
appraisal,
|
||||
expression,
|
||||
affect_state_after=ctx.state_after.affect_state,
|
||||
affect_baseline=ctx.persona.affect_baseline,
|
||||
)
|
||||
ctx.client_affect_metadata = {
|
||||
"provider": appraisal.provider,
|
||||
"model": appraisal.model,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue