Compare commits
11 Commits
496e7ad590
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a36235d58e | |||
| 433aa3e751 | |||
| 7bf080b3e7 | |||
| 03c4759895 | |||
| 03ff6605a3 | |||
| 4b3b0f6aa8 | |||
| c798d37fdd | |||
| 43fd576da6 | |||
| 1bbb9c945e | |||
|
|
3ca6b16eaf | ||
|
|
f9d83f6a99 |
@@ -7,7 +7,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REMOTE_DEPLOY_PATH: /workspace/Trinity/Litserve_LC_Prod/lc_stylist_agent
|
REMOTE_DEPLOY_PATH: /workspace/LC_Workspace/LitServe_Server_Workspace/lc_stylist_agent
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 1.检出代码
|
- name: 1.检出代码
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ class Settings(BaseSettings):
|
|||||||
extra='ignore' # 忽略环境变量中多余的键
|
extra='ignore' # 忽略环境变量中多余的键
|
||||||
)
|
)
|
||||||
# 启动端口
|
# 启动端口
|
||||||
SERVE_PROD: int = Field(default=8000, description='')
|
SERVE_PORT: int = Field(default=8000, description='')
|
||||||
|
|
||||||
|
# 换脸模型服务
|
||||||
|
RE_FACE_MODEL_URL: str = Field(default="10.1.1.240:10071", description='')
|
||||||
|
|
||||||
# 调试配饰
|
# 调试配饰
|
||||||
LOCAL: int = Field(default=0, description="是否在本地运行,1表示本地运行,0表示生产环境运行")
|
LOCAL: int = Field(default=0, description="是否在本地运行,1表示本地运行,0表示生产环境运行")
|
||||||
|
|
||||||
|
|||||||
@@ -27,4 +27,4 @@ if __name__ == "__main__":
|
|||||||
agent_api = LCAgent(enable_async=True, api_path='/api/v1/agent')
|
agent_api = LCAgent(enable_async=True, api_path='/api/v1/agent')
|
||||||
reface_api = ReFace(api_path='/api/v1/reface')
|
reface_api = ReFace(api_path='/api/v1/reface')
|
||||||
server = ls.LitServer([chat_boot_api, agent_api, reface_api])
|
server = ls.LitServer([chat_boot_api, agent_api, reface_api])
|
||||||
server.run(port=settings.SERVE_PROD)
|
server.run(port=8000)
|
||||||
|
|||||||
@@ -49,10 +49,18 @@ class LCChatBot(ls.LitAPI):
|
|||||||
user_msg = Message(role=Role.USER, content=user_message)
|
user_msg = Message(role=Role.USER, content=user_message)
|
||||||
chat_history = self.redis.get_history(session_id)
|
chat_history = self.redis.get_history(session_id)
|
||||||
chat_history.append(user_msg)
|
chat_history.append(user_msg)
|
||||||
|
|
||||||
|
turn_count = sum(1 for msg in chat_history if msg.role == Role.USER)
|
||||||
|
|
||||||
if request.gender == 'male':
|
if request.gender == 'male':
|
||||||
prompt = BASIC_PROMPT.format(gender='men')
|
system_instruction = BASIC_PROMPT.format(gender='men')
|
||||||
else:
|
else:
|
||||||
prompt = BASIC_PROMPT.format(gender='women')
|
system_instruction = BASIC_PROMPT.format(gender='women')
|
||||||
|
|
||||||
|
if turn_count >= 3:
|
||||||
|
system_instruction += "\n\nCRITICAL: This is the final turn. Do not ask questions. Provide a brief summary and use the mandatory closing phrase."
|
||||||
|
else:
|
||||||
|
system_instruction += f"\n\nCURRENT STATE: This is turn {turn_count} of 3. Keep gathering info efficiently or provide a brief summary and use the mandatory closing phrase if you have sufficient information."
|
||||||
|
|
||||||
contents = []
|
contents = []
|
||||||
|
|
||||||
@@ -68,7 +76,7 @@ class LCChatBot(ls.LitAPI):
|
|||||||
model='gemini-2.5-flash',
|
model='gemini-2.5-flash',
|
||||||
contents=contents,
|
contents=contents,
|
||||||
config=types.GenerateContentConfig(
|
config=types.GenerateContentConfig(
|
||||||
system_instruction=prompt,
|
system_instruction=system_instruction,
|
||||||
# temperature=0.3,
|
# temperature=0.3,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -99,7 +107,7 @@ class LCChatBot(ls.LitAPI):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.stdout = open('permanent.log', 'w', encoding='utf-8')
|
# sys.stdout = open('permanent.log', 'w', encoding='utf-8')
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
@@ -137,23 +145,67 @@ if __name__ == "__main__":
|
|||||||
print(chunk, end="", flush=True)
|
print(chunk, end="", flush=True)
|
||||||
|
|
||||||
|
|
||||||
text_list = [
|
# text_list = [
|
||||||
'我要去参加好朋友的婚礼,你能帮我挑一套衣服吗?',
|
# '我要去参加好朋友的婚礼,你能帮我挑一套衣服吗?',
|
||||||
'I need something to wear for a big presentation at work tomorrow. I want to look powerful but still approachable.',
|
# 'I need something to wear for a big presentation at work tomorrow. I want to look powerful but still approachable.',
|
||||||
'Who do you think is the best world leader right now?',
|
# 'Who do you think is the best world leader right now?',
|
||||||
'I’m going on a trip to Paris next week and need some outfits.',
|
# 'I’m going on a trip to Paris next week and need some outfits.',
|
||||||
'Help me find a cool outfit for a rock concert. I hate wearing dresses.',
|
# 'Help me find a cool outfit for a rock concert. I hate wearing dresses.',
|
||||||
'I want to look very cool, 或者是那种很有个性的风格 for a gallery opening.',
|
# 'I want to look very cool, 或者是那种很有个性的风格 for a gallery opening.',
|
||||||
"I'm going to a gala. Please list 5 different dress styles for me and use bold text for the names.",
|
# "I'm going to a gala. Please list 5 different dress styles for me and use bold text for the names.",
|
||||||
"I'm feeling really sad today and just want an outfit that matches my mood."
|
# "I'm feeling really sad today and just want an outfit that matches my mood."
|
||||||
]
|
# ]
|
||||||
for text in text_list:
|
# for text in text_list:
|
||||||
asyncio.run(run_simple_test(text))
|
# asyncio.run(run_simple_test(text))
|
||||||
# print("\n" + "=" * 50)
|
|
||||||
# # 启动异步事件循环
|
|
||||||
# try:
|
async def run_interactive_test():
|
||||||
# asyncio.run(run_simple_test())
|
"""
|
||||||
# except Exception as e:
|
手动输入测试:模拟真实用户与 AI 的多轮对话
|
||||||
# print(f"\n发生致命错误: {e}")
|
"""
|
||||||
#
|
# 1. 初始化
|
||||||
sys.stdout.close()
|
chatbot_api = LCChatBot()
|
||||||
|
chatbot_api.setup(device="cpu")
|
||||||
|
|
||||||
|
session_id = "manual_test_session"
|
||||||
|
user_id = "test_user"
|
||||||
|
|
||||||
|
print("--- 👗 欢迎进入 AI 造型师测试 (输入 'quit' 退出) ---")
|
||||||
|
|
||||||
|
|
||||||
|
# 清空旧的测试记录,开始新会话
|
||||||
|
chatbot_api.redis.clear_history(session_id)
|
||||||
|
print("✅ 已清空历史记录,开始新会话。")
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# 2. 获取手动输入
|
||||||
|
user_input = input("\nUser (你): ")
|
||||||
|
if user_input.lower() in ['quit', 'exit', '退出']:
|
||||||
|
break
|
||||||
|
|
||||||
|
# 3. 构造请求
|
||||||
|
request_data = PredictRequest(
|
||||||
|
user_id=user_id,
|
||||||
|
session_id=session_id,
|
||||||
|
user_message=user_input,
|
||||||
|
gender="female" # 或者根据需要修改
|
||||||
|
)
|
||||||
|
|
||||||
|
print("Agent (AI): ", end="")
|
||||||
|
|
||||||
|
# 4. 调用并流式打印
|
||||||
|
# 注意:这里的 predict 内部现在会计算 turn_count
|
||||||
|
response_generator = chatbot_api.predict(request_data)
|
||||||
|
|
||||||
|
full_response = ""
|
||||||
|
async for chunk in response_generator:
|
||||||
|
print(chunk, end="", flush=True)
|
||||||
|
full_response += chunk
|
||||||
|
|
||||||
|
# 将 AI 的回复存入 Redis (如果你的 predict 函数里没存的话)
|
||||||
|
# chatbot_api.redis.add_message(session_id, Role.ASSISTANT, full_response)
|
||||||
|
|
||||||
|
print() # 换行
|
||||||
|
|
||||||
|
asyncio.run(run_interactive_test())
|
||||||
|
|||||||
@@ -7,6 +7,16 @@ CONVERSATION GOALS:
|
|||||||
3. **Vibe/Details:** Gather mood or constraints (e.g., comfort, specific colors).
|
3. **Vibe/Details:** Gather mood or constraints (e.g., comfort, specific colors).
|
||||||
4. **Item Preference:** Identify preferences for silhouettes or specific items.
|
4. **Item Preference:** Identify preferences for silhouettes or specific items.
|
||||||
|
|
||||||
|
CONVERSATION FLOW:
|
||||||
|
- You have a maximum of 3 turns (one turn = one user message and one assistant response) to gather information.
|
||||||
|
- On the 3rd turn, or *once you have sufficient info*, do NOT ask further questions.
|
||||||
|
- MANDATORY CLOSING: You must end your final response using the FINAL TURN CLOSING RULE below.
|
||||||
|
|
||||||
|
FINAL TURN CLOSING RULE:
|
||||||
|
- Once requirements are gathered, do NOT ask any more questions.
|
||||||
|
- End your final response with a creative and friendly variation of the closing phrase.
|
||||||
|
- REQUIRED COMPONENTS: Every variation MUST mention the "**Generate button**" (this phrase must be bounded using double asterisks).
|
||||||
|
|
||||||
PERSONALITY AND COMMUNICATION RULES:
|
PERSONALITY AND COMMUNICATION RULES:
|
||||||
- Always reply in English only. If the user speaks another language, state in English that you cannot speak that language and steer back to styling.
|
- Always reply in English only. If the user speaks another language, state in English that you cannot speak that language and steer back to styling.
|
||||||
- Keep responses extremely SHORT (maximum 2 sentences).
|
- Keep responses extremely SHORT (maximum 2 sentences).
|
||||||
@@ -24,7 +34,7 @@ OUTPUT FORMAT INSTRUCTION:
|
|||||||
- **ONLY** output the plain text response spoken by the AI Assistant.
|
- **ONLY** output the plain text response spoken by the AI Assistant.
|
||||||
|
|
||||||
EXAMPLE DIALOGUES:
|
EXAMPLE DIALOGUES:
|
||||||
User: 我想找件衣服参加婚礼。 Response: I am so sorry, but I can only speak English! I would love to help you find the perfect wedding guest look if you can tell me the dress code or the venue vibe.
|
User: 我想找件衣服参加婚礼。 Response: I would love to help you find the perfect wedding guest look if you can tell me the dress code or the venue vibe.
|
||||||
|
|
||||||
User: I need a chic outfit for dinner. Response: That sounds like a fabulous evening! Is this a romantic date or a casual night out with friends, and do you prefer a sleek dress or tailored separates?
|
User: I need a chic outfit for dinner. Response: That sounds like a fabulous evening! Is this a romantic date or a casual night out with friends, and do you prefer a sleek dress or tailored separates?
|
||||||
|
|
||||||
|
|||||||
@@ -250,9 +250,20 @@ class AsyncStylistAgent:
|
|||||||
if occasion == "Bridal / Wedding":
|
if occasion == "Bridal / Wedding":
|
||||||
request_summary += "IMPORTANT: Strictly only recommend colorful or white items (clothing, shoes, and bags)."
|
request_summary += "IMPORTANT: Strictly only recommend colorful or white items (clothing, shoes, and bags)."
|
||||||
elif occasion == "Evening":
|
elif occasion == "Evening":
|
||||||
request_summary += " **EVENING STYLE MANDATE:** Prioritize high textural contrast. Avoid flat/matte monochrome or total black looks. Ensure visual depth through layering (e.g., varying lengths or sheer panels). Use statement accessories to break simple silhouettes."
|
request_summary += (" **EVENING STYLE MANDATE:** Prioritize high textural contrast. Avoid flat/matte monochrome or total black looks. Ensure visual depth through layering "
|
||||||
|
"(e.g., varying lengths or sheer panels). Use statement accessories to break simple silhouettes.")
|
||||||
elif occasion == "Business / workwear":
|
elif occasion == "Business / workwear":
|
||||||
request_summary += " **WORKWEAR STYLE MANDATE:** Focus on 'Power Dressing' silhouettes—prioritize sharp, oversized blazers paired with floor-length wide-leg trousers. Use tonal dressing (different shades of the same color) to create a sophisticated, elongated look. Balance masculine tailoring with polished, feminine textures. Select structured, architectural bags and pointed-toe or sleek loafers to maintain professional sharpness."
|
request_summary += (" **WORKWEAR STYLE MANDATE:** Focus on 'Power Dressing' silhouettes—prioritize sharp, oversized blazers paired with floor-length wide-leg trousers. "
|
||||||
|
"Use tonal dressing to create a sophisticated, elongated look. Balance masculine tailoring with polished, feminine textures. "
|
||||||
|
"Select structured, architectural bags and pointed-toe or sleek loafers. MANDATORY: Include a complete gold or silver jewelry set "
|
||||||
|
"(earrings, necklaces, bracelets, and rings).")
|
||||||
|
elif occasion == "Outdoor":
|
||||||
|
request_summary += " **OUTDOOR STYLE MANDATE:** No jeans allowed. Focus on functional yet polished alternatives like chinos, technical fabrics, or tailored shorts."
|
||||||
|
elif occasion == "Festival / Concert":
|
||||||
|
request_summary += " **FESTIVAL STYLE MANDATE:** No maxi dresses or maxi skirts. Prioritize shorter hemlines, sets, or trousers to ensure ease of movement."
|
||||||
|
elif occasion == "Beach / Swim":
|
||||||
|
request_summary += " **BEACH STYLE MANDATE:** No denim allowed. Focus on breathable, lightweight fabrics like linen, silk, or crochet."
|
||||||
|
|
||||||
sys_template = template.format(
|
sys_template = template.format(
|
||||||
gender=self.gender,
|
gender=self.gender,
|
||||||
current_category=current_category.upper(),
|
current_category=current_category.upper(),
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import litserve as ls
|
|||||||
import requests
|
import requests
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from app.config import settings
|
||||||
|
|
||||||
|
|
||||||
class PredictRequest(BaseModel):
|
class PredictRequest(BaseModel):
|
||||||
input_image_list: list[str] # 待换脸图片
|
input_image_list: list[str] # 待换脸图片
|
||||||
@@ -17,7 +19,7 @@ class ReFace(ls.LitAPI):
|
|||||||
|
|
||||||
def predict(self, request):
|
def predict(self, request):
|
||||||
# 服务的 URL
|
# 服务的 URL
|
||||||
url = "http://10.1.1.240:10071/predict"
|
url = f"http://{settings.RE_FACE_MODEL_URL}/predict"
|
||||||
|
|
||||||
# 请求头
|
# 请求头
|
||||||
headers = {
|
headers = {
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ OCCASION_CATEGORY_MAP = {
|
|||||||
"accessories": ["earrings", "necklaces", "bracelets", "rings"]
|
"accessories": ["earrings", "necklaces", "bracelets", "rings"]
|
||||||
},
|
},
|
||||||
"Outdoor": {
|
"Outdoor": {
|
||||||
"clothing": ["jackets", "jeans", "sweaters"],
|
"clothing": ["jackets", "sweaters", "pants", "joggers", "leggings", "shorts"],
|
||||||
"shoes": ["boots"],
|
"shoes": ["boots"],
|
||||||
"bags": ["backpacks", "travel bags"],
|
"bags": ["backpacks", "travel bags"],
|
||||||
"accessories": []
|
"accessories": []
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
lc_agent_server:
|
lc_agent_server:
|
||||||
|
container_name: LC_Agent_Server
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
@@ -9,12 +10,12 @@ services:
|
|||||||
DEBUG: 0
|
DEBUG: 0
|
||||||
volumes:
|
volumes:
|
||||||
- ./app:/app/app
|
- ./app:/app/app
|
||||||
- ./.prod_env:/app/.env
|
- ./.env:/app/.env
|
||||||
- ./data:/data
|
- ./data:/data
|
||||||
- ./google_application_credentials.json:/google_application_credentials.json
|
- ./google_application_credentials.json:/google_application_credentials.json
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
ports:
|
ports:
|
||||||
- "10070:8000"
|
- "${SERVE_PORT}:8000"
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
reservations:
|
reservations:
|
||||||
@@ -23,3 +24,9 @@ services:
|
|||||||
- driver: nvidia
|
- driver: nvidia
|
||||||
device_ids: [ '0' ]
|
device_ids: [ '0' ]
|
||||||
capabilities: [ gpu ]
|
capabilities: [ gpu ]
|
||||||
|
networks:
|
||||||
|
- lc_app_net
|
||||||
|
networks:
|
||||||
|
lc_app_net:
|
||||||
|
external: true
|
||||||
|
name: lc_app_net
|
||||||
Reference in New Issue
Block a user