hf_rabbit_life_poc / app_p /handler /recommendation_handler.py
SUMANA SUMANAKUL (ING)
debug
f30514b
raw
history blame
7.03 kB
import json
from pathlib import Path
import os
from langchain_google_genai import ChatGoogleGenerativeAI
# --- Data Loading ---
try:
print("Loading personas for RecommendationHandler module...")
# ABSOLUTE_PATH_TO_CONFIG = '/Users/jts-ai-sumana/rabbitlife_gemini/config/personas.json'
# with open(ABSOLUTE_PATH_TO_CONFIG, "r", encoding="utf-8") as f:
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
CONFIG_PATH = PROJECT_ROOT / "config" / "personas.json"
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
PERSONAS = json.load(f)
print(f"✅ Personas config loaded successfully from: {CONFIG_PATH}")
except Exception as e:
PERSONAS = {}
print(f"‼️ ERROR: Could not load personas.json: {e}")
# --- LLM Initialization ---
try:
INTEREST_LLM = ChatGoogleGenerativeAI(
model="gemini-2.5-flash",
google_api_key=os.getenv("GOOGLE_API_KEY"),
temperature=0,
)
print("✅ LLM for interest classification initialized.")
except Exception as e:
INTEREST_LLM = None
print(f"‼️ ERROR initializing LLM for RecommendationHandler: {e}")
# RECOMMENDATION ###
def generate_recommendation_from_profile(
age: int,
gender: str, # M, F
salary: int,
original_interest: str
) -> dict:
if not PERSONAS or not INTEREST_LLM:
return {"error": "Module not initialized correctly."}
# print(f"\n--- Generating Recommendation Profile ---")
# print(f"Input Data: Age={age}, Gender='{gender}', Salary={salary}, Interest='{original_interest}'")
# 1. หา Persona ที่ตรงกับอายุ
matched_persona = next((details for _, details in PERSONAS.items() if details.get("age_min", -1) <= age <= details.get("age_max", -1)), None)
if not matched_persona:
return {"error": f"No matching persona found for age {age}."}
# print(f"-> Matched Persona: '{matched_persona.get('persona_name', 'N/A')}'")
# 2. หา Tier ที่ตรงกับเงินเดือน
products_from_tier = []
salary_tiers = matched_persona.get('recommendations_by_salary', [])
for tier in salary_tiers:
if tier.get("salary_min", 0) <= salary <= tier.get("salary_max", float('inf')):
products_from_tier = tier.get("products", [])
# print(f"-> Matched Salary Tier: '{tier.get('tier_name')}'")
break
if not products_from_tier and salary_tiers:
products_from_tier = salary_tiers[0].get("products", [])
# 3. รวบรวม product จาก Tier และเพศ
all_tier_products = [p for p in products_from_tier if p.get('gender') == 'all' or p.get('gender') == gender]
# 4. กรองตาม Interest - LLM
interest_prompt = f"""Analyze the user's interest and classify it into ONE of these categories:
'ประกันคุ้มครองชีวิต', 'ประกันเพื่อการลงทุน', 'ประกันสุขภาพ', 'ประกันอุบัติเหตุ', 'ทั่วไป'.
User Interest: "{original_interest}"
Category:"""
response = INTEREST_LLM.invoke(interest_prompt)
target_insurance_type = response.content.strip()
# print(f"-> LLM classified interest as: '{target_insurance_type}'")
products_to_recommend = all_tier_products
searched_outside_tier = False
valid_types = ['ประกันเพื่อการลงทุน', 'ประกันสุขภาพ', 'ประกันอุบัติเหตุ', 'ประกันคุ้มครองชีวิต']
if target_insurance_type in valid_types:
# print(f"-> Filtering for interest: '{target_insurance_type}' across all tiers...")
all_persona_products = []
for tier_fallback in matched_persona.get('recommendations_by_salary', []):
all_persona_products.extend(tier_fallback.get('products', []))
# print(all_persona_products)
gender_filtered_products = [p for p in all_persona_products if p.get('gender') == 'all' or p.get('gender') == gender]
final_filtered_products = [p for p in gender_filtered_products if p.get('insurance_type') == target_insurance_type]
if final_filtered_products:
# print(f"-> Found {len(final_filtered_products)} matching products.")
products_to_recommend = final_filtered_products
else:
print(f"-> CRITICAL: No product of type '{target_insurance_type}' found anywhere in this persona.")
return {"error": f"ขออภัยค่ะ เราไม่พบผลิตภัณฑ์ประเภท '{target_insurance_type}' ที่เหมาะสมกับโปรไฟล์ของคุณในขณะนี้ค่ะ"}
# 5. ตรวจสอบและเพิ่มสัญญาหลักถ้าจำเป็น
has_basic_plan = any(p.get('plan_type') == 'Basic' for p in products_to_recommend)
auto_added_main_plan = False
if not has_basic_plan and any(p.get('plan_type') == 'Rider' for p in products_to_recommend):
# print("-> Filtered list only has Riders. Adding a default main plan.")
default_main_plans = [p for p in all_tier_products if p.get('plan_type') == 'Basic']
if default_main_plans:
products_to_recommend.insert(0, default_main_plans[0])
auto_added_main_plan = True
# 6. จัดกลุ่มและเตรียมข้อมูลสำหรับส่งคืน
main_plans_pre = [p for p in products_to_recommend if p.get('plan_type') == 'Basic']
main_plans = [dict(t) for t in {tuple(d.items()) for d in main_plans_pre}]
riders_pre = [p for p in products_to_recommend if p.get('plan_type') == 'Rider']
riders = [dict(t) for t in {tuple(d.items()) for d in riders_pre}]
print(main_plans)
main_plans_str = "ไม่มีแผนประกันหลักที่แนะนำ"
if main_plans:
main_plans_str = "\n".join([f"• **{p.get('product_name')}**: {p.get('product_description')}" for p in main_plans])
riders_str = "ไม่มีสัญญาเพิ่มเติมที่แนะนำ"
if riders:
riders_str = "\n".join([f"• **{p.get('product_name')}**: {p.get('product_description')}" for p in riders])
return {
"age": age, "gender": gender, "salary": f"{salary:,}",
"persona_name": matched_persona['persona_name'],
"persona_description": matched_persona['description'],
"original_interest": original_interest,
"main_plans_str": main_plans_str.strip(),
"riders_str": riders_str.strip(),
"auto_added_main_plan": auto_added_main_plan,
"searched_outside_tier": searched_outside_tier,
"interest_category": target_insurance_type if target_insurance_type != 'ทั่วไป' else 'ประกัน'
}