콘텐츠로 이동

SeniMate Node Graph

dm/scenario/senimate_weather.json 기준의 node 흐름입니다.

기본 흐름

SeniMate 기본 흐름

기본 경로는 default_next로 연결된 선형 구조입니다.

greeting_introduction
→ daily_life_check
→ meal_check
→ health_check
→ general_conversation
→ conversation_closure
→ complete

Node 요약

Node 역할 수집 슬롯 기본 다음 node
greeting_introduction 위치 기반 날씨, 자기소개와 대화 가능 여부 확인 daily_life_check
daily_life_check 생활·기분·활동 안부 확인 daily_life meal_check
meal_check 최근 식사 상태 확인 meal_status health_check
health_check 몸 상태와 필요한 증상 확인 health_status, pain_level, health_issue, symptom general_conversation
general_conversation 사용자가 꺼낸 소재의 자유 대화 조건에 따라 stay 또는 closure
conversation_closure 새 질문 없이 대화 마무리 terminal

전체 runtime routing

정적 graph edge 외에 global transition과 action: stay를 포함한 전체 흐름입니다.

flowchart TD
    INPUT["현재 사용자 턴"] --> ACTION_END{"Executor Action END?"}
    ACTION_END -- "예" --> COMPLETE([Session complete])
    ACTION_END -- "아니오" --> END_INTENT{"intent =<br/>user_wants_to_end?"}
    END_INTENT -- "예" --> CLOSURE["conversation_closure"]
    END_INTENT -- "아니오" --> SILENT{"dialogue_act = silence<br/>AND no_input_count > 2?"}
    SILENT -- "예" --> CLOSURE
    SILENT -- "아니오" --> CURRENT{"현재 node"}

    CURRENT --> GREETING["greeting_introduction"]
    CURRENT --> DAILY["daily_life_check"]
    CURRENT --> MEAL["meal_check"]
    CURRENT --> HEALTH["health_check"]
    CURRENT --> GENERAL["general_conversation"]
    CURRENT --> CLOSURE

    GREETING --> G_TURN{"node_turns = 1?"}
    G_TURN -- "예" --> GREETING
    G_TURN -- "아니오" --> DAILY

    DAILY --> DAILY_MIN{"min_turns 충족?"}
    DAILY_MIN -- "아니오" --> DAILY
    DAILY_MIN -- "예" --> MEAL

    MEAL --> MEAL_MIN{"min_turns 충족?"}
    MEAL_MIN -- "아니오" --> MEAL
    MEAL_MIN -- "예" --> HEALTH

    HEALTH --> HEALTH_MIN{"min_turns 충족?"}
    HEALTH_MIN -- "아니오" --> HEALTH
    HEALTH_MIN -- "예" --> HEALTH_MISSING{"complaint AND<br/>symptom empty AND<br/>health_issue empty?"}
    HEALTH_MISSING -- "예" --> HEALTH
    HEALTH_MISSING -- "아니오" --> GENERAL

    GENERAL --> GENERAL_END{"종료 intent?"}
    GENERAL_END -- "예" --> CLOSURE
    GENERAL_END -- "아니오" --> GENERAL

    CLOSURE --> ACK{"affirm / goodbye /<br/>thank / silence?"}
    ACK -- "예" --> COMPLETE([Session complete])
    ACK -- "아니오" --> RESUME{"미수집 slot"}
    RESUME -->|"daily_life empty"| DAILY
    RESUME -->|"meal_status empty"| MEAL
    RESUME -->|"health_status empty"| HEALTH
    RESUME -->|"그 외"| GENERAL

Global transitions

Global transition은 모든 node에서 node-local routing보다 먼저 평가합니다.

flowchart TD
    T1["1. user_wants_to_end"] -->|"intent 일치"| C["conversation_closure"]
    T1 -->|"불일치"| T2["2. silent_close"]
    T2 -->|"연속 무응답 3회"| C
    T2 -->|"불일치"| LOCAL["node-local routing"]

배열의 선언 순서가 우선순위입니다. Closure 재개는 global transition이 아니라 closure node의 next_nodes가 담당합니다.

Node-local routing

greeting_introduction

flowchart LR
    G["greeting_introduction"] --> T{"system.node_turns = 1?"}
    T -- "예" --> G
    T -- "아니오" --> D["daily_life_check"]

