# %%writefile inference_engine.py import re import numpy as np from typing import Dict, List, Tuple, Set, Optional, Any, Callable from dataclasses import dataclass, field import traceback @dataclass class InferenceRule: """推理規則結構""" name: str condition: Callable[[str, Dict[str, Any]], bool] imply: Callable[[str, Dict[str, Any]], Dict[str, float]] reasoning: str priority: int = 1 # 規則優先級 @dataclass class InferenceResult: """推理結果結構""" implicit_priorities: Dict[str, float] = field(default_factory=dict) triggered_rules: List[str] = field(default_factory=list) reasoning_chains: List[str] = field(default_factory=list) confidence: float = 1.0 class BreedRecommendationInferenceEngine: """ 品種推薦推理引擎 從使用者明確輸入中推斷隱含需求,補充優先級設定 核心邏輯: 1. 居住環境推理 (公寓 → 安靜、中小型) 2. 家庭情況推理 (有小孩 → 溫和、耐心) 3. 經驗程度推理 (新手 → 易照顧) 4. 生活方式推理 (忙碌 → 低維護) """ def __init__(self): """初始化推理引擎""" self.inference_rules = self._build_inference_rules() self.spatial_keywords = self._initialize_spatial_keywords() self.lifestyle_keywords = self._initialize_lifestyle_keywords() def _initialize_spatial_keywords(self) -> Dict[str, List[str]]: """初始化空間相關關鍵字""" return { 'apartment': [ 'apartment', 'flat', 'condo', 'studio', 'small space', 'limited space', 'city living', 'urban' ], 'small_house': [ 'small house', 'townhouse', 'small home' ], 'large_house': [ 'large house', 'big house', 'spacious home', 'yard', 'garden', 'backyard', 'outdoor space' ] } def _initialize_lifestyle_keywords(self) -> Dict[str, List[str]]: """初始化生活方式關鍵字""" return { 'has_children': [ 'kids', 'children', 'toddler', 'baby', 'school age', 'child', 'son', 'daughter', 'family with kids' ], 'beginner': [ 'first dog', 'first time', 'beginner', 'never had', 'new to dogs', 'inexperienced', 'no experience' ], 'busy': [ 'busy', 'limited time', 'work full time', 'not much time', 'long hours', 'busy schedule', 'hectic lifestyle' ], 'active': [ 'active', 'athletic', 'outdoor', 'hiking', 'running', 'jogging', 'sports', 'exercise enthusiast' ] } def _build_inference_rules(self) -> List[InferenceRule]: """構建推理規則庫""" return [ # 規則1: 公寓居住推理 InferenceRule( name="apartment_living", condition=lambda input_text, ctx: self._check_apartment(input_text, ctx), imply=lambda input_text, ctx: { 'noise': 1.3, # 公寓暗示需要安靜 (reduced from 1.4) 'size': 1.2, # 公寓暗示偏好中小型 (reduced from 1.3) 'exercise': 1.15 # 公寓暗示可能運動空間有限 (reduced from 1.2) }, reasoning="Apartment living typically requires quieter, smaller dogs with moderate exercise needs", priority=1 ), # 規則2: 有小孩推理 InferenceRule( name="has_children", condition=lambda input_text, ctx: self._check_children(input_text, ctx), imply=lambda input_text, ctx: { 'family': 1.4, # 明確需要家庭友善 (reduced from 1.5) 'experience': 1.15, # 暗示希望容易訓練 (reduced from 1.2) 'noise': 1.15 # 有小孩通常希望狗較安靜 (reduced from 1.2) }, reasoning="Families with children need gentle, patient, child-safe breeds", priority=1 ), # 規則3: 新手飼主推理 InferenceRule( name="beginner_owner", condition=lambda input_text, ctx: self._check_beginner(input_text, ctx), imply=lambda input_text, ctx: { 'experience': 1.3, # 需要容易照顧 (reduced from 1.4) 'grooming': 1.25, # 偏好低維護 (reduced from 1.3) 'health': 1.15 # 希望健康問題少 (reduced from 1.2) }, reasoning="Beginners benefit from easier-to-care-for, low-maintenance breeds", priority=1 ), # 規則4: 忙碌生活方式推理 InferenceRule( name="busy_lifestyle", condition=lambda input_text, ctx: self._check_busy(input_text, ctx), imply=lambda input_text, ctx: { 'grooming': 1.3, # 需要低維護 (reduced from 1.4) 'exercise': 1.25, # 不能需要太多運動 (reduced from 1.3) 'experience': 1.15 # 希望獨立性強 (reduced from 1.2) }, reasoning="Busy owners need lower-maintenance breeds with moderate exercise needs", priority=1 ), # 規則5: 大型住宅推理 InferenceRule( name="large_house", condition=lambda input_text, ctx: self._check_large_house(input_text, ctx), imply=lambda input_text, ctx: { 'size': 1.15, # 可以接受大型犬 (reduced from 1.2) 'exercise': 1.2 # 可能有更多運動空間 (reduced from 1.3) }, reasoning="Large homes can accommodate more active, larger breeds", priority=2 ), # 規則6: 有院子推理 InferenceRule( name="has_yard", condition=lambda input_text, ctx: self._check_yard(input_text, ctx), imply=lambda input_text, ctx: { 'exercise': 1.2, # 有院子可以支持更活躍的品種 (reduced from 1.3) 'size': 1.15 # 可以考慮較大的品種 (reduced from 1.2) }, reasoning="Yards provide exercise space for more active breeds", priority=2 ), # 規則7: 噪音敏感環境推理 InferenceRule( name="noise_sensitive", condition=lambda input_text, ctx: self._check_noise_sensitive(input_text, ctx), imply=lambda input_text, ctx: { 'noise': 1.5 # 強調需要安靜 (reduced from 1.6) }, reasoning="Noise-sensitive environments require quieter breeds", priority=1 ), # 規則8: 過敏體質推理 InferenceRule( name="allergy_concerns", condition=lambda input_text, ctx: self._check_allergies(input_text, ctx), imply=lambda input_text, ctx: { 'grooming': 1.4, # 需要低掉毛品種 (reduced from 1.5) 'health': 1.25 # 關注健康問題 (reduced from 1.3) }, reasoning="Allergy concerns require hypoallergenic, low-shedding breeds", priority=1 ), # 規則9: 活躍生活方式推理 InferenceRule( name="active_lifestyle", condition=lambda input_text, ctx: self._check_active(input_text, ctx), imply=lambda input_text, ctx: { 'exercise': 1.3, # 需要高運動量品種 (reduced from 1.4) 'size': 1.15 # 可能偏好中大型犬 (reduced from 1.2) }, reasoning="Active lifestyle matches well with energetic, athletic breeds", priority=1 ), # 規則10: 小型空間推理 InferenceRule( name="small_space", condition=lambda input_text, ctx: self._check_small_space(input_text, ctx), imply=lambda input_text, ctx: { 'size': 1.3, # 強調需要小型犬 (reduced from 1.4) 'noise': 1.25, # 小空間需要安靜 (reduced from 1.3) 'exercise': 1.15 # 運動需求不宜過高 (reduced from 1.2) }, reasoning="Small spaces require compact, quiet dogs with moderate energy", priority=1 ) ] def _check_apartment(self, input_text: str, ctx: Dict[str, Any]) -> bool: """檢查是否提到公寓""" text_lower = input_text.lower() return (any(keyword in text_lower for keyword in self.spatial_keywords['apartment']) or ctx.get('living_space') == 'apartment') def _check_children(self, input_text: str, ctx: Dict[str, Any]) -> bool: """檢查是否有小孩""" text_lower = input_text.lower() return (any(keyword in text_lower for keyword in self.lifestyle_keywords['has_children']) or ctx.get('has_children') == True) def _check_beginner(self, input_text: str, ctx: Dict[str, Any]) -> bool: """檢查是否為新手""" text_lower = input_text.lower() return (any(keyword in text_lower for keyword in self.lifestyle_keywords['beginner']) or ctx.get('experience_level') == 'beginner') def _check_busy(self, input_text: str, ctx: Dict[str, Any]) -> bool: """檢查是否為忙碌生活方式""" text_lower = input_text.lower() return (any(keyword in text_lower for keyword in self.lifestyle_keywords['busy']) or ctx.get('time_availability') == 'limited') def _check_large_house(self, input_text: str, ctx: Dict[str, Any]) -> bool: """檢查是否有大房子""" text_lower = input_text.lower() return (any(keyword in text_lower for keyword in self.spatial_keywords['large_house']) or ctx.get('living_space') in ['house_large', 'house']) def _check_yard(self, input_text: str, ctx: Dict[str, Any]) -> bool: """檢查是否有院子""" text_lower = input_text.lower() return (any(keyword in text_lower for keyword in ['yard', 'garden', 'backyard', 'outdoor space']) or ctx.get('yard_access') in ['shared_yard', 'private_yard']) def _check_noise_sensitive(self, input_text: str, ctx: Dict[str, Any]) -> bool: """檢查是否為噪音敏感環境""" text_lower = input_text.lower() noise_keywords = ['noise sensitive', 'thin walls', 'neighbors close', 'townhouse', 'condo'] return any(keyword in text_lower for keyword in noise_keywords) def _check_allergies(self, input_text: str, ctx: Dict[str, Any]) -> bool: """檢查是否有過敏體質""" text_lower = input_text.lower() allergy_keywords = ['allergies', 'hypoallergenic', 'sensitive to fur', 'asthma', 'allergy'] return (any(keyword in text_lower for keyword in allergy_keywords) or ctx.get('has_allergies') == True) def _check_active(self, input_text: str, ctx: Dict[str, Any]) -> bool: """檢查是否為活躍生活方式""" text_lower = input_text.lower() return (any(keyword in text_lower for keyword in self.lifestyle_keywords['active']) or ctx.get('activity_level') == 'high') def _check_small_space(self, input_text: str, ctx: Dict[str, Any]) -> bool: """檢查是否為小型空間""" text_lower = input_text.lower() small_space_keywords = ['small space', 'limited space', 'tiny', 'compact', 'studio'] return any(keyword in text_lower for keyword in small_space_keywords) def infer_implicit_priorities(self, explicit_input: str, user_context: Optional[Dict[str, Any]] = None) -> InferenceResult: """ 從明確輸入和使用者上下文推斷隱含優先級 Args: explicit_input: 使用者明確輸入 user_context: 使用者上下文資訊 Returns: InferenceResult: 推理結果 """ try: if user_context is None: user_context = {} implicit_priorities = {} triggered_rules = [] reasoning_chains = [] # 按優先級排序規則 sorted_rules = sorted(self.inference_rules, key=lambda r: r.priority) # 應用推理規則 for rule in sorted_rules: try: if rule.condition(explicit_input, user_context): # 規則觸發 implied = rule.imply(explicit_input, user_context) triggered_rules.append(rule.name) reasoning_chains.append(rule.reasoning) # 合併隱含優先級(取最大值) for dim, score in implied.items(): implicit_priorities[dim] = max( implicit_priorities.get(dim, 1.0), score ) except Exception as e: print(f"Error applying rule {rule.name}: {str(e)}") continue # 計算信心度 confidence = self._calculate_inference_confidence( triggered_rules, explicit_input ) return InferenceResult( implicit_priorities=implicit_priorities, triggered_rules=triggered_rules, reasoning_chains=reasoning_chains, confidence=confidence ) except Exception as e: print(f"Error inferring implicit priorities: {str(e)}") print(traceback.format_exc()) return InferenceResult() def _calculate_inference_confidence(self, triggered_rules: List[str], input_text: str) -> float: """ 計算推理信心度 Args: triggered_rules: 觸發的規則列表 input_text: 輸入文字 Returns: float: 信心度 (0-1) """ base_confidence = 0.6 # 觸發的規則越多,信心度越高 rule_bonus = min(0.3, len(triggered_rules) * 0.1) # 輸入文字越詳細,信心度越高 word_count = len(input_text.split()) detail_bonus = min(0.1, word_count / 100) return min(1.0, base_confidence + rule_bonus + detail_bonus) def get_inference_summary(self, result: InferenceResult) -> Dict[str, Any]: """ 獲取推理摘要 Args: result: 推理結果 Returns: Dict[str, Any]: 推理摘要 """ return { 'total_implicit_priorities': len(result.implicit_priorities), 'implicit_priorities': result.implicit_priorities, 'triggered_rules': result.triggered_rules, 'reasoning_chains': result.reasoning_chains, 'inference_confidence': result.confidence, 'high_confidence_inferences': [ dim for dim, score in result.implicit_priorities.items() if score >= 1.4 ] } def infer_user_priorities(user_input: str, user_context: Optional[Dict[str, Any]] = None) -> InferenceResult: """ 便利函數: 推斷使用者隱含優先級 Args: user_input: 使用者輸入 user_context: 使用者上下文 Returns: InferenceResult: 推理結果 """ engine = BreedRecommendationInferenceEngine() return engine.infer_implicit_priorities(user_input, user_context) def get_inference_summary(user_input: str, user_context: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: """ 便利函數: 獲取推理摘要 Args: user_input: 使用者輸入 user_context: 使用者上下文 Returns: Dict[str, Any]: 推理摘要 """ engine = BreedRecommendationInferenceEngine() result = engine.infer_implicit_priorities(user_input, user_context) return engine.get_inference_summary(result)