FIX: delete iterative mode
This commit is contained in:
@@ -371,7 +371,6 @@ if __name__ == "__main__":
|
|||||||
stylist_agent_kwages['gender'] = "female"
|
stylist_agent_kwages['gender'] = "female"
|
||||||
stylist_agent_kwages['callback_url'] = ""
|
stylist_agent_kwages['callback_url'] = ""
|
||||||
agent = AsyncStylistAgent(**stylist_agent_kwages)
|
agent = AsyncStylistAgent(**stylist_agent_kwages)
|
||||||
# coro = agent.run_iterative_styling(
|
|
||||||
coro = agent.run_quick_batch_styling(
|
coro = agent.run_quick_batch_styling(
|
||||||
request_summary=request_summary,
|
request_summary=request_summary,
|
||||||
occasions=occasions,
|
occasions=occasions,
|
||||||
|
|||||||
@@ -84,119 +84,6 @@ GENERAL_RULES_DICT = {
|
|||||||
* No watches, No hats, No sunglasses""",
|
* 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"""
|
all_items_template = f"""
|
||||||
# ROLE: Professional Fashion Stylist Agent
|
# ROLE: Professional Fashion Stylist Agent
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ from app.config import settings
|
|||||||
from app.server.ChatbotAgent.core.prompt import (
|
from app.server.ChatbotAgent.core.prompt import (
|
||||||
GENERAL_RULES,
|
GENERAL_RULES,
|
||||||
GENERAL_RULES_DICT,
|
GENERAL_RULES_DICT,
|
||||||
core_outfit_template,
|
|
||||||
accessories_template,
|
|
||||||
all_items_template,
|
all_items_template,
|
||||||
build_iterative_schema,
|
build_iterative_schema,
|
||||||
build_batch_schema
|
build_batch_schema
|
||||||
@@ -322,101 +320,6 @@ class AsyncStylistAgent:
|
|||||||
else:
|
else:
|
||||||
return {}
|
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(
|
async def _execute_batch_recommendation(
|
||||||
self,
|
self,
|
||||||
current_category: str, # this can be any category or all
|
current_category: str, # this can be any category or all
|
||||||
@@ -512,94 +415,6 @@ class AsyncStylistAgent:
|
|||||||
return cat
|
return cat
|
||||||
return "unknown"
|
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=""):
|
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_time = time.monotonic()
|
||||||
# 深拷贝start_outfit 避免实例之间的参数泄漏 确保每个实例都有自己的 start_outfit 副本
|
# 深拷贝start_outfit 避免实例之间的参数泄漏 确保每个实例都有自己的 start_outfit 副本
|
||||||
@@ -616,16 +431,16 @@ class AsyncStylistAgent:
|
|||||||
logger.info(f"""--- Starting Agent (Outfit ID: {self.outfit_id}) ---
|
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}""")
|
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")
|
allowed_subcategories = self._get_allowed_subcategories(occasions[0], "all")
|
||||||
system_prompt = self._build_system_prompt(
|
system_prompt = self._build_system_prompt(
|
||||||
all_items_template,
|
all_items_template,
|
||||||
general_rules,
|
general_rules,
|
||||||
request_summary,
|
request_summary,
|
||||||
occasions[0],
|
occasions[0],
|
||||||
stylist_guide,
|
stylist_guide,
|
||||||
"",
|
"",
|
||||||
allowed_subcategories,
|
allowed_subcategories,
|
||||||
MAX_LEN
|
MAX_LEN
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user