Compare commits

...

11 Commits

Author SHA1 Message Date
zcr
a36235d58e 1 2026-04-14 10:11:29 +08:00
zcr
433aa3e751 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	docker-compose.yml
2026-04-14 10:10:56 +08:00
zcr
7bf080b3e7 1 2026-04-14 10:10:00 +08:00
zcr
03c4759895 fix:更新occasion映射服装类别 2026-03-05 15:41:39 +08:00
03ff6605a3 更新 docker-compose.yml 2026-02-09 14:54:17 +08:00
4b3b0f6aa8 更新 .gitea/workflows/prod_build_manual.yaml 2026-02-09 14:50:25 +08:00
zcr
c798d37fdd fix:防止服务宕机,增加自动重启 2026-02-03 15:34:10 +08:00
zcr
43fd576da6 fix:启动端口改由环境变量控制 2026-01-27 10:40:50 +08:00
zcr
1bbb9c945e fix:启动端口改由环境变量控制 2026-01-27 10:33:35 +08:00
pangkaicheng
3ca6b16eaf UPDATE: update prompt. Make generate button bold 2026-01-19 13:18:21 +08:00
pangkaicheng
f9d83f6a99 UPDATE: chat basic prompt. Limit Chatbot to conclude within three turns. 2026-01-19 12:16:47 +08:00
9 changed files with 121 additions and 35 deletions

View File

@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
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:
- name: 1.检出代码

View File

@@ -17,7 +17,11 @@ class Settings(BaseSettings):
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表示生产环境运行")

View File

@@ -27,4 +27,4 @@ if __name__ == "__main__":
agent_api = LCAgent(enable_async=True, api_path='/api/v1/agent')
reface_api = ReFace(api_path='/api/v1/reface')
server = ls.LitServer([chat_boot_api, agent_api, reface_api])
server.run(port=settings.SERVE_PROD)
server.run(port=8000)

View File

@@ -49,10 +49,18 @@ class LCChatBot(ls.LitAPI):
user_msg = Message(role=Role.USER, content=user_message)
chat_history = self.redis.get_history(session_id)
chat_history.append(user_msg)
turn_count = sum(1 for msg in chat_history if msg.role == Role.USER)
if request.gender == 'male':
prompt = BASIC_PROMPT.format(gender='men')
system_instruction = BASIC_PROMPT.format(gender='men')
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 = []
@@ -68,7 +76,7 @@ class LCChatBot(ls.LitAPI):
model='gemini-2.5-flash',
contents=contents,
config=types.GenerateContentConfig(
system_instruction=prompt,
system_instruction=system_instruction,
# temperature=0.3,
)
)
@@ -99,7 +107,7 @@ class LCChatBot(ls.LitAPI):
if __name__ == "__main__":
sys.stdout = open('permanent.log', 'w', encoding='utf-8')
# sys.stdout = open('permanent.log', 'w', encoding='utf-8')
import asyncio
@@ -137,23 +145,67 @@ if __name__ == "__main__":
print(chunk, end="", flush=True)
text_list = [
'我要去参加好朋友的婚礼,你能帮我挑一套衣服吗?',
'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?',
'Im 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.',
'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 feeling really sad today and just want an outfit that matches my mood."
]
for text in text_list:
asyncio.run(run_simple_test(text))
# print("\n" + "=" * 50)
# # 启动异步事件循环
# try:
# asyncio.run(run_simple_test())
# except Exception as e:
# print(f"\n发生致命错误: {e}")
#
sys.stdout.close()
# text_list = [
# '我要去参加好朋友的婚礼,你能帮我挑一套衣服吗?',
# '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?',
# 'Im 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.',
# '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 feeling really sad today and just want an outfit that matches my mood."
# ]
# for text in text_list:
# asyncio.run(run_simple_test(text))
async def run_interactive_test():
"""
手动输入测试:模拟真实用户与 AI 的多轮对话
"""
# 1. 初始化
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())

View File

@@ -7,6 +7,16 @@ CONVERSATION GOALS:
3. **Vibe/Details:** Gather mood or constraints (e.g., comfort, specific colors).
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:
- 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).
@@ -24,7 +34,7 @@ OUTPUT FORMAT INSTRUCTION:
- **ONLY** output the plain text response spoken by the AI Assistant.
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?

View File

@@ -250,9 +250,20 @@ class AsyncStylistAgent:
if occasion == "Bridal / Wedding":
request_summary += "IMPORTANT: Strictly only recommend colorful or white items (clothing, shoes, and bags)."
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":
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(
gender=self.gender,
current_category=current_category.upper(),

View File

@@ -4,6 +4,8 @@ import litserve as ls
import requests
from pydantic import BaseModel
from app.config import settings
class PredictRequest(BaseModel):
input_image_list: list[str] # 待换脸图片
@@ -17,7 +19,7 @@ class ReFace(ls.LitAPI):
def predict(self, request):
# 服务的 URL
url = "http://10.1.1.240:10071/predict"
url = f"http://{settings.RE_FACE_MODEL_URL}/predict"
# 请求头
headers = {

View File

@@ -163,7 +163,7 @@ OCCASION_CATEGORY_MAP = {
"accessories": ["earrings", "necklaces", "bracelets", "rings"]
},
"Outdoor": {
"clothing": ["jackets", "jeans", "sweaters"],
"clothing": ["jackets", "sweaters", "pants", "joggers", "leggings", "shorts"],
"shoes": ["boots"],
"bags": ["backpacks", "travel bags"],
"accessories": []

View File

@@ -1,5 +1,6 @@
services:
lc_agent_server:
container_name: LC_Agent_Server
build:
context: .
dockerfile: Dockerfile
@@ -9,12 +10,12 @@ services:
DEBUG: 0
volumes:
- ./app:/app/app
- ./.prod_env:/app/.env
- ./.env:/app/.env
- ./data:/data
- ./google_application_credentials.json:/google_application_credentials.json
- /etc/localtime:/etc/localtime:ro
ports:
- "10070:8000"
- "${SERVE_PORT}:8000"
deploy:
resources:
reservations:
@@ -22,4 +23,10 @@ services:
# 告诉 Docker 使用所有可用的 NVIDIA GPU
- driver: nvidia
device_ids: [ '0' ]
capabilities: [ gpu ]
capabilities: [ gpu ]
networks:
- lc_app_net
networks:
lc_app_net:
external: true
name: lc_app_net