From 496e7ad59090e923964c805174e9edd40e1ddfaa Mon Sep 17 00:00:00 2001 From: pangkaicheng <924366729@qq.com> Date: Wed, 14 Jan 2026 12:21:14 +0800 Subject: [PATCH] FIX: delete iterative mode --- app/server/ChatbotAgent/agent_server.py | 1 - app/server/ChatbotAgent/core/prompt.py | 113 ---------- .../ChatbotAgent/core/stylist_agent_server.py | 199 +----------------- 3 files changed, 7 insertions(+), 306 deletions(-) diff --git a/app/server/ChatbotAgent/agent_server.py b/app/server/ChatbotAgent/agent_server.py index b59fae2..74c1cee 100644 --- a/app/server/ChatbotAgent/agent_server.py +++ b/app/server/ChatbotAgent/agent_server.py @@ -371,7 +371,6 @@ if __name__ == "__main__": stylist_agent_kwages['gender'] = "female" stylist_agent_kwages['callback_url'] = "" agent = AsyncStylistAgent(**stylist_agent_kwages) - # coro = agent.run_iterative_styling( coro = agent.run_quick_batch_styling( request_summary=request_summary, occasions=occasions, diff --git a/app/server/ChatbotAgent/core/prompt.py b/app/server/ChatbotAgent/core/prompt.py index a27a4b7..089796c 100644 --- a/app/server/ChatbotAgent/core/prompt.py +++ b/app/server/ChatbotAgent/core/prompt.py @@ -84,119 +84,6 @@ GENERAL_RULES_DICT = { * No watches, No hats, No sunglasses""", } -core_outfit_template = f""" -# ROLE: Professional Fashion Stylist Agent -You are a professional fashion stylist for {{gender}}. Your current task is to recommend the next logical item for the **{{current_category}}** stage. - -## 1. INTEGRATION LOGIC (How to Think) -1. **Analyze User Request**: Identify the target occasion, mood, and specific color/item preferences from the [Request Summary]. -2. **Apply Stylist Filter**: Use the [Stylist Guide] as the aesthetic filter. If the user request and Stylist Guide conflict, the user request takes precedence. -3. **Synthesis with Material**: Incorporate the [Material Hint] into your item descriptions to ensure the outfit is contextually appropriate for the {{occasion}} and facilitates high-accuracy vector search. -4. **Contextual Coordination**: Review the already selected items (provided in the user's message) to ensure the next item maintains silhouette balance (Loose vs. Fitted) and color harmony. - -## 2. RULE HIERARCHY -1. **USER REQUEST**: (Highest Priority) -2. **STYLIST CORE RULES**: (Secondary Priority) -3. **GENERAL COORDINATION RULES**: (Standard Professional Logic) - ---- - -## 3. CONTEXT & GUIDANCE -### [User Request Summary] -{{request_summary}} - -### [Target Occasion] -{{occasion}} - -### [Stylist Guide] -{{stylist_guide}} - -### [Material Hint] -{{material_hint}} - -### [General Rules] -{{general_rule}} - ---- - -## 4. CONSTRAINTS & WORKFLOW (Iterative Mode) -1. **Selection Pool**: You MUST only choose items from the following **[Allowed Subcategories]**. - **ALLOWED**: {{allowed_subcategories}} - -2. **Uniqueness Mandate**: Every item must follow the **absolute no-repeat rule for subcategories**. Each subcategory can appear **exactly once** in the entire outfit. - -3. **Action Selection**: You must output only one of two actions: "recommend_item" or "stop". - - **recommend_item**: Suggest the next single item following a logical sequence (e.g., top-down, inside-out). - - **stop**: Use ONLY when the Termination Conditions below are fully met. - -4. **Termination Conditions**: - - **CLOTHING Stage**: Achieved full body coverage (Top + Bottom OR Dress) AND satisfied all mandatory style elements. (Typically {{max_len}} items). - - **SHOES Stage**: Exactly one (1) pair has been recommended. - - **BAGS Stage**: Exactly one (1) item recommended, OR skipped if not mandated for the occasion. - ---- - -## 5. OUTPUT REQUIREMENT -- **Format**: Valid JSON object. -- **Description Quality**: Each 'description' field must be a precise string for vector search: **subcategory + Color + Fit/Silhouette + Material/Detail + Role in the Outfit.** -- **Reasoning**: Explain why this item is the next logical step and how it balances the User Request with Stylist DNA. - -Generate the JSON for the next item now. -""" - -accessories_template = f""" -# ROLE: Professional Fashion Stylist Agent -You are a professional fashion stylist for {{gender}}. Your current task is to finalize the look by recommending a complete set of accessories for the **{{current_category}}** stage. - -## 1. INTEGRATION LOGIC (How to Think) -1. **Outfit Coordination**: Analyze the existing clothing, bags and shoes (provided in the user's message). Accessories must enhance, not overwhelm, the established look. -2. **Apply Stylist Filter**: Strictly follow the [Stylist's Accessories Guide] regarding metal mixing (Gold/Silver), layering, and vintage/worn aesthetic preferences. -3. **Synthesis with Material**: Integrate [Material Hint] keywords into your descriptions to maintain consistency with the {{occasion}}. -4. **Prohibition Check**: Ensure NO items from the [Exclusion List] are included, regardless of user preference. - -## 2. RULE HIERARCHY -1. **ABSOLUTE PROHIBITION**: (Highest Priority - No {",".join(IGNORE_SUBCATEGORY)}) -2. **USER REQUEST**: (Secondary Priority) -3. **STYLIST CORE RULES**: (Aesthetic Filter) -4. **GENERAL COORDINATION RULES**: (Standard Professional Logic) - ---- -## 3. CONTEXT & GUIDANCE -### [User Request Summary] -{{request_summary}} - -### [Target Occasion] -{{occasion}} - -### [Existing Outfit Description] -(The existing items will be provided in the user's prompt) - -### [Stylist's Accessories Guide] -{{stylist_guide}} - -### [Material Hint] -{{material_hint}} - -### [General Rules] -{{general_rule}} - ---- -## 4. CONSTRAINTS & WORKFLOW (Batch Mode) -1. **Batch Recommendation**: You must output the **COMPLETE LIST of accessories** in a single response using the 'recommended_accessories' list. -2. **Quantity Constraint**: Recommend between 1 and {{max_len}} distinct items. -3. **Selection Pool**: Choose ONLY from: {{allowed_subcategories}}. -4. **Exclusion List**: Strictly FORBIDDEN to recommend: {",".join(IGNORE_SUBCATEGORY)}. -5. **Harmony**: Ensure all metals match the stylist's metal-mixing logic and all colors stay within the 3-tone limit. - ---- - -## 5. OUTPUT REQUIREMENT -- **Format**: Valid JSON matching the accessory API schema. -- **Description Quality**: Precise string for vector search: **subcategory + Color + Material/Detail + Specific Role in this Look.** -- **Reasoning**: Justify the accessory choices based on the clothing's silhouette and the user's requested mood. - -Generate the final accessory set now. -""" all_items_template = f""" # ROLE: Professional Fashion Stylist Agent diff --git a/app/server/ChatbotAgent/core/stylist_agent_server.py b/app/server/ChatbotAgent/core/stylist_agent_server.py index 7abb276..3bbf5d8 100644 --- a/app/server/ChatbotAgent/core/stylist_agent_server.py +++ b/app/server/ChatbotAgent/core/stylist_agent_server.py @@ -17,8 +17,6 @@ from app.config import settings from app.server.ChatbotAgent.core.prompt import ( GENERAL_RULES, GENERAL_RULES_DICT, - core_outfit_template, - accessories_template, all_items_template, build_iterative_schema, build_batch_schema @@ -322,101 +320,6 @@ class AsyncStylistAgent: else: return {} - async def _execute_iterative_recommendation( - self, - current_category: str, - system_prompt: str, - schema: Dict, - max_len: int, - occasions: List[str], - batch_sources: List[str], - user_id: str, - url: str - ): - recommend_timestep = 0 - gemini_data = {'action': 'start'} - existing_subcategories = [] - while recommend_timestep < max_len and gemini_data.get('action') != 'stop': - recommend_timestep += 1 - # 1. 准备用户输入(上下文) - user_input = self._build_user_input(current_category, ", ".join(existing_subcategories)) - - # 2. 把图片组装起来供api调用 - merged_image_path, image_bytes = await self._merge_images(self.outfit_id, user_id, self.stylist_name) - - # 3. 调用 Gemini Agent - gemini_response_text = await self._call_gemini( - user_input, - user_id, - self.outfit_id, - schema, - image_bytes, - system_prompt - ) - gemini_data = self._parse_gemini_response(gemini_response_text) - - if not gemini_data: - self.post_operation( - status="failed", - message="Agent returned invalid response, terminating process.", - callback_url=url, - img_path=merged_image_path, - ) - raise Exception("Agent 返回无效响应,终止流程。") - - # 处理推荐单品 - if gemini_data.get('action') == 'recommend_item': - subcategory = gemini_data.get('subcategory') - description = gemini_data.get('description') - - # 4a. 检查类别是否有效 (重要步骤) - allowed_subcategories = self._get_allowed_subcategories(occasions[0], current_category) - if subcategory not in allowed_subcategories: - self.post_operation( - status="continue", - message=f"Invalid subcategory recommended by Agent: {subcategory}. Requesting Agent to re-output.", - callback_url=url, - img_path=merged_image_path, - ) - continue - - # 4b. 在本地 DB 中查询单品 - new_item = self._get_next_item(description, current_category, subcategory, occasions, batch_sources, self.gender) - if not new_item: - self.post_operation( - status="continue", - message=f"No matching item is found. Ask Gemini to re-output.", - callback_url=url, - img_path=merged_image_path, - ) - continue - elif new_item['subcategory'] in [x['subcategory'] for x in self.outfit_items]: - self.post_operation( - status="continue", - message=f"{new_item['item_id']}'s subcategory {new_item['subcategory']} duplicated. Ask Gemini to re-output.", - callback_url=url, - img_path=merged_image_path, - ) - continue - elif new_item['item_id'] in [x['item_id'] for x in self.outfit_items]: - self.post_operation( - status="continue", - message=f"Item {new_item['item_id']} duplicated. Ask Gemini to re-output.", - callback_url=url, - img_path=merged_image_path, - ) - continue - else: - self.outfit_items.append(new_item) - existing_subcategories.append(new_item["subcategory"]) - self.post_operation( - status="ok", - message=f"Add new item {new_item['item_id']} in category {new_item['category']} successfully.", - callback_url=url, - img_path=merged_image_path, - ) - print(f"Stage {current_category.upper()}, Step {recommend_timestep}: {gemini_data}, found item: {new_item['item_id']}") - async def _execute_batch_recommendation( self, current_category: str, # this can be any category or all @@ -512,94 +415,6 @@ class AsyncStylistAgent: return cat return "unknown" - async def run_iterative_styling(self, request_summary, occasions, start_outfit: Optional[List] = None, batch_sources: List = [], user_id="test", callback_url=""): - start_time = time.monotonic() - STAGES = ['clothing', 'shoes', 'bags'] - # 深拷贝start_outfit 避免实例之间的参数泄漏 确保每个实例都有自己的 start_outfit 副本 - if start_outfit is None: - self.outfit_items = [] - else: - self.outfit_items = deepcopy(start_outfit) - stylist_guide = self._load_style_guide(self.stylist_name) - url = f'{callback_url}/api/style/callback' - if not occasions: - occasions = ["Casual"] - - """主流程控制循环。""" - print(f"--- Starting Agent (Outfit ID: {self.outfit_id}) ---") - for current_category in STAGES: - allowed_subcategories = self._get_allowed_subcategories(occasions[0], current_category) - max_len = min(4, len(allowed_subcategories)) if current_category == 'clothing' else 1 - - general_rule = GENERAL_RULES + GENERAL_RULES_DICT.get(current_category, "") - system_prompt = self._build_system_prompt( - core_outfit_template, - general_rule, - request_summary, - occasions[0], - stylist_guide, - current_category, - allowed_subcategories, - max_len - ) - if allowed_subcategories: - await self._execute_iterative_recommendation( - current_category, - system_prompt, - build_iterative_schema(current_category), - max_len, - occasions, - batch_sources, - user_id, - url - ) - - # 根据stylist要求增加配饰 3-4个配饰 - MAX_LEN_ACC = 3 - current_category = 'accessories' - general_rule = GENERAL_RULES + GENERAL_RULES_DICT.get(current_category, "") - allowed_subcategories = self._get_allowed_subcategories(occasions[0], current_category) - acc_system_prompt = self._build_system_prompt( - accessories_template, - general_rule, - request_summary, - occasions[0], - stylist_guide, - current_category, - allowed_subcategories, - MAX_LEN_ACC - ) - if allowed_subcategories: - reason = await self._execute_batch_recommendation( - current_category, # can be 'accessories' or 'all' - acc_system_prompt, - build_batch_schema(specified_category=current_category, subcategory_list=allowed_subcategories), - occasions, - batch_sources, - user_id, - url - ) - else: - reason = "No allowed subcategories for accessories, skipping accessories recommendation." - - final_image_path, _ = await self._merge_images(self.outfit_id, user_id, self.stylist_name) - # 推荐完成返回 - response_data = self.post_operation( - status="stop", - message=reason, - callback_url=url, - img_path=final_image_path, - request_summary=request_summary, - occasions=occasions - ) - end_time = time.monotonic() - total_duration = end_time - start_time - if settings.LOCAL == 1: - with open(os.path.join(settings.OUTFIT_OUTPUT_DIR, self.stylist_name, f'{self.outfit_id}.json'), 'w') as f: - json.dump({"request_summary": request_summary, "occasions": occasions, "items": self.outfit_items, "total_duration": total_duration}, f, indent=2) - - return response_data, total_duration - async def run_quick_batch_styling(self, request_summary, occasions, start_outfit: Optional[List] = None, batch_sources: List = [], user_id="test", callback_url=""): start_time = time.monotonic() # 深拷贝start_outfit 避免实例之间的参数泄漏 确保每个实例都有自己的 start_outfit 副本 @@ -616,16 +431,16 @@ class AsyncStylistAgent: logger.info(f"""--- Starting Agent (Outfit ID: {self.outfit_id}) --- Occasion: {occasions[0]}. Stylist: {self.stylist_name}. User ID: {user_id}. Request Summary: {request_summary}. Batch sources: {batch_sources}""") - general_rules = "\n".join(GENERAL_RULES_DICT.values()) + general_rules = "\n".join(GENERAL_RULES_DICT.values()) + '\n' + GENERAL_RULES allowed_subcategories = self._get_allowed_subcategories(occasions[0], "all") system_prompt = self._build_system_prompt( all_items_template, - general_rules, - request_summary, - occasions[0], - stylist_guide, - "", - allowed_subcategories, + general_rules, + request_summary, + occasions[0], + stylist_guide, + "", + allowed_subcategories, MAX_LEN )