콘텐츠로 이동

06. 슬롯 오배정

이 문서는 당시 오배정 문제를 추적한 기록입니다. 현재 slot registry·수집 범위·남은 위험은 SeniMate Slot을 기준으로 확인하세요.

증상

노드가 한 칸씩 밀려서 답이 엉뚱한 슬롯에 들어갔다.

'응 먹었지'  → daily_life  = '먹었지'      ✗  (meal_status 여야 함)
'건강해'     → meal_status = ...           ✗  (health_status 여야 함)

그 결과 봇이 같은 주제를 두 번 물었다.

Bot: 식사를 잘 챙기셨다니 다행이에요. 그럼 요즘 식사는 잘 챙기고 계신가요?

원인은 NLU 프롬프트 한 줄이었다

# dm/core/llm/prompts/prompts_nlu.py (수정 전)
"- 사용자가 현재 노드 목적과 무관한 일상대화를 하면 intent를 'off_topic'으로 설정하세요."

일상 안부를 묻는 노드에서 "일상대화면 off_topic"이라고 지시하고 있었다. daily_life_check 의 목적이 곧 일상인데 그것을 주제 이탈로 분류하라는 뜻이 된다.

여기서 연쇄가 시작된다.

"오늘 심심해" → intent = off_topic
   ├─ entities 비어 있음        → daily_life 안 채워짐
   ├─ TopicGuardPolicy 발동     → 노드에 붙잡힘
   └─ 그런데 봇은 이미 다음 노드(식사) 질문을 했다
        → 사용자가 식사에 답
        → 아직 daily_life_check 이므로 daily_life 로 저장   ✗

노드가 "직전 질문의 답을 받고 다음 질문을 던지는" 구조라, 노드가 이동하지 않으면 답과 슬롯의 짝이 어긋난다.

무엇을 바꿨나

1. off_topic 판정 기준을 바꿨다

판단 기준을 "잡담인가" 에서 "이 노드가 알고 싶어 하는 것에 대한 정보가 들어 있는가" 로 바꿨다.

intent 'off_topic' 은 **현재 노드의 수집 대상 슬롯 어디에도 쓸 수 없고, 노드 목적과
명확히 무관한** 발화에만 쓰세요. 판단 기준은 '잡담인가'가 아니라 **'이 노드가 알고
싶어 하는 것에 대한 정보가 들어 있는가'** 입니다. 노드가 일상·안부를 묻는 중이라면
날씨·기분·심심함·소일거리 이야기는 off_topic 이 아니라 그 노드의 정보입니다.
애매하면 off_topic 으로 두지 마세요.

한국어·일본어 프롬프트 양쪽에 적용했다.

2. 라우팅 키를 프롬프트에서 제외했다

node_config 는 프롬프트에 통째로 덤프된다. 거기에 default_next: "meal_check" 가 보였다.

_ROUTING_ONLY_KEYS = frozenset({"next_nodes", "default_next"})

다음 노드를 정하는 것은 런타임의 일이지 LLM 의 일이 아니다. 이 값이 보이면 LLM 이 아직 전이하지 않은 턴에도 다음 노드 주제를 앞질러 묻는다. 전이해도 되는 턴에는 handoff 규칙이 다음 주제를 명시적으로 알려 준다.

3. daily_life_check description 을 손봤다

  • "다음 노드(meal_check)로 넘어갈 때는…" 제거 — 전이 지시는 handoff 규칙이 담당한다. description 에 다음 노드 이름을 적으면 아직 전이하지 않은 턴에도 그 주제를 꺼낸다.
  • "한 걸음 더 들어가세요" 예시 제거 — 이 예시가 전환 턴에서 두 번째 질문을 만들고 있었다.

결과

--node daily_life_check 단독 진단 (실제 LLM)

입력 이전 이후
오늘 심심해 off_topic · 슬롯 없음 · 붙잡힘 daily_life_check · daily_life='심심해'
그냥 집에 있어 정상 정상
TV 봐 off_topic · 슬롯 없음 · 붙잡힘 daily_life_check · daily_life='TV 봐'

전체 흐름에서 슬롯이 제자리에 들어간다.

'오늘 심심해' → daily_life_check   daily_life    = '심심해'
'응 먹었지'   → meal_check         meal_status   = 'adequate'
'건강해'      → health_check       health_status = 'adequate'

TopicGuardPolicy 는 손대지 않았다

이전 단계에서 되돌린 정책 변경(dialog act 기반 판정, 세션 종료 제거)을 다시 넣지 않았다. NLU 가 제대로 판정하니 가드가 발동할 일이 없어졌다.

off_topic_count 가 3에 도달해 세션이 끊기던 경로도 함께 사라졌다. 정책을 고치는 대신 그 정책을 잘못 트리거하던 입력을 고친 셈이다.

남은 위험

  • NLU 판정에 의존한다. 프롬프트 지시가 지켜지지 않으면 같은 문제가 재발한다. TopicGuardPolicy 자체는 여전히 intent 기반이고 세션을 끊을 수 있다.
  • 구조적 취약점은 남아 있다. "노드가 다음 질문을 미리 던지는" 구조는 그대로다. 어떤 이유로든 노드가 이동하지 않으면 짝이 다시 어긋난다.