|
|
""" |
|
|
Dialogue management for multi-turn conversations. |
|
|
""" |
|
|
from typing import Dict, Any, Optional, List, Tuple |
|
|
from enum import Enum |
|
|
|
|
|
|
|
|
class DialogueState(Enum): |
|
|
"""Dialogue states.""" |
|
|
INITIAL = "initial" |
|
|
COLLECTING_INFO = "collecting_info" |
|
|
CLARIFYING = "clarifying" |
|
|
PROVIDING_ANSWER = "providing_answer" |
|
|
FOLLOW_UP = "follow_up" |
|
|
COMPLETED = "completed" |
|
|
|
|
|
|
|
|
class DialogueManager: |
|
|
"""Manages dialogue state and multi-turn conversations.""" |
|
|
|
|
|
def __init__(self): |
|
|
self.state = DialogueState.INITIAL |
|
|
self.slots = {} |
|
|
self.context_switch_detected = False |
|
|
|
|
|
def update_state( |
|
|
self, |
|
|
query: str, |
|
|
intent: str, |
|
|
results_count: int, |
|
|
confidence: float, |
|
|
recent_messages: Optional[List[Dict[str, Any]]] = None |
|
|
) -> DialogueState: |
|
|
""" |
|
|
Update dialogue state based on current query and context. |
|
|
|
|
|
Args: |
|
|
query: Current user query. |
|
|
intent: Detected intent. |
|
|
results_count: Number of results found. |
|
|
confidence: Confidence score. |
|
|
recent_messages: Recent conversation messages. |
|
|
|
|
|
Returns: |
|
|
Updated dialogue state. |
|
|
""" |
|
|
|
|
|
if recent_messages and len(recent_messages) > 0: |
|
|
last_intent = recent_messages[-1].get("intent") |
|
|
if last_intent and last_intent != intent and intent != "greeting": |
|
|
self.context_switch_detected = True |
|
|
self.state = DialogueState.INITIAL |
|
|
self.slots = {} |
|
|
return self.state |
|
|
|
|
|
|
|
|
if results_count == 0 and confidence < 0.5: |
|
|
|
|
|
self.state = DialogueState.CLARIFYING |
|
|
elif results_count > 0 and confidence >= 0.7: |
|
|
|
|
|
self.state = DialogueState.PROVIDING_ANSWER |
|
|
elif results_count > 0 and confidence < 0.7: |
|
|
|
|
|
self.state = DialogueState.FOLLOW_UP |
|
|
else: |
|
|
self.state = DialogueState.PROVIDING_ANSWER |
|
|
|
|
|
return self.state |
|
|
|
|
|
def needs_clarification( |
|
|
self, |
|
|
query: str, |
|
|
intent: str, |
|
|
results_count: int |
|
|
) -> Tuple[bool, Optional[str]]: |
|
|
""" |
|
|
Check if clarification is needed. |
|
|
|
|
|
Args: |
|
|
query: User query. |
|
|
intent: Detected intent. |
|
|
results_count: Number of results. |
|
|
|
|
|
Returns: |
|
|
Tuple of (needs_clarification, clarification_message). |
|
|
""" |
|
|
if results_count == 0: |
|
|
|
|
|
clarification_messages = { |
|
|
"search_fine": "Bạn có thể cho biết cụ thể hơn về loại vi phạm không? Ví dụ: vượt đèn đỏ, không đội mũ bảo hiểm...", |
|
|
"search_procedure": "Bạn muốn tìm thủ tục nào? Ví dụ: đăng ký cư trú, thủ tục ANTT...", |
|
|
"search_office": "Bạn muốn tìm đơn vị nào? Ví dụ: công an phường, điểm tiếp dân...", |
|
|
"search_advisory": "Bạn muốn tìm cảnh báo về chủ đề gì?", |
|
|
} |
|
|
message = clarification_messages.get(intent, "Bạn có thể cung cấp thêm thông tin không?") |
|
|
return (True, message) |
|
|
|
|
|
return (False, None) |
|
|
|
|
|
def detect_missing_slots( |
|
|
self, |
|
|
intent: str, |
|
|
query: str, |
|
|
results_count: int |
|
|
) -> Dict[str, Any]: |
|
|
""" |
|
|
Detect missing information slots. |
|
|
|
|
|
Args: |
|
|
intent: Detected intent. |
|
|
query: User query. |
|
|
results_count: Number of results. |
|
|
|
|
|
Returns: |
|
|
Dictionary of missing slots. |
|
|
""" |
|
|
missing_slots = {} |
|
|
|
|
|
if intent == "search_fine": |
|
|
|
|
|
if "v001" not in query.lower() and "v002" not in query.lower(): |
|
|
if not any(kw in query.lower() for kw in ["vượt đèn đỏ", "mũ bảo hiểm", "nồng độ cồn"]): |
|
|
missing_slots["fine_specification"] = True |
|
|
|
|
|
elif intent == "search_procedure": |
|
|
|
|
|
if not any(kw in query.lower() for kw in ["cư trú", "antt", "pccc", "đăng ký"]): |
|
|
missing_slots["procedure_specification"] = True |
|
|
|
|
|
elif intent == "search_office": |
|
|
|
|
|
if not any(kw in query.lower() for kw in ["phường", "huyện", "tỉnh", "điểm tiếp dân"]): |
|
|
missing_slots["office_specification"] = True |
|
|
|
|
|
return missing_slots |
|
|
|
|
|
def handle_follow_up( |
|
|
self, |
|
|
query: str, |
|
|
recent_messages: List[Dict[str, Any]] |
|
|
) -> Optional[str]: |
|
|
""" |
|
|
Generate follow-up question if needed. |
|
|
|
|
|
Args: |
|
|
query: Current query. |
|
|
recent_messages: Recent conversation messages. |
|
|
|
|
|
Returns: |
|
|
Follow-up question or None. |
|
|
""" |
|
|
if not recent_messages: |
|
|
return None |
|
|
|
|
|
|
|
|
if len(query.split()) <= 3: |
|
|
last_message = recent_messages[-1] |
|
|
last_intent = last_message.get("intent") |
|
|
|
|
|
if last_intent == "search_fine": |
|
|
return "Bạn muốn biết thêm thông tin gì về mức phạt này? (ví dụ: điều luật, biện pháp khắc phục)" |
|
|
elif last_intent == "search_procedure": |
|
|
return "Bạn muốn biết thêm thông tin gì về thủ tục này? (ví dụ: hồ sơ, lệ phí, thời hạn)" |
|
|
|
|
|
return None |
|
|
|
|
|
def reset(self): |
|
|
"""Reset dialogue manager state.""" |
|
|
self.state = DialogueState.INITIAL |
|
|
self.slots = {} |
|
|
self.context_switch_detected = False |
|
|
|
|
|
|