from typing import List, Optional, Dict import json DEFAULT_CATEGORIES = ["材料", "功能", "用途", "使用族群", "特性"] CATEGORY_DESCRIPTIONS = { "材料": "物件由什麼材料組成", "功能": "物件能做什麼", "用途": "物件在什麼場景使用", "使用族群": "誰會使用這個物件", "特性": "物件有什麼特徵", } def get_attribute_prompt(query: str, categories: Optional[List[str]] = None) -> str: """Generate prompt with causal chain structure.""" prompt = f"""分析「{query}」的屬性,以因果鏈方式呈現:材料→功能→用途→使用族群。 請列出 3-5 種材料,每種材料延伸出完整因果鏈。 JSON 格式: {{"name": "{query}", "children": [{{"name": "材料名", "category": "材料", "children": [{{"name": "功能名", "category": "功能", "children": [{{"name": "用途名", "category": "用途", "children": [{{"name": "族群名", "category": "使用族群"}}]}}]}}]}}]}} 只回傳 JSON。""" return prompt def get_step1_attributes_prompt(query: str) -> str: """Step 1: 生成各類別的屬性列表(平行結構)""" return f"""/no_think 分析「{query}」,列出以下四個類別的屬性。每個類別列出 3-5 個常見屬性。 只回傳 JSON,格式如下: {{"materials": ["材料1", "材料2", "材料3"], "functions": ["功能1", "功能2", "功能3"], "usages": ["用途1", "用途2", "用途3"], "users": ["族群1", "族群2", "族群3"]}} 物件:{query}""" def get_step2_causal_chain_prompt( query: str, materials: List[str], functions: List[str], usages: List[str], users: List[str], existing_chains: List[dict], chain_index: int ) -> str: """Step 2: 生成單條因果鏈""" existing_chains_text = "" if existing_chains: chains_list = [ f"- {c['material']} → {c['function']} → {c['usage']} → {c['user']}" for c in existing_chains ] existing_chains_text = f""" 【已生成的因果鏈,請勿重複】 {chr(10).join(chains_list)} """ return f"""/no_think 為「{query}」生成第 {chain_index} 條因果鏈。 【可選材料】{', '.join(materials)} 【可選功能】{', '.join(functions)} 【可選用途】{', '.join(usages)} 【可選族群】{', '.join(users)} {existing_chains_text} 【規則】 1. 從每個類別選擇一個屬性,組成合理的因果鏈 2. 因果關係必須合邏輯(材料決定功能,功能決定用途,用途決定族群) 3. 不要與已生成的因果鏈重複 只回傳 JSON: {{"material": "選擇的材料", "function": "選擇的功能", "usage": "選擇的用途", "user": "選擇的族群"}}""" def get_flat_attribute_prompt(query: str, categories: Optional[List[str]] = None) -> str: """Generate prompt with flat/parallel categories (original design).""" cats = categories if categories else DEFAULT_CATEGORIES # Build category list category_lines = [] for cat in cats: desc = CATEGORY_DESCRIPTIONS.get(cat, f"{cat}的相關屬性") category_lines.append(f"- {cat}:{desc}") categories_text = "\n".join(category_lines) prompt = f"""/no_think 你是一個物件屬性分析專家。請將用戶輸入的物件拆解成以下屬性類別。 【必須包含的類別】 {categories_text} 【重要】回傳格式必須是合法的 JSON,每個節點都必須有 "name" 欄位: ```json {{ "name": "物件名稱", "children": [ {{ "name": "類別名稱", "children": [ {{"name": "屬性1"}}, {{"name": "屬性2"}} ] }} ] }} ``` 只回傳 JSON,不要有任何其他文字。 用戶輸入:{query}""" return prompt # ===== Dynamic category system prompts ===== def get_step0_category_analysis_prompt(query: str, suggested_count: int = 3) -> str: """Step 0: LLM 分析建議類別""" return f"""/no_think 分析「{query}」,建議 {suggested_count} 個最適合的屬性類別來描述它。 【常見類別參考】材料、功能、用途、使用族群、特性、形狀、顏色、尺寸、品牌、價格區間 【重要】 1. 選擇最能描述此物件本質的類別 2. 類別之間應該有邏輯關係(如:材料→功能→用途) 3. 不要選擇過於抽象或重複的類別 只回傳 JSON: {{ "categories": [ {{"name": "類別1", "description": "說明1", "order": 0}}, {{"name": "類別2", "description": "說明2", "order": 1}} ] }} 物件:{query}""" def get_step1_dynamic_attributes_prompt( query: str, categories: List # List[CategoryDefinition] ) -> str: """動態 Step 1 - 根據類別列表生成屬性""" # 按 order 排序並構建描述 sorted_cats = sorted(categories, key=lambda x: x.order if hasattr(x, 'order') else x.get('order', 0)) category_desc = "\n".join([ f"- {cat.name if hasattr(cat, 'name') else cat['name']}: {cat.description if hasattr(cat, 'description') else cat.get('description', '相關屬性')}" for cat in sorted_cats ]) category_keys = [cat.name if hasattr(cat, 'name') else cat['name'] for cat in sorted_cats] json_template = {cat: ["屬性1", "屬性2", "屬性3"] for cat in category_keys} return f"""/no_think 分析「{query}」,列出以下類別的屬性。每個類別列出 3-5 個常見屬性。 【類別列表】 {category_desc} 只回傳 JSON: {json.dumps(json_template, ensure_ascii=False, indent=2)} 物件:{query}""" def get_step2_dynamic_causal_chain_prompt( query: str, categories: List, # List[CategoryDefinition] attributes_by_category: Dict[str, List[str]], existing_chains: List[Dict[str, str]], chain_index: int ) -> str: """動態 Step 2 - 生成動態類別的因果鏈""" sorted_cats = sorted(categories, key=lambda x: x.order if hasattr(x, 'order') else x.get('order', 0)) # 構建可選屬性 available_attrs = "\n".join([ f"【{cat.name if hasattr(cat, 'name') else cat['name']}】{', '.join(attributes_by_category.get(cat.name if hasattr(cat, 'name') else cat['name'], []))}" for cat in sorted_cats ]) # 已生成的因果鏈 existing_text = "" if existing_chains: chains_list = [ " → ".join([chain.get(cat.name if hasattr(cat, 'name') else cat['name'], '?') for cat in sorted_cats]) for chain in existing_chains ] existing_text = f"\n【已生成,請勿重複】\n" + "\n".join([f"- {c}" for c in chains_list]) # JSON 模板 json_template = {cat.name if hasattr(cat, 'name') else cat['name']: f"選擇的{cat.name if hasattr(cat, 'name') else cat['name']}" for cat in sorted_cats} return f"""/no_think 為「{query}」生成第 {chain_index} 條因果鏈。 【可選屬性】 {available_attrs} {existing_text} 【規則】 1. 從每個類別選擇一個屬性 2. 因果關係必須合理 3. 不要重複 只回傳 JSON: {json.dumps(json_template, ensure_ascii=False, indent=2)}"""