Spaces:
Running
on
Zero
Running
on
Zero
| import random | |
| import hashlib | |
| import numpy as np | |
| import sqlite3 | |
| import re | |
| import traceback | |
| from typing import List, Dict, Tuple, Optional, Any | |
| from dataclasses import dataclass | |
| from sentence_transformers import SentenceTransformer | |
| import torch | |
| from sklearn.metrics.pairwise import cosine_similarity | |
| from dog_database import get_dog_description | |
| from breed_health_info import breed_health_info | |
| from breed_noise_info import breed_noise_info | |
| from scoring_calculation_system import UserPreferences, calculate_compatibility_score, UnifiedScoringSystem, calculate_unified_breed_scores | |
| from query_understanding import QueryUnderstandingEngine, analyze_user_query | |
| from constraint_manager import ConstraintManager, apply_breed_constraints | |
| from multi_head_scorer import MultiHeadScorer, score_breed_candidates, BreedScore | |
| from score_calibrator import ScoreCalibrator, calibrate_breed_scores | |
| from config_manager import get_config_manager, get_standardized_breed_data | |
| class MatchingScoreCalculator: | |
| """ | |
| 匹配評分計算器 | |
| 處理多維度匹配計算、約束條件過濾和評分校準 | |
| """ | |
| def __init__(self, breed_list: List[str]): | |
| """初始化匹配評分計算器""" | |
| self.breed_list = breed_list | |
| def apply_size_distribution_correction(self, recommendations: List[Dict]) -> List[Dict]: | |
| """應用尺寸分佈修正以防止大型品種偏差""" | |
| if len(recommendations) < 10: | |
| return recommendations | |
| # 分析尺寸分佈 | |
| size_counts = {'toy': 0, 'small': 0, 'medium': 0, 'large': 0, 'giant': 0} | |
| for rec in recommendations: | |
| breed_info = get_dog_description(rec['breed']) | |
| if breed_info: | |
| size = self._normalize_breed_size(breed_info.get('Size', 'Medium')) | |
| size_counts[size] += 1 | |
| total_recs = len(recommendations) | |
| large_giant_ratio = (size_counts['large'] + size_counts['giant']) / total_recs | |
| # 如果超過 70% 是大型/巨型品種,應用修正 | |
| if large_giant_ratio > 0.7: | |
| corrected_recommendations = [] | |
| size_quotas = {'toy': 2, 'small': 4, 'medium': 6, 'large': 2, 'giant': 1} | |
| current_counts = {'toy': 0, 'small': 0, 'medium': 0, 'large': 0, 'giant': 0} | |
| # 第一輪:在配額內添加品種 | |
| for rec in recommendations: | |
| breed_info = get_dog_description(rec['breed']) | |
| if breed_info: | |
| size = self._normalize_breed_size(breed_info.get('Size', 'Medium')) | |
| if current_counts[size] < size_quotas[size]: | |
| corrected_recommendations.append(rec) | |
| current_counts[size] += 1 | |
| # 第二輪:用最佳剩餘候選品種填滿剩餘位置 | |
| remaining_slots = 15 - len(corrected_recommendations) | |
| remaining_breeds = [rec for rec in recommendations if rec not in corrected_recommendations] | |
| corrected_recommendations.extend(remaining_breeds[:remaining_slots]) | |
| return corrected_recommendations | |
| return recommendations | |
| def _normalize_breed_size(self, size: str) -> str: | |
| """標準化品種尺寸到標準分類""" | |
| if not isinstance(size, str): | |
| return 'medium' | |
| size_lower = size.lower() | |
| if any(term in size_lower for term in ['toy', 'tiny']): | |
| return 'toy' | |
| elif 'small' in size_lower: | |
| return 'small' | |
| elif 'medium' in size_lower: | |
| return 'medium' | |
| elif 'large' in size_lower: | |
| return 'large' | |
| elif any(term in size_lower for term in ['giant', 'extra large']): | |
| return 'giant' | |
| else: | |
| return 'medium' | |
| def apply_hard_constraints(self, breed: str, user_input: str, breed_characteristics: Dict[str, Any]) -> float: | |
| """增強硬約束,具有更嚴格的懲罰""" | |
| penalty = 0.0 | |
| user_text_lower = user_input.lower() | |
| # 獲取品種信息 | |
| breed_info = get_dog_description(breed) | |
| if not breed_info: | |
| return 0.0 | |
| breed_size = breed_info.get('Size', '').lower() | |
| exercise_needs = breed_info.get('Exercise Needs', '').lower() | |
| # 公寓居住約束 - 更嚴格 | |
| if any(term in user_text_lower for term in ['apartment', 'flat', 'studio', 'small space']): | |
| if 'giant' in breed_size: | |
| return -2.0 # 完全淘汰 | |
| elif 'large' in breed_size: | |
| if any(term in exercise_needs for term in ['high', 'very high']): | |
| return -2.0 # 完全淘汰 | |
| else: | |
| penalty -= 0.5 # 仍有顯著懲罰 | |
| elif 'medium' in breed_size and 'very high' in exercise_needs: | |
| penalty -= 0.6 | |
| # 運動不匹配約束 | |
| if "don't exercise much" in user_text_lower or "low exercise" in user_text_lower: | |
| if any(term in exercise_needs for term in ['very high', 'extreme', 'intense']): | |
| return -2.0 # 完全淘汰 | |
| elif 'high' in exercise_needs: | |
| penalty -= 0.8 | |
| # 中等生活方式檢測 | |
| if any(term in user_text_lower for term in ['moderate', 'balanced', '30 minutes', 'half hour']): | |
| # 懲罰極端情況 | |
| if 'giant' in breed_size: | |
| penalty -= 0.7 # 對巨型犬的強懲罰 | |
| elif 'very high' in exercise_needs: | |
| penalty -= 0.5 | |
| # 兒童安全(現有邏輯保持但增強) | |
| if any(term in user_text_lower for term in ['child', 'kids', 'family', 'baby']): | |
| good_with_children = breed_info.get('Good with Children', '').lower() | |
| if good_with_children == 'no': | |
| return -2.0 # 為了安全完全淘汰 | |
| return penalty | |
| def calculate_lifestyle_bonus(self, breed_characteristics: Dict[str, Any], | |
| lifestyle_keywords: Dict[str, List[str]]) -> float: | |
| """增強生活方式匹配獎勵計算""" | |
| bonus = 0.0 | |
| penalties = 0.0 | |
| # 增強尺寸匹配 | |
| breed_size = breed_characteristics.get('size', '').lower() | |
| size_prefs = lifestyle_keywords.get('size_preference', []) | |
| for pref in size_prefs: | |
| if pref in breed_size: | |
| bonus += 0.25 # 尺寸匹配的強獎勵 | |
| elif (pref == 'small' and 'large' in breed_size) or \ | |
| (pref == 'large' and 'small' in breed_size): | |
| penalties += 0.15 # 尺寸不匹配的懲罰 | |
| # 增強活動水平匹配 | |
| breed_exercise = breed_characteristics.get('exercise_needs', '').lower() | |
| activity_prefs = lifestyle_keywords.get('activity_level', []) | |
| if 'high' in activity_prefs: | |
| if 'high' in breed_exercise or 'very high' in breed_exercise: | |
| bonus += 0.2 | |
| elif 'low' in breed_exercise: | |
| penalties += 0.2 | |
| elif 'low' in activity_prefs: | |
| if 'low' in breed_exercise: | |
| bonus += 0.2 | |
| elif 'high' in breed_exercise or 'very high' in breed_exercise: | |
| penalties += 0.25 | |
| elif 'moderate' in activity_prefs: | |
| if 'moderate' in breed_exercise: | |
| bonus += 0.15 | |
| # 增強家庭情況匹配 | |
| good_with_children = breed_characteristics.get('good_with_children', 'Yes') | |
| family_prefs = lifestyle_keywords.get('family_situation', []) | |
| if 'children' in family_prefs: | |
| if good_with_children == 'Yes': | |
| bonus += 0.15 | |
| else: | |
| penalties += 0.3 # 對非兒童友好品種的強懲罰 | |
| # 增強居住空間匹配 | |
| living_prefs = lifestyle_keywords.get('living_space', []) | |
| if 'apartment' in living_prefs: | |
| if 'small' in breed_size: | |
| bonus += 0.2 | |
| elif 'medium' in breed_size and 'low' in breed_exercise: | |
| bonus += 0.1 | |
| elif 'large' in breed_size or 'giant' in breed_size: | |
| penalties += 0.2 # 公寓中大型犬的懲罰 | |
| # 噪音偏好匹配 | |
| noise_prefs = lifestyle_keywords.get('noise_preference', []) | |
| temperament = breed_characteristics.get('temperament', '').lower() | |
| if 'low' in noise_prefs: | |
| # 獎勵安靜品種 | |
| if any(term in temperament for term in ['gentle', 'calm', 'quiet']): | |
| bonus += 0.1 | |
| # 照護水平匹配 | |
| grooming_needs = breed_characteristics.get('grooming_needs', '').lower() | |
| care_prefs = lifestyle_keywords.get('care_level', []) | |
| if 'low' in care_prefs and 'low' in grooming_needs: | |
| bonus += 0.1 | |
| elif 'high' in care_prefs and 'high' in grooming_needs: | |
| bonus += 0.1 | |
| elif 'low' in care_prefs and 'high' in grooming_needs: | |
| penalties += 0.15 | |
| # 特殊需求匹配 | |
| special_needs = lifestyle_keywords.get('special_needs', []) | |
| if 'guard' in special_needs: | |
| if any(term in temperament for term in ['protective', 'alert', 'watchful']): | |
| bonus += 0.1 | |
| elif 'companion' in special_needs: | |
| if any(term in temperament for term in ['affectionate', 'gentle', 'loyal']): | |
| bonus += 0.1 | |
| # 計算包含懲罰的最終獎勵 | |
| final_bonus = bonus - penalties | |
| return max(-0.3, min(0.5, final_bonus)) # 允許負獎勵但限制範圍 | |
| def apply_intelligent_trait_matching(self, recommendations: List[Dict], user_input: str) -> List[Dict]: | |
| """基於增強關鍵字提取和數據庫挖掘應用智能特徵匹配""" | |
| try: | |
| # 從用戶輸入提取增強關鍵字 | |
| extracted_keywords = self._extract_enhanced_lifestyle_keywords(user_input) | |
| # 對每個推薦應用智能特徵匹配 | |
| enhanced_recommendations = [] | |
| for rec in recommendations: | |
| breed_name = rec['breed'].replace(' ', '_') | |
| # 獲取品種數據庫信息 | |
| breed_info = get_dog_description(breed_name) or {} | |
| # 計算智能特徵獎勵 | |
| intelligence_bonus = 0.0 | |
| trait_match_details = {} | |
| # 1. 智力匹配 | |
| if extracted_keywords.get('intelligence_preference'): | |
| intelligence_pref = extracted_keywords['intelligence_preference'][0] | |
| breed_desc = breed_info.get('Description', '').lower() | |
| if intelligence_pref == 'high': | |
| if any(word in breed_desc for word in ['intelligent', 'smart', 'clever', 'quick to learn', 'trainable']): | |
| intelligence_bonus += 0.05 | |
| trait_match_details['intelligence_match'] = 'High intelligence match detected' | |
| elif any(word in breed_desc for word in ['stubborn', 'independent', 'difficult']): | |
| intelligence_bonus -= 0.02 | |
| trait_match_details['intelligence_warning'] = 'May be challenging to train' | |
| elif intelligence_pref == 'independent': | |
| if any(word in breed_desc for word in ['independent', 'stubborn', 'strong-willed']): | |
| intelligence_bonus += 0.03 | |
| trait_match_details['independence_match'] = 'Independent nature match' | |
| # 2. 美容偏好匹配 | |
| if extracted_keywords.get('grooming_preference'): | |
| grooming_pref = extracted_keywords['grooming_preference'][0] | |
| breed_grooming = breed_info.get('Grooming Needs', '').lower() | |
| if grooming_pref == 'low' and 'low' in breed_grooming: | |
| intelligence_bonus += 0.03 | |
| trait_match_details['grooming_match'] = 'Low maintenance grooming match' | |
| elif grooming_pref == 'high' and 'high' in breed_grooming: | |
| intelligence_bonus += 0.03 | |
| trait_match_details['grooming_match'] = 'High maintenance grooming match' | |
| elif grooming_pref == 'low' and 'high' in breed_grooming: | |
| intelligence_bonus -= 0.04 | |
| trait_match_details['grooming_mismatch'] = 'High grooming needs may not suit preferences' | |
| # 3. 氣質偏好匹配 | |
| if extracted_keywords.get('temperament_preference'): | |
| temp_prefs = extracted_keywords['temperament_preference'] | |
| breed_temperament = breed_info.get('Temperament', '').lower() | |
| breed_desc = breed_info.get('Description', '').lower() | |
| temp_text = (breed_temperament + ' ' + breed_desc).lower() | |
| for temp_pref in temp_prefs: | |
| if temp_pref == 'gentle' and any(word in temp_text for word in ['gentle', 'calm', 'peaceful', 'mild']): | |
| intelligence_bonus += 0.04 | |
| trait_match_details['temperament_match'] = f'Gentle temperament match: {temp_pref}' | |
| elif temp_pref == 'playful' and any(word in temp_text for word in ['playful', 'energetic', 'lively', 'fun']): | |
| intelligence_bonus += 0.04 | |
| trait_match_details['temperament_match'] = f'Playful temperament match: {temp_pref}' | |
| elif temp_pref == 'protective' and any(word in temp_text for word in ['protective', 'guard', 'alert', 'watchful']): | |
| intelligence_bonus += 0.04 | |
| trait_match_details['temperament_match'] = f'Protective temperament match: {temp_pref}' | |
| elif temp_pref == 'friendly' and any(word in temp_text for word in ['friendly', 'social', 'outgoing', 'people']): | |
| intelligence_bonus += 0.04 | |
| trait_match_details['temperament_match'] = f'Friendly temperament match: {temp_pref}' | |
| # 4. 經驗水平匹配 | |
| if extracted_keywords.get('experience_level'): | |
| exp_level = extracted_keywords['experience_level'][0] | |
| breed_desc = breed_info.get('Description', '').lower() | |
| if exp_level == 'beginner': | |
| # 為初學者偏愛易於處理的品種 | |
| if any(word in breed_desc for word in ['easy', 'gentle', 'good for beginners', 'family', 'calm']): | |
| intelligence_bonus += 0.06 | |
| trait_match_details['beginner_friendly'] = 'Good choice for first-time owners' | |
| elif any(word in breed_desc for word in ['challenging', 'dominant', 'requires experience', 'strong-willed']): | |
| intelligence_bonus -= 0.08 | |
| trait_match_details['experience_warning'] = 'May be challenging for first-time owners' | |
| elif exp_level == 'advanced': | |
| # 高級用戶可以處理更具挑戰性的品種 | |
| if any(word in breed_desc for word in ['working', 'requires experience', 'intelligent', 'strong']): | |
| intelligence_bonus += 0.03 | |
| trait_match_details['advanced_suitable'] = 'Good match for experienced owners' | |
| # 5. 壽命偏好匹配 | |
| if extracted_keywords.get('lifespan_preference'): | |
| lifespan_pref = extracted_keywords['lifespan_preference'][0] | |
| breed_lifespan = breed_info.get('Lifespan', '10-12 years') | |
| try: | |
| import re | |
| years = re.findall(r'\d+', breed_lifespan) | |
| if years: | |
| avg_years = sum(int(y) for y in years) / len(years) | |
| if lifespan_pref == 'long' and avg_years >= 13: | |
| intelligence_bonus += 0.02 | |
| trait_match_details['longevity_match'] = f'Long lifespan match: {breed_lifespan}' | |
| elif lifespan_pref == 'healthy' and avg_years >= 12: | |
| intelligence_bonus += 0.02 | |
| trait_match_details['health_match'] = f'Healthy lifespan: {breed_lifespan}' | |
| except: | |
| pass | |
| # 將智力獎勵應用到總分 | |
| original_score = rec['overall_score'] | |
| enhanced_score = min(1.0, original_score + intelligence_bonus) | |
| # 創建包含特徵匹配詳細信息的增強推薦 | |
| enhanced_rec = rec.copy() | |
| enhanced_rec['overall_score'] = enhanced_score | |
| enhanced_rec['intelligence_bonus'] = intelligence_bonus | |
| enhanced_rec['trait_match_details'] = trait_match_details | |
| # 如果發生顯著增強,添加詳細說明 | |
| if abs(intelligence_bonus) > 0.02: | |
| enhancement_explanation = [] | |
| for detail_key, detail_value in trait_match_details.items(): | |
| enhancement_explanation.append(detail_value) | |
| if enhancement_explanation: | |
| current_explanation = enhanced_rec.get('explanation', '') | |
| enhanced_explanation = current_explanation + f" Enhanced matching: {'; '.join(enhancement_explanation)}" | |
| enhanced_rec['explanation'] = enhanced_explanation | |
| enhanced_recommendations.append(enhanced_rec) | |
| # 按增強總分重新排序 | |
| enhanced_recommendations.sort(key=lambda x: x['overall_score'], reverse=True) | |
| # 更新排名 | |
| for i, rec in enumerate(enhanced_recommendations): | |
| rec['rank'] = i + 1 | |
| print(f"Applied intelligent trait matching with average bonus: {sum(r['intelligence_bonus'] for r in enhanced_recommendations) / len(enhanced_recommendations):.3f}") | |
| return enhanced_recommendations | |
| except Exception as e: | |
| print(f"Error in intelligent trait matching: {str(e)}") | |
| # 如果特徵匹配失敗,返回原始推薦 | |
| return recommendations | |
| def _extract_enhanced_lifestyle_keywords(self, user_input: str) -> Dict[str, List[str]]: | |
| """提取增強的生活方式關鍵字(用於智能特徵匹配)""" | |
| keywords = { | |
| 'intelligence_preference': [], | |
| 'grooming_preference': [], | |
| 'temperament_preference': [], | |
| 'experience_level': [], | |
| 'lifespan_preference': [] | |
| } | |
| text = user_input.lower() | |
| # 智力偏好檢測 | |
| smart_terms = ['smart', 'intelligent', 'clever', 'bright', 'quick learner', 'easy to train', 'trainable', 'genius', 'brilliant'] | |
| independent_terms = ['independent', 'stubborn', 'strong-willed', 'less trainable', 'thinks for themselves'] | |
| if any(term in text for term in smart_terms): | |
| keywords['intelligence_preference'].append('high') | |
| if any(term in text for term in independent_terms): | |
| keywords['intelligence_preference'].append('independent') | |
| # 美容偏好檢測 | |
| low_grooming_terms = ['low grooming', 'minimal grooming', 'easy care', 'wash and wear', 'no grooming', 'simple coat'] | |
| high_grooming_terms = ['high grooming', 'professional grooming', 'lots of care', 'high maintenance coat', 'daily brushing', 'regular grooming'] | |
| if any(term in text for term in low_grooming_terms): | |
| keywords['grooming_preference'].append('low') | |
| if any(term in text for term in high_grooming_terms): | |
| keywords['grooming_preference'].append('high') | |
| # 氣質偏好檢測 | |
| gentle_terms = ['gentle', 'calm', 'peaceful', 'laid back', 'chill', 'mellow', 'docile'] | |
| playful_terms = ['playful', 'energetic', 'fun', 'active personality', 'lively', 'spirited', 'bouncy'] | |
| protective_terms = ['protective', 'guard', 'watchdog', 'alert', 'vigilant', 'defensive'] | |
| friendly_terms = ['friendly', 'social', 'outgoing', 'loves people', 'sociable', 'gregarious'] | |
| if any(term in text for term in gentle_terms): | |
| keywords['temperament_preference'].append('gentle') | |
| if any(term in text for term in playful_terms): | |
| keywords['temperament_preference'].append('playful') | |
| if any(term in text for term in protective_terms): | |
| keywords['temperament_preference'].append('protective') | |
| if any(term in text for term in friendly_terms): | |
| keywords['temperament_preference'].append('friendly') | |
| # 經驗水平檢測 | |
| beginner_terms = ['first time', 'beginner', 'new to dogs', 'never had', 'novice', 'inexperienced'] | |
| advanced_terms = ['experienced', 'advanced', 'dog expert', 'many dogs before', 'professional', 'seasoned'] | |
| if any(term in text for term in beginner_terms): | |
| keywords['experience_level'].append('beginner') | |
| if any(term in text for term in advanced_terms): | |
| keywords['experience_level'].append('advanced') | |
| # 壽命偏好檢測 | |
| long_lived_terms = ['long lived', 'long lifespan', 'live long', 'many years', '15+ years', 'longevity'] | |
| healthy_terms = ['healthy breed', 'few health issues', 'robust', 'hardy', 'strong constitution'] | |
| if any(term in text for term in long_lived_terms): | |
| keywords['lifespan_preference'].append('long') | |
| if any(term in text for term in healthy_terms): | |
| keywords['lifespan_preference'].append('healthy') | |
| return keywords | |
| def calculate_enhanced_matching_score(self, breed: str, breed_info: dict, user_description: str, base_similarity: float) -> dict: | |
| """計算增強的匹配分數,基於用戶描述和品種特性""" | |
| try: | |
| user_desc = user_description.lower() | |
| # 分析用戶需求 | |
| space_requirements = self._analyze_space_requirements(user_desc) | |
| exercise_requirements = self._analyze_exercise_requirements(user_desc) | |
| noise_requirements = self._analyze_noise_requirements(user_desc) | |
| size_requirements = self._analyze_size_requirements(user_desc) | |
| family_requirements = self._analyze_family_requirements(user_desc) | |
| # 獲取品種特性 | |
| breed_size = breed_info.get('Size', '').lower() | |
| breed_exercise = breed_info.get('Exercise Needs', '').lower() | |
| breed_noise = breed_noise_info.get(breed, {}).get('noise_level', 'moderate').lower() | |
| breed_temperament = breed_info.get('Temperament', '').lower() | |
| breed_good_with_children = breed_info.get('Good with Children', '').lower() | |
| # 計算各維度匹配分數 | |
| dimension_scores = {} | |
| # 空間匹配 (30% 權重) | |
| space_score = self._calculate_space_compatibility(space_requirements, breed_size, breed_exercise) | |
| dimension_scores['space'] = space_score | |
| # 運動需求匹配 (25% 權重) | |
| exercise_score = self._calculate_exercise_compatibility(exercise_requirements, breed_exercise) | |
| dimension_scores['exercise'] = exercise_score | |
| # 噪音匹配 (20% 權重) | |
| noise_score = self._calculate_noise_compatibility(noise_requirements, breed_noise) | |
| dimension_scores['noise'] = noise_score | |
| # 體型匹配 (15% 權重) | |
| size_score = self._calculate_size_compatibility(size_requirements, breed_size) | |
| dimension_scores['grooming'] = min(0.9, base_similarity + 0.1) # 美容需求基於語意相似度 | |
| # 家庭相容性 (10% 權重) | |
| family_score = self._calculate_family_compatibility(family_requirements, breed_good_with_children, breed_temperament) | |
| dimension_scores['family'] = family_score | |
| dimension_scores['experience'] = min(0.9, base_similarity + 0.05) # 經驗需求基於語意相似度 | |
| # 應用硬約束過濾 | |
| constraint_penalty = self._apply_hard_constraints_enhanced(user_desc, breed_info) | |
| # 計算加權總分 - 精確化維度權重配置 | |
| # 根據指導建議重新平衡維度權重 | |
| weighted_score = ( | |
| space_score * 0.30 + # 空間相容性(降低5%) | |
| exercise_score * 0.28 + # 運動需求匹配(降低2%) | |
| noise_score * 0.18 + # 噪音控制(提升3%) | |
| family_score * 0.12 + # 家庭相容性(提升2%) | |
| size_score * 0.08 + # 體型匹配(降低2%) | |
| min(0.9, base_similarity + 0.1) * 0.04 # 護理需求(新增獨立權重) | |
| ) | |
| # 優化完美匹配獎勵機制 - 降低觸發門檻並增加層次 | |
| perfect_match_bonus = 0.0 | |
| if space_score >= 0.88 and exercise_score >= 0.88 and noise_score >= 0.85: | |
| perfect_match_bonus = 0.08 # 卓越匹配獎勵 | |
| elif space_score >= 0.82 and exercise_score >= 0.82 and noise_score >= 0.75: | |
| perfect_match_bonus = 0.04 # 優秀匹配獎勵 | |
| elif space_score >= 0.75 and exercise_score >= 0.75: | |
| perfect_match_bonus = 0.02 # 良好匹配獎勵 | |
| # 結合語意相似度與維度匹配 - 調整為75%維度匹配 25%語義相似度 | |
| base_combined_score = (weighted_score * 0.75 + base_similarity * 0.25) + perfect_match_bonus | |
| # 應用漸進式約束懲罰,但確保基礎分數保障 | |
| raw_final_score = base_combined_score + constraint_penalty | |
| # 實施動態分數保障機制 - 提升至40-42%基礎分數 | |
| # 根據品種特性動態調整基礎分數 | |
| base_guaranteed_score = 0.42 # 提升基礎保障分數 | |
| # 特殊品種基礎分數調整 | |
| high_adaptability_breeds = ['French_Bulldog', 'Pug', 'Golden_Retriever', 'Labrador_Retriever'] | |
| if any(breed in breed for breed in high_adaptability_breeds): | |
| base_guaranteed_score = 0.45 # 高適應性品種更高基礎分數 | |
| # 動態分數分佈優化 | |
| if raw_final_score >= base_guaranteed_score: | |
| # 對於高分品種,實施適度壓縮避免過度集中 | |
| if raw_final_score > 0.85: | |
| compression_factor = 0.92 # 輕度壓縮高分 | |
| final_score = 0.85 + (raw_final_score - 0.85) * compression_factor | |
| else: | |
| final_score = raw_final_score | |
| final_score = min(0.93, final_score) # 降低最高分數限制 | |
| else: | |
| # 對於低分品種,使用改進的保障機制 | |
| normalized_raw_score = max(0.15, raw_final_score) | |
| # 基礎保障75% + 實際計算25%,保持一定區分度 | |
| final_score = base_guaranteed_score * 0.75 + normalized_raw_score * 0.25 | |
| final_score = max(base_guaranteed_score, min(0.93, final_score)) | |
| lifestyle_bonus = max(0.0, weighted_score - base_similarity) | |
| return { | |
| 'final_score': final_score, | |
| 'weighted_score': weighted_score, | |
| 'lifestyle_bonus': lifestyle_bonus, | |
| 'dimension_scores': dimension_scores, | |
| 'constraint_penalty': constraint_penalty | |
| } | |
| except Exception as e: | |
| print(f"Error in enhanced matching calculation for {breed}: {str(e)}") | |
| return { | |
| 'final_score': base_similarity, | |
| 'weighted_score': base_similarity, | |
| 'lifestyle_bonus': 0.0, | |
| 'dimension_scores': { | |
| 'space': base_similarity * 0.9, | |
| 'exercise': base_similarity * 0.85, | |
| 'grooming': base_similarity * 0.8, | |
| 'experience': base_similarity * 0.75, | |
| 'noise': base_similarity * 0.7, | |
| 'family': base_similarity * 0.65 | |
| }, | |
| 'constraint_penalty': 0.0 | |
| } | |
| def _analyze_space_requirements(self, user_desc: str) -> dict: | |
| """分析空間需求 - 增強中等活動量識別""" | |
| requirements = {'type': 'unknown', 'size': 'medium', 'importance': 0.5} | |
| if any(word in user_desc for word in ['apartment', 'small apartment', 'small space', 'condo', 'flat']): | |
| requirements['type'] = 'apartment' | |
| requirements['size'] = 'small' | |
| requirements['importance'] = 0.95 # 提高重要性 | |
| elif any(word in user_desc for word in ['medium-sized house', 'medium house', 'townhouse']): | |
| requirements['type'] = 'medium_house' | |
| requirements['size'] = 'medium' | |
| requirements['importance'] = 0.8 # 中等活動量用戶的特殊標記 | |
| elif any(word in user_desc for word in ['large house', 'big house', 'yard', 'garden', 'large space', 'backyard']): | |
| requirements['type'] = 'house' | |
| requirements['size'] = 'large' | |
| requirements['importance'] = 0.7 | |
| return requirements | |
| def _analyze_exercise_requirements(self, user_desc: str) -> dict: | |
| """分析運動需求 - 增強中等活動量識別""" | |
| requirements = {'level': 'moderate', 'importance': 0.5} | |
| # 低運動量識別 | |
| if any(word in user_desc for word in ["don't exercise", "don't exercise much", "low exercise", "minimal", "lazy", "not active"]): | |
| requirements['level'] = 'low' | |
| requirements['importance'] = 0.95 | |
| # 中等運動量的精確識別 | |
| elif any(phrase in user_desc for phrase in ['30 minutes', 'half hour', 'moderate', 'balanced', 'walk about']): | |
| if 'walk' in user_desc or 'daily' in user_desc: | |
| requirements['level'] = 'moderate' | |
| requirements['importance'] = 0.85 # 中等活動量的特殊標記 | |
| # 高運動量識別 | |
| elif any(word in user_desc for word in ['active', 'hiking', 'outdoor activities', 'running', 'outdoors', 'love hiking']): | |
| requirements['level'] = 'high' | |
| requirements['importance'] = 0.9 | |
| return requirements | |
| def _analyze_noise_requirements(self, user_desc: str) -> dict: | |
| """分析噪音需求""" | |
| requirements = {'tolerance': 'medium', 'importance': 0.5} | |
| if any(word in user_desc for word in ['quiet', 'no bark', "won't bark", "doesn't bark", 'silent', 'peaceful']): | |
| requirements['tolerance'] = 'low' | |
| requirements['importance'] = 0.9 | |
| elif any(word in user_desc for word in ['loud', 'barking ok', 'noise ok']): | |
| requirements['tolerance'] = 'high' | |
| requirements['importance'] = 0.7 | |
| return requirements | |
| def _analyze_size_requirements(self, user_desc: str) -> dict: | |
| """分析體型需求""" | |
| requirements = {'preferred': 'any', 'importance': 0.5} | |
| if any(word in user_desc for word in ['small', 'tiny', 'little', 'lap dog', 'compact']): | |
| requirements['preferred'] = 'small' | |
| requirements['importance'] = 0.8 | |
| elif any(word in user_desc for word in ['large', 'big', 'giant']): | |
| requirements['preferred'] = 'large' | |
| requirements['importance'] = 0.8 | |
| return requirements | |
| def _analyze_family_requirements(self, user_desc: str) -> dict: | |
| """分析家庭需求""" | |
| requirements = {'children': False, 'importance': 0.3} | |
| if any(word in user_desc for word in ['children', 'kids', 'family', 'child']): | |
| requirements['children'] = True | |
| requirements['importance'] = 0.8 | |
| return requirements | |
| def _calculate_space_compatibility(self, space_req: dict, breed_size: str, breed_exercise: str) -> float: | |
| """計算空間相容性分數 - 增強中等活動量處理""" | |
| if space_req['type'] == 'apartment': | |
| if 'small' in breed_size or 'toy' in breed_size: | |
| base_score = 0.95 | |
| elif 'medium' in breed_size: | |
| if 'low' in breed_exercise: | |
| base_score = 0.75 | |
| else: | |
| base_score = 0.45 # 降低中型犬在公寓的分數 | |
| elif 'large' in breed_size: | |
| base_score = 0.05 # 大型犬極度不適合公寓 | |
| elif 'giant' in breed_size: | |
| base_score = 0.01 # 超大型犬完全不適合公寓 | |
| else: | |
| base_score = 0.7 | |
| elif space_req['type'] == 'medium_house': | |
| # 中型房屋的特殊處理 - 適合中等活動量用戶 | |
| if 'small' in breed_size or 'toy' in breed_size: | |
| base_score = 0.9 | |
| elif 'medium' in breed_size: | |
| base_score = 0.95 # 中型犬在中型房屋很適合 | |
| elif 'large' in breed_size: | |
| if 'moderate' in breed_exercise or 'low' in breed_exercise: | |
| base_score = 0.8 # 低運動量大型犬還可以 | |
| else: | |
| base_score = 0.6 # 高運動量大型犬不太適合 | |
| elif 'giant' in breed_size: | |
| base_score = 0.3 # 超大型犬在中型房屋不太適合 | |
| else: | |
| base_score = 0.85 | |
| else: | |
| # 大型房屋的情況 | |
| if 'small' in breed_size or 'toy' in breed_size: | |
| base_score = 0.85 | |
| elif 'medium' in breed_size: | |
| base_score = 0.9 | |
| elif 'large' in breed_size or 'giant' in breed_size: | |
| base_score = 0.95 | |
| else: | |
| base_score = 0.8 | |
| return min(0.95, base_score) | |
| def _calculate_exercise_compatibility(self, exercise_req: dict, breed_exercise: str) -> float: | |
| """計算運動需求相容性分數 - 增強中等活動量處理""" | |
| if exercise_req['level'] == 'low': | |
| if 'low' in breed_exercise or 'minimal' in breed_exercise: | |
| return 0.95 | |
| elif 'moderate' in breed_exercise: | |
| return 0.5 # 降低不匹配分數 | |
| elif 'high' in breed_exercise: | |
| return 0.1 # 進一步降低高運動需求的匹配 | |
| else: | |
| return 0.7 | |
| elif exercise_req['level'] == 'high': | |
| if 'high' in breed_exercise: | |
| return 0.95 | |
| elif 'moderate' in breed_exercise: | |
| return 0.8 | |
| elif 'low' in breed_exercise: | |
| return 0.6 | |
| else: | |
| return 0.7 | |
| else: # moderate - 中等活動量的精確處理 | |
| if 'moderate' in breed_exercise: | |
| return 0.95 # 完美匹配 | |
| elif 'low' in breed_exercise: | |
| return 0.85 # 低運動需求的品種對中等活動量用戶也不錯 | |
| elif 'high' in breed_exercise: | |
| return 0.5 # 中等活動量用戶不太適合高運動需求品種 | |
| else: | |
| return 0.75 | |
| return 0.6 | |
| def _calculate_noise_compatibility(self, noise_req: dict, breed_noise: str) -> float: | |
| """計算噪音相容性分數,更好處理複合等級""" | |
| breed_noise_lower = breed_noise.lower() | |
| if noise_req['tolerance'] == 'low': | |
| if 'low' in breed_noise_lower and 'moderate' not in breed_noise_lower: | |
| return 0.95 # 純低噪音 | |
| elif 'low-moderate' in breed_noise_lower or 'low to moderate' in breed_noise_lower: | |
| return 0.8 # 低到中等噪音,還可接受 | |
| elif breed_noise_lower in ['moderate']: | |
| return 0.4 # 中等噪音有些問題 | |
| elif 'high' in breed_noise_lower: | |
| return 0.1 # 高噪音不適合 | |
| else: | |
| return 0.6 # 未知噪音水平,保守估計 | |
| elif noise_req['tolerance'] == 'high': | |
| if 'high' in breed_noise_lower: | |
| return 0.9 | |
| elif 'moderate' in breed_noise_lower: | |
| return 0.85 | |
| elif 'low' in breed_noise_lower: | |
| return 0.8 # 安靜犬對高容忍度的人也很好 | |
| else: | |
| return 0.8 | |
| else: # moderate tolerance | |
| if 'moderate' in breed_noise_lower: | |
| return 0.9 | |
| elif 'low' in breed_noise_lower: | |
| return 0.85 | |
| elif 'high' in breed_noise_lower: | |
| return 0.6 | |
| else: | |
| return 0.75 | |
| return 0.7 | |
| def _calculate_size_compatibility(self, size_req: dict, breed_size: str) -> float: | |
| """計算體型相容性分數""" | |
| if size_req['preferred'] == 'small': | |
| if any(word in breed_size for word in ['small', 'toy', 'tiny']): | |
| return 0.9 | |
| elif 'medium' in breed_size: | |
| return 0.6 | |
| else: | |
| return 0.3 | |
| elif size_req['preferred'] == 'large': | |
| if any(word in breed_size for word in ['large', 'giant']): | |
| return 0.9 | |
| elif 'medium' in breed_size: | |
| return 0.7 | |
| else: | |
| return 0.4 | |
| return 0.7 # 無特別偏好 | |
| def _calculate_family_compatibility(self, family_req: dict, good_with_children: str, temperament: str) -> float: | |
| """計算家庭相容性分數""" | |
| if family_req['children']: | |
| if 'yes' in good_with_children.lower(): | |
| return 0.9 | |
| elif any(word in temperament for word in ['gentle', 'patient', 'friendly']): | |
| return 0.8 | |
| elif 'no' in good_with_children.lower(): | |
| return 0.2 | |
| else: | |
| return 0.6 | |
| return 0.7 | |
| def _apply_hard_constraints_enhanced(self, user_desc: str, breed_info: dict) -> float: | |
| """應用品種特性感知的動態懲罰機制""" | |
| penalty = 0.0 | |
| # 建立懲罰衰減係數和補償機制 | |
| penalty_decay_factor = 0.7 | |
| breed_adaptability_bonus = 0.0 | |
| breed_size = breed_info.get('Size', '').lower() | |
| breed_exercise = breed_info.get('Exercise Needs', '').lower() | |
| breed_name = breed_info.get('Breed', '').replace(' ', '_') | |
| # 公寓空間約束 - 品種特性感知懲罰機制 | |
| if 'apartment' in user_desc or 'small apartment' in user_desc: | |
| if 'giant' in breed_size: | |
| base_penalty = -0.35 # 減少基礎懲罰 | |
| # 特定品種適應性補償 | |
| adaptable_giants = ['Mastiff', 'Great Dane'] # 相對安靜的巨型犬 | |
| if any(adapt_breed in breed_name for adapt_breed in adaptable_giants): | |
| breed_adaptability_bonus += 0.08 | |
| penalty += base_penalty * penalty_decay_factor | |
| elif 'large' in breed_size: | |
| base_penalty = -0.25 # 減少大型犬懲罰 | |
| # 適合公寓的大型犬補償 | |
| apartment_friendly_large = ['Greyhound', 'Great_Dane'] | |
| if any(apt_breed in breed_name for apt_breed in apartment_friendly_large): | |
| breed_adaptability_bonus += 0.06 | |
| penalty += base_penalty * penalty_decay_factor | |
| elif 'medium' in breed_size and 'high' in breed_exercise: | |
| penalty += -0.15 * penalty_decay_factor # 進一步減少懲罰 | |
| # 運動需求不匹配 - 品種特性感知懲罰機制 | |
| if any(phrase in user_desc for phrase in ["don't exercise", "not active", "low exercise", "don't exercise much"]): | |
| if 'high' in breed_exercise: | |
| base_penalty = -0.28 # 減少基礎懲罰 | |
| # 低維護高運動犬種補償 | |
| adaptable_high_energy = ['Greyhound', 'Whippet'] # 運動爆發型,平時安靜 | |
| if any(adapt_breed in breed_name for adapt_breed in adaptable_high_energy): | |
| breed_adaptability_bonus += 0.10 | |
| penalty += base_penalty * penalty_decay_factor | |
| elif 'moderate' in breed_exercise: | |
| penalty += -0.08 * penalty_decay_factor # 進一步減少懲罰 | |
| # 噪音控制需求不匹配 - 品種特性感知懲罰機制 | |
| if any(phrase in user_desc for phrase in ['quiet', "won't bark", "doesn't bark", "silent"]): | |
| breed_noise = breed_noise_info.get(breed_name, {}).get('noise_level', 'moderate').lower() | |
| if 'high' in breed_noise: | |
| base_penalty = -0.18 # 減少基礎懲罰 | |
| # 訓練性良好的高噪音品種補償 | |
| trainable_vocal_breeds = ['German_Shepherd', 'Golden_Retriever'] | |
| if any(train_breed in breed_name for train_breed in trainable_vocal_breeds): | |
| breed_adaptability_bonus += 0.05 | |
| penalty += base_penalty * penalty_decay_factor | |
| elif 'moderate' in breed_noise and 'low' not in breed_noise: | |
| penalty += -0.05 * penalty_decay_factor | |
| # 體型偏好不匹配 - 漸進式懲罰 | |
| if any(phrase in user_desc for phrase in ['small', 'tiny', 'little']): | |
| if 'giant' in breed_size: | |
| penalty -= 0.35 # 超大型犬懲罰 | |
| elif 'large' in breed_size: | |
| penalty -= 0.20 # 大型犬懲罰 | |
| # 中等活動量用戶的特殊約束處理 - 漸進式懲罰 | |
| moderate_activity_terms = ['30 minutes', 'half hour', 'moderate', 'balanced', 'medium-sized house'] | |
| if any(term in user_desc for term in moderate_activity_terms): | |
| # 超大型犬對中等活動量用戶的適度懲罰 | |
| giant_breeds = ['Saint Bernard', 'Tibetan Mastiff', 'Great Dane', 'Mastiff', 'Newfoundland'] | |
| if any(giant in breed_name for giant in giant_breeds) or 'giant' in breed_size: | |
| penalty -= 0.35 # 適度懲罰,不完全排除 | |
| # 中型房屋 + 超大型犬的額外考量 | |
| if 'medium-sized house' in user_desc and any(giant in breed_name for giant in giant_breeds): | |
| if not any(high_activity in user_desc for high_activity in ['hiking', 'running', 'active', 'outdoor activities']): | |
| penalty -= 0.15 # 輕度額外懲罰 | |
| # 30分鐘散步對極高運動需求品種的懲罰 | |
| if any(term in user_desc for term in ['30 minutes', 'half hour']) and 'walk' in user_desc: | |
| high_energy_breeds = ['Siberian Husky', 'Border Collie', 'Jack Russell Terrier', 'Weimaraner'] | |
| if any(he_breed in breed_name for he_breed in high_energy_breeds) and 'high' in breed_exercise: | |
| penalty -= 0.25 # 適度懲罰極高運動需求品種 | |
| # 添加特殊品種適應性補償機制 | |
| # 對於邊界適配品種,給予適度補償 | |
| boundary_adaptable_breeds = { | |
| 'Italian_Greyhound': 0.08, # 安靜、低維護的小型犬 | |
| 'Boston_Bull': 0.06, # 適應性強的小型犬 | |
| 'Havanese': 0.05, # 友好適應的小型犬 | |
| 'Silky_terrier': 0.04, # 安靜的玩具犬 | |
| 'Basset': 0.07 # 低能量但友好的中型犬 | |
| } | |
| if breed_name in boundary_adaptable_breeds: | |
| breed_adaptability_bonus += boundary_adaptable_breeds[breed_name] | |
| # 應用品種適應性補償並設置懲罰上限 | |
| final_penalty = penalty + breed_adaptability_bonus | |
| # 限制最大懲罰,避免單一約束主導評分 | |
| final_penalty = max(-0.4, final_penalty) | |
| return final_penalty | |
| def get_breed_characteristics_enhanced(self, breed: str) -> Dict[str, Any]: | |
| """獲取品種特徵""" | |
| breed_info = get_dog_description(breed) | |
| if not breed_info: | |
| return {} | |
| characteristics = { | |
| 'size': breed_info.get('Size', 'Unknown'), | |
| 'temperament': breed_info.get('Temperament', ''), | |
| 'exercise_needs': breed_info.get('Exercise Needs', 'Moderate'), | |
| 'grooming_needs': breed_info.get('Grooming Needs', 'Moderate'), | |
| 'good_with_children': breed_info.get('Good with Children', 'Unknown'), | |
| 'lifespan': breed_info.get('Lifespan', '10-12 years'), | |
| 'description': breed_info.get('Description', '') | |
| } | |
| # 添加噪音資訊 | |
| noise_info = breed_noise_info.get(breed, {}) | |
| characteristics['noise_level'] = noise_info.get('noise_level', 'moderate') | |
| return characteristics | |
| def get_breed_info_from_standardized(self, standardized_info) -> Dict[str, Any]: | |
| """將標準化品種信息轉換為字典格式""" | |
| try: | |
| size_map = {1: 'Tiny', 2: 'Small', 3: 'Medium', 4: 'Large', 5: 'Giant'} | |
| exercise_map = {1: 'Low', 2: 'Moderate', 3: 'High', 4: 'Very High'} | |
| care_map = {1: 'Low', 2: 'Moderate', 3: 'High'} | |
| return { | |
| 'Size': size_map.get(standardized_info.size_category, 'Medium'), | |
| 'Exercise Needs': exercise_map.get(standardized_info.exercise_level, 'Moderate'), | |
| 'Grooming Needs': care_map.get(standardized_info.care_complexity, 'Moderate'), | |
| 'Good with Children': 'Yes' if standardized_info.child_compatibility >= 0.8 else | |
| 'No' if standardized_info.child_compatibility <= 0.2 else 'Unknown', | |
| 'Temperament': 'Varies by individual', | |
| 'Lifespan': '10-12 years', | |
| 'Description': f'A {size_map.get(standardized_info.size_category, "medium")} sized breed' | |
| } | |
| except Exception as e: | |
| print(f"Error converting standardized info: {str(e)}") | |
| return {} | |
| def get_fallback_recommendations(self, top_k: int = 15) -> List[Dict[str, Any]]: | |
| """當增強系統失敗時獲取備用推薦""" | |
| try: | |
| safe_breeds = [ | |
| ('Labrador Retriever', 0.85), | |
| ('Golden Retriever', 0.82), | |
| ('Cavalier King Charles Spaniel', 0.80), | |
| ('French Bulldog', 0.78), | |
| ('Boston Terrier', 0.76), | |
| ('Bichon Frise', 0.74), | |
| ('Pug', 0.72), | |
| ('Cocker Spaniel', 0.70) | |
| ] | |
| recommendations = [] | |
| for i, (breed, score) in enumerate(safe_breeds[:top_k]): | |
| breed_info = get_dog_description(breed.replace(' ', '_')) or {} | |
| recommendation = { | |
| 'breed': breed, | |
| 'rank': i + 1, | |
| 'overall_score': score, | |
| 'final_score': score, | |
| 'semantic_score': score * 0.8, | |
| 'comparative_bonus': 0.0, | |
| 'lifestyle_bonus': 0.0, | |
| 'size': breed_info.get('Size', 'Unknown'), | |
| 'temperament': breed_info.get('Temperament', ''), | |
| 'exercise_needs': breed_info.get('Exercise Needs', 'Moderate'), | |
| 'grooming_needs': breed_info.get('Grooming Needs', 'Moderate'), | |
| 'good_with_children': breed_info.get('Good with Children', 'Yes'), | |
| 'lifespan': breed_info.get('Lifespan', '10-12 years'), | |
| 'description': breed_info.get('Description', ''), | |
| 'search_type': 'fallback' | |
| } | |
| recommendations.append(recommendation) | |
| return recommendations | |
| except Exception as e: | |
| print(f"Error generating fallback recommendations: {str(e)}") | |
| return [] | |