FIX: delete iterative mode

This commit is contained in:
pangkaicheng
2026-01-14 12:21:14 +08:00
parent a7b101253b
commit 496e7ad590
3 changed files with 7 additions and 306 deletions

View File

@@ -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,

View File

@@ -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

View File

@@ -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,7 +431,7 @@ 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,