99 lines
3.1 KiB
Python
99 lines
3.1 KiB
Python
"""
|
|
NextBestActionEngine (Rules 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91):
|
|
- Conversational State Machine Evaluation
|
|
- Priority Scoring & Action Selection
|
|
- Cooldown & Intent Deduplication
|
|
- Status-aware field tracking (reported, confirmed_none, unknown, declined, skipped, not_asked)
|
|
"""
|
|
|
|
from typing import Dict, Any, List, Optional, Tuple
|
|
|
|
|
|
class NextBestActionEngine:
|
|
"""
|
|
Evaluates intake state and determines the next-best action and target question.
|
|
"""
|
|
|
|
def calculate_priority_score(
|
|
self,
|
|
field: str,
|
|
current_state: Dict[str, Any],
|
|
is_psych: bool = False,
|
|
asked_intents: Optional[List[str]] = None
|
|
) -> float:
|
|
"""
|
|
Calculates question priority score based on Rule 83:
|
|
priority = safety_weight + clinical_importance + symptom_completeness
|
|
+ specialty_relevance + information_value
|
|
- repetition_penalty - already_answered_penalty - unnecessary_question_penalty
|
|
"""
|
|
score = 0.0
|
|
asked_intents = asked_intents or []
|
|
|
|
weights = {
|
|
"emergency": 100.0,
|
|
"crisis": 100.0,
|
|
"reason_for_visit": 50.0,
|
|
"symptoms": 50.0,
|
|
"duration": 45.0,
|
|
"severity": 30.0,
|
|
"medical_history": 35.0,
|
|
"medications": 30.0,
|
|
"allergies": 25.0,
|
|
"previous_episodes": 15.0,
|
|
"family_history": 10.0,
|
|
"additional_information": 5.0,
|
|
}
|
|
|
|
score += weights.get(field, 10.0)
|
|
|
|
# Repetition penalty (Rule 84 & 85)
|
|
if field in asked_intents:
|
|
score -= 60.0
|
|
|
|
# Specialty relevance bonus
|
|
if is_psych and field in ["duration", "medical_history"]:
|
|
score += 10.0
|
|
|
|
return score
|
|
|
|
def determine_next_action(
|
|
self,
|
|
current_state: Dict[str, Any],
|
|
is_emergency: bool = False,
|
|
is_crisis: bool = False,
|
|
is_psych: bool = False,
|
|
missing_fields: Optional[List[str]] = None
|
|
) -> Tuple[str, Optional[str]]:
|
|
"""
|
|
Returns (action_type, target_field)
|
|
Action types (Rule 82):
|
|
- SAFETY_ACTION
|
|
- CLARIFY
|
|
- ASK_QUESTION
|
|
- COMPLETE
|
|
- HANDLE_REFUSAL
|
|
- HANDLE_UNKNOWN
|
|
"""
|
|
if is_crisis or is_emergency:
|
|
return "SAFETY_ACTION", None
|
|
|
|
# Check for state conflicts requiring clarification (Rule 67)
|
|
if current_state and current_state.get("conflicts"):
|
|
return "CLARIFY", current_state["conflicts"][-1].get("field")
|
|
|
|
missing_fields = missing_fields or []
|
|
if not missing_fields:
|
|
return "COMPLETE", None
|
|
|
|
# Score missing fields using priority score (Rule 83)
|
|
scored_fields = []
|
|
for field in missing_fields:
|
|
score = self.calculate_priority_score(field, current_state, is_psych=is_psych)
|
|
scored_fields.append((score, field))
|
|
|
|
scored_fields.sort(key=lambda x: x[0], reverse=True)
|
|
best_field = scored_fields[0][1]
|
|
|
|
return "ASK_QUESTION", best_field
|