PawMatchAI / matching_score_calculator.py
DawnC's picture
Upload 6 files
595e0a5 verified
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 []