첫 응답에서는 weather Skill이 세션 위치의 날씨를 조회합니다. 성공하면 결과를 참고해 날씨·소개·대화 가능 여부를 NLG로 말하고, 실패하면 params.fallback으로 날씨 조회 실패를 짧게 알립니다. 다음 일상 질문은 미리 하지 않습니다.

daily_life_check

flowchart LR
    D["daily_life_check"] -->|"min_turns 미충족"| D
    D -->|"진행 가능"| M["meal_check"]

daily_life는 optional이므로 미수집 자체가 진행을 막지는 않습니다.

meal_check

flowchart LR
    M["meal_check"] -->|"min_turns 미충족"| M
    M -->|"진행 가능"| H["health_check"]

meal_status도 optional이며 adequate 또는 skipped 값을 사용합니다.

health_check

flowchart TD
    H["health_check"] --> MIN{"min_turns 충족?"}
    MIN -- "아니오" --> H
    MIN -- "예" --> NEED{"health_status = complaint<br/>AND symptom empty<br/>AND health_issue empty?"}
    NEED -- "예" --> H
    NEED -- "아니오" --> G["general_conversation"]

불편하다고 말한 경우 symptom 또는 health_issue 중 하나도 듣지 못했다면 한 번 더 건강 node에 머뭅니다.

general_conversation

flowchart TD
    G["general_conversation"] --> END{"intent = user_wants_to_end?"}
    END -- "예" --> C["conversation_closure"]
    END -- "아니오" --> G

이 node는 max_turns: 6입니다. turn_limit_enabled를 끄지 않았으므로 턴 상한 응답 경로가 열릴 수 있지만, next_nodes는 종료 intent가 아니면 action: stay입니다. 대화 횟수만으로 closure로 가지 않습니다. 화법은 prompts.bot과 노드 description이 담당합니다.

사용자가 명시적으로 종료하지 않으면 자유 대화를 유지합니다.

conversation_closure

flowchart TD
    C["conversation_closure<br/>terminal: true"] --> LIMIT{"max_turns 도달?"}
    LIMIT -- "예" --> ACTION_END["session_ended=true<br/>action_end"]
    ACTION_END --> COMPLETE([Session complete])
    LIMIT -- "아니오" --> RESUME{"system.conversation_resuming?"}
    RESUME -- "false" --> COMPLETE
    RESUME -- "true" --> SLOT{"미수집 slot"}
    SLOT -->|"daily_life"| D["daily_life_check"]
    SLOT -->|"meal_status"| M["meal_check"]
    SLOT -->|"health_status"| H["health_check"]
    SLOT -->|"없음"| G["general_conversation"]

system.conversation_resuming은 현재 node가 end node이고 dialog act가 affirm, goodbye, thank, silence 중 하나가 아닐 때 true입니다.

Closure는 terminal: true를 명시하므로 successor가 있어도 terminal node입니다. 재개 규칙을 node 내부에 둘 수 있고, 조건이 일치하지 않으면 TransitionEngine의 terminal 판정이 세션을 닫습니다.

Closure의 max_turns=2에 먼저 도달하면 GeneralResponseExecutorsession_ended=true를 반환하고 Action END가 최우선으로 종료합니다. 이 경로에서는 node-local 재개 조건을 평가하지 않습니다.

general_conversation에는 node turn limit 경로가 없습니다. 사용자의 명시적인 user_wants_to_end만 closure 이동을 결정합니다.

정적 graph와 runtime graph의 차이

NetworkX 정적 edge:

greeting → daily → meal → health → general → closure
closure → daily
closure → meal
closure → health
closure → general

정적 edge가 아닌 runtime routing:

  • greeting_introductionaction: stay
  • health_check의 자기 자신을 향한 조건부 target
  • general_conversationaction: stay
  • 모든 global transition
  • executor와 policy의 transition request
  • terminal complete 처리

Closure의 단계별 재개는 이제 next_nodes[].target이므로 정적 edge와 runtime condition 양쪽에 존재합니다. Edge가 있다고 무조건 이동하는 것은 아니며 system.conversation_resuming과 slot 조건을 먼저 평가합니다.

따라서 graph 시각화만 확인하지 말고 detailed 출력의 transition reason도 함께 확인해야 합니다.

관련 문서