23. off_topic 제거¶
케어 대화에는 주제 이탈 라벨이 필요 없다. 슬롯을 잘못 채우지 않는 것과, required 슬롯이 비었을 때만 다시 묻는 것으로 충분하다.
무엇이 문제였나¶
NLU가 현재 노드 목적과 다른 말을 off_topic으로 분류하면, 런타임이 그 라벨을 노드를 붙잡는 신호로 썼다.
건강 확인에서 몸이 괜찮다고 답함
→ health_status 채워짐
→ 사용자가 손자 이야기를 꺼냄
→ intent = off_topic
→ TopicGuardPolicy / 전이 stay
→ "몸은 좀 어떠세요?" 를 다시 물음
수집이 끝난 뒤의 후속 이야기를 이탈로 보면, 그래프는 다음 노드로 가지 못하고 같은 질문을 반복한다. 케어 말벗에서 그 후속 이야기는 잡담이 아니라 대화의 본론인 경우가 많다.
슬롯 오배정도 같은 라벨에서 시작했다. 일상을 묻는 노드에서 일상 답을 off_topic으로 두면 슬롯이 비고, 노드는 붙잡히고, 봇은 이미 다음 주제 질문을 한 뒤라 답이 엉뚱한 칸에 들어갔다. 그 경로는 06. 슬롯 오배정에서 프롬프트만 좁혀 두었고, 붙잡는 정책은 남아 있었다.
예전 구조¶
off_topic은 한 계층의 분류가 아니라 네 곳이 같은 라벨을 전제하고 있었다.
NLU 프롬프트
→ intent = off_topic 을 허용·권장
nlu_postprocess
→ (당시에는 별다른 재해석 없음)
TopicGuardPolicy
→ 전역 게이트. 노드에 머물고 같은 주제를 되물음
TransitionEngine
→ collect 노드에서 off_topic 이면 stay
domain_rules OFF_TOPIC
→ NLG에 "주제를 되돌리라"는 화법 규칙
라벨 하나가 정책·전이·말투를 동시에 움직였다. 케어 시나리오에서 그 동작이 이득보다 해가 컸다.
무엇을 바꿨나¶
주제 이탈을 대화 실패가 아니라, 지금 슬롯에 넣을 값이 없는 발화로 본다. 넣을 값이 없으면 entities를 비우고 inform으로 두면 된다.
| 계층 | 이전 | 이후 |
|---|---|---|
| NLU 프롬프트 | 무관하면 off_topic |
off_topic을 쓰지 말 것. 슬롯에 못 넣으면 entities를 비우고 inform |
nlu_postprocess |
라벨을 그대로 통과 | off_topic / chitchat / small_talk / weather_inquiry → inform |
TopicGuardPolicy |
전역 파이프라인에서 노드 고정 | 제거. GlobalPolicyPipeline은 Safety · Fallback 만 |
TransitionEngine |
collect 노드에서 off_topic stay | 제거. 다른 전이 규칙과 동일하게 평가 |
domain_rules OFF_TOPIC |
주제를 되돌리라는 NLG 규칙 | 제거. 화법은 시나리오가 담당 |
슬롯을 지키는 쪽은 라벨이 아니라 기존 규칙이다.
- entities는 직전 봇 질문과 주제가 같을 때만 채운다
- required 슬롯이 비면 그 노드가 다시 묻는다
- optional 슬롯은 비워도 진행할 수 있다
손자 이야기는 건강 슬롯을 덮어쓰지 않고, 채워진 노드는 default_next로 넘어갈 수 있다.
왜 정책을 no-op으로 두지 않았나¶
TopicGuardPolicy.run을 빈 결과만 돌리거나, 전이 stay만 꺼도 증상은 줄어든다. 다만 라벨이 남아 있으면 시나리오·테스트·생성 프롬프트가 다시 그 라벨에 동작을 걸기 쉽다.
전역 게이트가 executor와 라우팅을 건너뛰면 노드가 할 말을 만들 기회가 없다. 14. 종료 권한과 같은 이유다. 노드가 처리할 수 있는 발화를 전역에서 끊지 않는다.
남겨 둔 안전망¶
LLM이 옛 습관으로 off_topic을 낼 수는 있다. 그때도 노드를 붙잡지 않도록 postprocess가 inform으로 바꾼다. 별칭 목록은 dm/core/constants.py의 OFF_TOPIC_INTENT_ALIASES다.
테스트 일부는 입력으로 intent="off_topic"을 넘긴다. 전이가 그 라벨 때문에 stay 하면 안 된다는 회귀 잠금이다.
test_global_pipeline_has_no_topic_guard
test_transition_engine_does_not_pin_collect_node_for_off_topic
현재 계약¶
현재 동작의 기준은 리팩터 기록이 아니라 계층 문서다.
전역 정책 파일 표의 "topic 정책"은 이 단계 이후 해당하지 않는다. global_policies.py는 Safety와 Fallback만 둔다.