FIX: delete iterative mode
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user