Kaynağa Gözat

chore: initial upload to remote

mia 3 ay önce
ebeveyn
işleme
f12b5d413b

BIN
7a5d202cbda1a9ef9d50c079402257e5.jpg


BIN
__pycache__/chat.cpython-310.pyc


BIN
__pycache__/conf.cpython-310.pyc


BIN
__pycache__/llm.cpython-310.pyc


BIN
__pycache__/prompt.cpython-310.pyc


+ 0 - 0
app.py


+ 50 - 23
app_v2.py

@@ -1,15 +1,35 @@
 from flask import Flask, request, jsonify
-from flask_cors import CORS  # 跨域支持
+# from flask_cors import CORS  # 跨域支持
 from typing import Dict, Any, List
-import logging
 import traceback
-from chat import gen_title, generate_text
-app = Flask(__name__)
-CORS(app)  # 允许所有域访问(生产环境应限制)
+import logging
+
+# 首先导入日志配置模块,确保日志系统在导入其他模块之前就配置好
+import logger_config
 
+# 确保日志系统已正确配置(因为可能多次导入,特别是gunicorn多worker情况下)
+logger_config.setup_logging()
 
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger("TextGenerationAPI")
+# 获取应用日志器
+logger = logger_config.get_logger("TextGenerationAPI")
+
+# 现在导入chat模块(它会导入conf等模块,此时日志已经配置好了)
+from chat import gen_title, generate_text, check_image_url
+
+# 验证chat和llm模块的logger是否正确配置(在导入后验证)
+chat_logger = logging.getLogger("chat")
+llm_logger = logging.getLogger("llm")
+
+# 确保chat和llm的logger有handler(通过propagate到root logger)
+chat_logger.propagate = True
+llm_logger.propagate = True
+
+logger.info("Flask应用启动,日志系统已初始化")
+logger.info(f"chat模块logger级别: {chat_logger.getEffectiveLevel()}, llm模块logger级别: {llm_logger.getEffectiveLevel()}")
+logger.info(f"根logger级别: {logging.root.getEffectiveLevel()}, handlers数量: {len(logging.root.handlers)}")
+
+app = Flask(__name__)
+# CORS(app)  # 允许所有域访问(生产环境应限制)
 
 
 
@@ -27,24 +47,30 @@ def request_description():
     try:
         data = request.get_json()
         if not data:
-            return jsonify({"error": "请求体必须为JSON格式"}), 400
+            return jsonify({"error": "The request body must be in JSON format."}), 400
             
         logger.info(f"收到请求数据:{data}")
-        required_fields = ["spu","reference_url",'plm_info', 'img', 'graphic_label']
+        required_fields = ["spu",'plm_info', 'img', 'graphic_label']
+        if 'plm_info' not in data or len(data["plm_info"])<1:
+            return jsonify({"error": f"Contact the designer to maintain product selling points data(plm_info)"}), 400
         missing = [field for field in required_fields if field not in data]
         if missing:
-            return jsonify({"error": f"缺少必要字段: {missing}"}), 400
+            return jsonify({"error": f"Necessary values are missing: {missing}"}), 400
+        is_valid, message=check_image_url(data['img'])
+        if not is_valid:
+            return jsonify({"error": message}), 400
 
         if not isinstance(data['graphic_label'], list) or \
            not all(isinstance(item, str) for item in data['graphic_label']):
-            return jsonify({"error": "graphic_label必须是字符串列表"}), 400
+            return jsonify({"error": "The graphic_label must be a list of strings."}), 400
 
         en,kw = generate_text(
             plm_info=data['plm_info'],
             img=data['img'],
             graphic_label=data['graphic_label']
         )
-
+        logger.info(f"生成描述:{en}")
+        logger.info(f"生成关键词:{kw}")
         return jsonify({
             "spu": data['spu'],  # 示例请求ID(实际应生成唯一标识)
             "result":{"descr":en,
@@ -54,7 +80,7 @@ def request_description():
     except Exception as e:
         logger.error(f"处理失败: {str(e)}\n{traceback.format_exc()}")
         return jsonify({
-            "error": "服务器内部错误",
+            "error": "Internal server error.",
             "detail": str(e)
         }), 500
     
@@ -72,29 +98,30 @@ def request_title():
     }
     """
     try:
-        data = request.get_json()
+        data = request.get_json(force=True)
         if not data:
-            return jsonify({"error": "请求体必须为JSON格式"}), 400
+            return jsonify({"error": "The request body must be in JSON format."}), 400
             
         logger.info(f"收到请求数据:{data}")
 
-        required_fields = ["spu","reference_title",'desc', 'tags']
+        required_fields = ["spu",'desc']
         missing = [field for field in required_fields if field not in data]
         if missing:
-            return jsonify({"error": f"缺少必要字段: {missing}"}), 400
+            return jsonify({"error": f"Necessary values are missing: {missing}"}), 400
         logger.info(f"11111111111数据完整:{data}")
         
         if not isinstance(data['tags'], list) or \
            not all(isinstance(item, str) for item in data['tags']) :
-            return jsonify({"error": "tags必须是字符串列表"}), 400
-        
+            return jsonify({"error": "The tags must be a list of strings."}), 400
+        reference_title = data.get("reference_title", "")
+        tags = data.get("tags", "")
         logger.info(f"数据完整:{data}")
         result = gen_title(
             info=data['desc'],
-            tags=data['tags'],
-            referencr_title=data["reference_title"]
+            tags=tags,
+            referencr_title=reference_title
         )
-        
+        logger.info(f"生成标题:{result}")
         # 5. 构造标准化响应 
         return jsonify({
             "spu": data['spu'],  # 示例请求ID(实际应生成唯一标识)
@@ -110,4 +137,4 @@ def request_title():
         }), 500
 if __name__ == '__main__':
     # 启动服务(生产环境应使用WSGI服务器) 
-    app.run(host='0.0.0.0', port=6868, debug=False)
+    app.run(host='0.0.0.0', port=5555, debug=False)

+ 400 - 218
chat.py

@@ -1,141 +1,142 @@
 import sys
-# print(sys.path)
-# sys.path.append("/data/data/luosy/project/sku_search")
 import time
 import os
+import logging
+
+# 首先导入日志配置模块,确保日志系统在导入其他模块之前就配置好
+# 这样当导入 llm、conf 等模块时,它们的日志也能正常工作
+import logger_config
+
+# 使用标准logging,与app_v2.py共用日志配置
+# 使用__name__会得到"chat",确保日志能正确输出
+logger = logging.getLogger(__name__)
+
+# 确保logger正确配置 - 强制添加handler,确保日志能输出
+logger.setLevel(logging.INFO)
+
+# 为chat logger强制添加handler(直接输出,不依赖传播)
+# 这样即使gunicorn重置了根logger,chat的logger仍能输出
+# 清除已有handler,重新添加(应对gunicorn worker重启的情况)
+for h in logger.handlers[:]:
+    logger.removeHandler(h)
+
+handler = logging.StreamHandler(sys.stderr)
+formatter = logging.Formatter(
+    '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+    datefmt='%Y-%m-%d %H:%M:%S'
+)
+handler.setFormatter(formatter)
+handler.setLevel(logging.INFO)
+logger.addHandler(handler)
+
+logger.propagate = False  # 不传播,直接使用自己的handler
+
+# 现在导入其他模块(它们会在导入时使用已配置好的日志系统)
 from PIL import Image
-import uuid
+import requests
 from  prompt import *
 from llm import *
 import json
 from conf import *
 import re
 
+MAX_RETRIES = 5
+MAX_HISTORY = 20
+MAX_CHAR_LIMIT = 400
+MIN_CHAR_LIMIT = 100 # 假设的最小长度限制
 
 history_list=[]
 
 plugins = {
-    "ch_en_selling_points":get_ch_en_selling_points,
-    "en_ch_selling_points":get_en_ch_selling_points,
+#     "ch_en_selling_points":get_ch_en_selling_points,
+#     "en_ch_selling_points":get_en_ch_selling_points,
     "ch_en_selling_title":get_ch_en_selling_title,
-    "en_ch_selling_points_his":get_en_ch_selling_points_his,
-    "TextControl_his":TextControl_his
+#     "en_ch_selling_points_his":get_en_ch_selling_points_his,
+#     "TextControl_his":TextControl_his,
+#     "TextControl":TextControl
 }
 
-def search_json_files(filename):
-    directory = "/data/data/luosy/project/sku_search/database/meta"
-    data = {}
-    # im=Image.new('RGB', (800, 800), 'white')
-    for file in os.listdir(directory):
-        if file.endswith('.json') and filename in file:
-            with open(os.path.join(directory, file), 'r') as f:
-                data = json.load(f)
-    if data =={}:
-        return None,"暂无数据","暂无数据","暂无数据","暂无数据","暂无数据"
-    return data["商品图像"], data["价格"], data["色号"], data["成分"], data["关键词"], data["商品细节"]
 
 
-def is_valid_response(response):
-    """
-    检查响应是否是有效的JSON格式且包含所需字段
-    """
-    try:
-        response_dict = json.loads(response)
-        required_fields = ["服装描述(中文)", "服装描述(英文)", "3个关键点(英文)"]
-        return all(field in response_dict for field in required_fields)
-    except Exception as e:
-        return False
 
-def extract_response_values(response):
-    """
-    从响应中提取各个字段的值
+
+def contains_chinese(text):
+    pattern = re.compile(r'[\u4e00-\u9fa5]')
+    return bool(pattern.search(text))
+
+def check_image_url(url):
+    """检查图片URL是否有效
+    
     Args:
-        response: JSON格式的响应字符串
+        url: 图片URL字符串
+        
     Returns:
-        tuple: (中文描述, 英文描述, 关键点)
+        tuple: (is_valid, message) 
+            is_valid: bool,表示URL是否有效
+            message: str,错误信息(如果无效)或空字符串(如果有效)
     """
-    try:
-        response_dict = json.loads(response)
-        cn_desc = response_dict.get("服装描述(中文)", "")
-        en_desc = response_dict.get("服装描述(英文)", "")
-        key_points = response_dict.get("3个关键点(英文)", "")
-        return cn_desc, en_desc, key_points
-    except Exception as e:
-        return "", "", ""
-
-# def doubao_request(pre_signed_url_output, info,method, max_retries=1):
-#     """
-#     带重试机制的豆包请求
-#     Args:
-#         pre_signed_url_output: 预签名URL
-#         info: 商品信息
-#         max_retries: 最大重试次数
-#     Returns:
-#         tuple: (中文描述, 英文描述, 关键点)
-#     """
-#     info = plugins[method](info)
+    logger.info(f"开始检查图片URL: {url}")
     
-#     for attempt in range(max_retries):
-#         try:
-#             result = doubao_MMM_request( pre_signed_url_output, info)
-#             if is_valid_response(result):
-#                 ch,en,key=extract_response_values(result)
-#                 if len(en)>350 and method=="ch_en_selling_points":
-#                     sysp,usrp=TextControl(en)
-#                     en=get_lm_text(sysp,usrp)
-#                 return ch,en,key 
-#         except Exception as e:
-#             print(f"Attempt {attempt + 1} failed with error: {str(e)}")
-#             if attempt == max_retries - 1:
-#                 raise e
-#     raise Exception("Failed to get valid response after maximum retries")
-
-# def doubao_generate(image,info,method):
-#     if not os.path.exists(MMMConfig.root_path):
-#         os.makedirs(MMMConfig.root_path)
-#     file_name=f"{uuid.uuid4()}.jpg"
-#     original_file=os.path.join(MMMConfig.root_path,file_name)
+    if not url or not isinstance(url, str):
+        logger.warning(f"图片URL格式无效: {url}")
+        return False, "Image URL is required and must be a string."
     
-#     # 添加这一行,将RGBA转换为RGB
-#     if image.mode == 'RGBA':
-#         image = image.convert('RGB')
-        
-#     image.save(original_file)
-#     compressed_file = original_file
-#     object_key = file_name
-#     quality = compress_image(original_file, compressed_file)
-  
-#     pre_signed_url_output = upload_tos(compressed_file,object_key)
-#     if pre_signed_url_output and os.path.exists(compressed_file):
-#         os.remove(compressed_file)
-#     return doubao_request(pre_signed_url_output,info,method)
-
-# def generate_text(id,image=None,info="",method="ch_en_selling_points"):
-#     if len(id) == 9:
-#         id_image,id_price, id_color, id_ingredient, id_selling_point, id_details=search_json_files(id)
-
-#     else:
-#         id_image,id_price, id_color, id_ingredient, id_selling_point, id_details=None,None,None,None,None,None
-
-#     info=id_details+info if id_details else info
-#     if image == None and id_image != None:
-#         image=Image.open(id_image)
-
-#     ch_sen,en_sen,key_point=doubao_generate(image,info,method)
-#     return ch_sen,en_sen,key_point,id_image,id_price, id_color, id_ingredient, id_selling_point, id_details
-
-# def gen_title(id,info=None,method="ch_en_selling_title"):
-#     info_new=None
-#     if len(id) == 9:
-#         info_new=generate_text(id)[1]
-#     if info_new ==None and info !=None:
-#         info_new=info
+    # 检查是否是有效的URL格式
+    if not url.startswith(('http://', 'https://')):
+        logger.warning(f"图片URL必须以http://或https://开头: {url}")
+        return False, "Image URL must start with http:// or https://"
     
-#     sysp,usrp = plugins[method](info_new)
-#     res=get_lm_text(sysp,usrp)
-#     res_dict = json.loads(res)
-#     # print(res)
-#     return res_dict["en_tile"],res_dict["ch_title"]
+    try:
+        # 使用HEAD请求检查URL是否可访问,设置超时避免长时间等待
+        logger.debug(f"发送HEAD请求检查URL: {url}")
+        response = requests.head(url, timeout=10, allow_redirects=True)
+        logger.debug(f"HEAD请求响应状态码: {response.status_code}")
+        
+        # 检查状态码
+        if response.status_code == 200:
+            # 检查Content-Type是否是图片类型
+            content_type = response.headers.get('Content-Type', '').lower()
+            logger.debug(f"Content-Type: {content_type}")
+            if 'image' in content_type:
+                logger.info(f"图片URL验证成功: {url}")
+                return True, ""
+            else:
+                logger.warning(f"URL不是图片类型,Content-Type: {content_type}")
+                return False, f"URL does not point to an image. Content-Type: {content_type}"
+        elif response.status_code == 405:
+            # 如果HEAD不支持,尝试GET方法(但只获取头信息)
+            logger.info(f"HEAD方法不支持,尝试GET方法: {url}")
+            try:
+                response = requests.get(url, timeout=10, stream=True)
+                response.raise_for_status()
+                content_type = response.headers.get('Content-Type', '').lower()
+                logger.debug(f"GET请求Content-Type: {content_type}")
+                if 'image' in content_type:
+                    logger.info(f"图片URL验证成功(通过GET方法): {url}")
+                    return True, ""
+                else:
+                    logger.warning(f"URL不是图片类型,Content-Type: {content_type}")
+                    return False, f"URL does not point to an image. Content-Type: {content_type}"
+            except requests.exceptions.RequestException as e:
+                logger.error(f"GET请求失败: {str(e)}")
+                return False, f"Cannot access image URL: {str(e)}"
+        else:
+            logger.warning(f"无法访问图片URL,状态码: {response.status_code}")
+            return False, f"Cannot access image URL. Status code: {response.status_code}"
+            
+    except requests.exceptions.Timeout:
+        logger.error(f"检查图片URL超时: {url}")
+        return False, "Timeout while checking image URL. Please check if the URL is accessible."
+    except requests.exceptions.ConnectionError:
+        logger.error(f"无法连接到图片URL: {url}")
+        return False, "Cannot connect to the image URL. Please check your network connection."
+    except requests.exceptions.RequestException as e:
+        logger.error(f"检查图片URL时发生请求异常: {str(e)}")
+        return False, f"Error checking image URL: {str(e)}"
+    except Exception as e:
+        logger.error(f"检查图片URL时发生未知错误: {str(e)}")
+        return False, f"Unexpected error while checking image URL: {str(e)}"
+
 def format_history(strings, indent="    "):
     result = ""
     for i, string in enumerate(strings, start=1):
@@ -144,165 +145,346 @@ def format_history(strings, indent="    "):
     return result
 
 def get_history():
+    """获取格式化的历史记录(用于原始prompt)"""
     global history_list
     if len(history_list)==0:
         history=''
     else:
         history=format_history(history_list)
     return history
+
+
 def add_history(input,max_num=20):
     global history_list
     text = re.split(r'[,\.\!\?\;\:]+', input)
     text=text[0].strip()
+    logger.debug(f"添加历史记录: {text[:50]}..." if len(text) > 50 else f"添加历史记录: {text}")
     history_list.insert(0, text)
     if len(history_list)>max_num:
         history_list=history_list[:max_num]
 
 
-def generate_text(plm_info,img,graphic_label=None,method="en_ch_selling_points_his",plat="ali",model_name="mm_tyqw"):
-    # his="""1.Embrace the spirit of freedom with this Hanma denim\n
-    #         2.Meet Aria, your new wardrobe staple that exudes elegance and comfort\n
-    #         3.Embrace the elegance of this 16 momme mulberry silk wide-leg pant\n
-    #         4.Meet your new best friend, the ultra-lightweight and versatile 'Transformer' down jacket\n
-    #         5.Embrace the allure of this enchanting blouse, where every detail whispers sophistication\n
-    #         6.Meet Gloria\n
-    #         7.Discover Gloria
-    #         8.Step into the spotlight with Gloria"""
-    his=get_history()
-    print(his)
+def generate_text(plm_info,img,graphic_label=None,plat="ali",model_name="mm_qwen"):
+    logger.info(f"开始生成文本,platform={plat}, model={model_name}")
+    logger.debug(f"plm_info长度: {len(plm_info) if plm_info else 0}, graphic_label: {graphic_label}")
+    
+    history_string=get_history()
+    logger.debug(f"历史记录数量: {len(history_list)}")
+    
     if graphic_label:
         tags_sen=",".join(graphic_label)
-        plm_info="\n'      '以下是该衣服的关键点:"+tags_sen
+        plm_info+="\n'      '以下是该衣服的标签信息:"+tags_sen
+        logger.debug(f"添加标签信息: {tags_sen}")
+        
     if plat=="ali":
         key=ali_ky
         model=ali_model[model_name]
+        logger.debug(f"使用阿里云平台,模型: {model}")
     else:
         key=doubao_ky
         model=doubao_model[model_name]
-    usrp = plugins[method](plm_info,his)
-    # print(usrp)
+        logger.debug(f"使用豆包平台,模型: {model}")
+
     llm=llm_request(*key,model)
+    en,kw='',['']
+    result_json = None
+    for attempt in range(MAX_RETRIES):
+        # --- 构造Prompt ---
+        if attempt == 0:
+            # 第一次尝试:使用您的主Prompt
+            usrp = user_prompt.format(basic_info_string=plm_info,history_string=history_string)
+            logger.info(f"第 {attempt + 1} 次尝试:使用原始Prompt")
+        else:
+            # 后续尝试:使用"修正Prompt"
+            usrp = get_refinement_prompt(plm_info, history_string, result_json)
+            logger.info(f"第 {attempt + 1} 次尝试:使用修正Prompt,失败原因: {result_json.get('error', 'UNKNOWN')}")
+        
+        logger.debug(f"Prompt长度: {len(usrp)}")
 
-    for _ in range(3):
         try:
-            result = llm.llm_mm_request(usrp,img)
-            print(result)
-            if is_valid_response(result):
-                ch,en,kw=extract_response_values(result)
+            response_text = llm.llm_mm_request(usrp,img,sys_text=system_prompt)
+            logger.debug(f"API响应长度: {len(response_text) if response_text else 0}")
+        except Exception as e:
+            logger.error(f"API调用失败(第{attempt + 1}次尝试): {str(e)}")
+            result_json = {"error": "API_FAILURE", "raw_response": str(e)}
+            continue
+            
+        try:
+            is_valid, validation_error, result_json = validate_response(response_text)
+            
+            if is_valid:
+                # 成功!
+                en,kw=result_json['en'],result_json['kw']
+                logger.info(f"文本生成成功(第{attempt + 1}次尝试),描述长度: {len(en)}, 关键词数量: {len(kw) if isinstance(kw, list) else 1}")
+                logger.debug(f"生成的描述: {en[:100]}..." if len(en) > 100 else f"生成的描述: {en}")
                 add_history(en)
                 break
+            
+            else:
+                # 失败,记录错误,循环将继续
+                logger.warning(f"第 {attempt + 1} 次尝试验证失败: {validation_error}")
+                # result_json 已经包含了失败的文本和错误信息,将用于下一次修正
+                continue
+                
         except Exception as e:
-            print(f"Attempt failed with error: {str(e)}")
+            logger.error(f"验证响应时发生异常: {str(e)}")
+            result_json = {"error": "VALIDATION_ERROR", "raw_response": str(e)}
+            continue   
+ 
+    if result_json and result_json.get("error") == "EN_TOO_LONG":
+        # 如果是因为超长而失败,且 raw_response 有效
+        logger.info("检测到文本超长,尝试智能截断")
+        try:
+            failed_data = json.loads(result_json.get("raw_response", "{}"))
+            long_en_text = failed_data.get("en")
+            
+            if long_en_text and len(long_en_text) > MAX_CHAR_LIMIT+100:
+                logger.info(f"原始文本长度: {len(long_en_text)},开始截断到 {MAX_CHAR_LIMIT+100} 字符")
+                en = smart_truncate_by_sentence(long_en_text, max_chars=MAX_CHAR_LIMIT+100)
+                kw = failed_data.get("kw", '')
+                logger.info(f"截断后文本长度: {len(en)}")
+                add_history(en)
+        
+        except (json.JSONDecodeError, KeyError, TypeError) as e:
+            logger.error(f"截断文本时发生错误: {str(e)}")
+            pass
 
-    # result={"descr":en,"keywords":kw}
+    if isinstance(kw,str):
+        kw = [item.strip() for item in kw.split('.') if item.strip()]
+        logger.debug(f"关键词从字符串转换为列表,数量: {len(kw)}")
+    
+    if not en:
+        logger.warning("最终生成的描述为空")
+    
     return en,kw
 
 
-def generate_text_new(plm_info,img,graphic_label=None,method="TextControl_his",plat="ali",model_name1="mm_tyqw",model_name2="text_qwen"):
+def validate_response(response_text):
+    """验证模型的输出是否符合所有规则"""
+    logger.debug(f"开始验证响应,响应文本长度: {len(response_text) if response_text else 0}")
+    
+    try:
+        # 规则1: 是否是有效JSON?
+        data = json.loads(response_text.strip())
+        logger.debug("JSON解析成功")
+    except json.JSONDecodeError as e:
+        logger.warning(f"JSON解析失败: {str(e)}")
+        return False, "INVALID_JSON", {"error": "INVALID_JSON", "raw_response": response_text}
+
+    # 规则2: 键是否齐全?
+    required_keys = ["en", "ch", "kw"]
+    missing_keys = [k for k in required_keys if k not in data]
+    if missing_keys:
+        logger.warning(f"缺少必需的键: {missing_keys}")
+        return False, "MISSING_KEYS", {"error": "MISSING_KEYS", "raw_response": json.dumps(data)}
+        
+    en_text = data.get("en", "")
+    logger.debug(f"英文文本长度: {len(en_text)}")
+    
+    # 规则3: 长度是否超标? 
+    if len(en_text) > MAX_CHAR_LIMIT+100:
+        logger.warning(f"文本长度超标: {len(en_text)} > {MAX_CHAR_LIMIT+100}")
+        return False, "EN_TOO_LONG", {"error": "EN_TOO_LONG", "raw_response": json.dumps(data)}
+        
+    # 规则4: 长度是否太短?
+    if len(en_text) < MIN_CHAR_LIMIT:
+        logger.warning(f"文本长度太短: {len(en_text)} < {MIN_CHAR_LIMIT}")
+        return False, "EN_TOO_SHORT", {"error": "EN_TOO_SHORT", "raw_response": json.dumps(data)}
+    
+    # 规则5: 是否包含中文
+    if contains_chinese(en_text):
+        logger.warning(f"文本包含中文字符")
+        return False, "EN_CONTAINS_CHINESE", {"error": "EN_CONTAINS_CHINESE", "raw_response": json.dumps(data)}
+    
+    logger.debug("响应验证成功")
+    return True, "SUCCESS", data
 
-    his=get_history()
-    print(his)
-    if graphic_label:
-        plm_info+="\n以下是该衣服的关键点:"+graphic_label
-    if plat=="ali":
-        key=ali_ky
-        model1=ali_model[model_name1]
-        model2=ali_model[model_name2]
-    else:
-        key=doubao_ky
-        model=doubao_model[model_name1]
-    sim_prompt="如果图片里有文字的话,请结合图片里的衣服和文本信息进行描述一下衣服,还要具体到衣服的风格,细节,版型,设计等"
+def smart_truncate_by_sentence(text, max_chars=MAX_CHAR_LIMIT):
+    logger.info(f"开始智能截断文本,原始长度: {len(text)}, 最大字符数: {max_chars}")
     
-    # print(usrp)
-    llm_mm=llm_request(*key,model1)
-    llm_lm=llm_request(*key,model2)
-    res1 = llm_mm.llm_mm_request(sim_prompt,img)
-    sys_prompt,usr_prompt = plugins[method](res1,his)
-    print(usr_prompt)
-    for _ in range(3):
-        try:
-            # print(sys_prompt,usr_prompt)
-            result=llm_lm.llm_text_request(usr_prompt,sys_prompt)
-            print(result)
-            if is_valid_response(result):
-                ch,en,kw=extract_response_values(result)
-                add_history(en)
-                break
-        except Exception as e:
-            print(f"Attempt failed with error: {str(e)}")
+    if len(text) <= max_chars:
+        logger.debug("文本长度未超过限制,无需截断")
+        return text
+    
+    # 按句子分隔符分割文本,保留分隔符
+    sentence_pattern = re.compile(r'([^.!?]+[.!?])')
+    sentences = sentence_pattern.findall(text)
+    logger.debug(f"分割出 {len(sentences)} 个句子")
+    
+    # 如果没有找到完整句子,直接截断
+    if not sentences:
+        logger.warning("未找到完整句子,使用直接截断")
+        truncated = text[:max_chars-3].strip() + '...'
+        logger.info(f"直接截断后长度: {len(truncated)}")
+        return truncated
+    
+    # 遍历每个句子,累加长度
+    result_sentences = []
+    total_length = 0
+    
+    for i, sentence in enumerate(sentences):
+        sentence_length = len(sentence)
+        
+        # 如果加上当前句子后超过限制,则停止添加
+        if total_length + sentence_length > max_chars:
+            logger.debug(f"第 {i+1} 个句子(长度: {sentence_length})会导致超出限制,停止添加")
+            break
+        
+        # 累加句子
+        result_sentences.append(sentence)
+        total_length += sentence_length
+        logger.debug(f"添加第 {i+1} 个句子(长度: {sentence_length}),累计长度: {total_length}")
+    
+    # 如果至少有一个句子被添加
+    if result_sentences:
+        truncated_text = ''.join(result_sentences).strip()
+        # 确保以句子结尾符号结尾
+        if truncated_text and not truncated_text.endswith(('.', '!', '?')):
+            truncated_text += '.'
+        logger.info(f"截断完成,使用了 {len(result_sentences)} 个句子,最终长度: {len(truncated_text)}")
+        return truncated_text.strip()
+    else:
+        # 如果第一个句子就超过限制,直接截断到 max_chars
+        logger.warning("第一个句子就超过限制,使用直接截断")
+        truncated = text[:max_chars-3].strip() + '...'
+        logger.info(f"直接截断后长度: {len(truncated)}")
+        return truncated
+
+
+def get_refinement_prompt(basic_info_string, history_string, failed_result):
+    """
+    根据上一次的失败原因,生成一个“引导式修正”的Prompt
+    """
+    failure_reason = failed_result.get("error", "UNKNOWN")
+    raw_response = failed_result.get("raw_response", "")
+
+    feedback = ""
+    # 尝试提取上次失败的文案
+    last_text_en = ""
+    try:
+        if raw_response:
+            last_text_en = json.loads(raw_response).get("en", "")
+    except json.JSONDecodeError:
+        pass # 无法解析,last_text_en 保持空
+
+    if failure_reason == "INVALID_JSON":
+        feedback = f"你上次的输出不是一个有效的JSON。请【严格】按照JSON格式输出。你上次的错误输出是:\n{raw_response}"
+    elif failure_reason == "EN_TOO_LONG":
+        feedback = f"""
+        你上次生成的 "en" 描述【超过了{MAX_CHAR_LIMIT}个字符】!
+        【你生成的超长原文】:\n{last_text_en}
+        【修正任务】: 请【大幅精简】上述原文,保留核心卖点,使其长度【绝对】在{MIN_CHAR_LIMIT}-{MAX_CHAR_LIMIT}字符以内。
+        """
+    elif failure_reason == "EN_TOO_SHORT":
+        feedback = f"""
+        你上次生成的 "en" 描述太短了(小于{MIN_CHAR_LIMIT}字符)。
+        【你生成的原文】:\n{last_text_en}
+        【修正任务】: 请在原文案基础上,围绕核心卖点再丰富一些细节,使其达到{MIN_CHAR_LIMIT}-{MAX_CHAR_LIMIT}字符。
+        """
+    elif failure_reason == "MISSING_KEYS":
+        feedback = f"你上次输出的JSON缺少 'en', 'ch' 或 'kw' 键。请确保三者齐全。"
+    elif failure_reason == "TOO_SIMILAR":
+        feedback = "你上次生成的文案与历史记录太相似了。请换一个角度(比如从'材质'或'穿搭场景')重新构思,字数保持在要求的范围内。"
+    elif failure_reason == "EN_CONTAINS_CHINESE":
+        feedback = f"""
+        你上次生成的 "en" 描述中包含了中文汉字(例如:{last_text_en})。
+        【修正任务】: "en" 字段【必须是纯英文】,【绝对禁止】出现任何中文字符。请严格修正并重新输出。
+        """
+    else:
+        feedback = "你上次的生成失败了。请重新严格按照所有规则生成一次。"
+
+    # 修正Prompt模板
+    refinement_prompt = f"""## 角色
+你是一个文案修正专家。
 
-    # result={"descr":en,"keywords":kw}
-    return ch,en,kw
-def gen_title(info,tags=None,referencr_title=None,method="ch_en_selling_title",plat="doubao",model_name="text_ds"):
+## 原始任务
+根据以下信息和随消息传入的图片生成文案:{basic_info_string}
+
+## 上次失败的反馈 (你必须修正!)
+{feedback}
+
+## 核心规则 (必须再次遵守)
+1.  【必须】输出严格的JSON格式。
+2.  "en" 描述【必须严格在{MAX_CHAR_LIMIT}字符以内】。
+3.  【不要】使用历史开篇:\n{history_string}
+
+## 最终输出
+请直接输出修正后的、严格符合要求的JSON字典。
+"""
+    return refinement_prompt
+
+
+
+
+def gen_title(info,tags=None,referencr_title=None,method="ch_en_selling_title",plat="ali",model_name="text_dsv3"):
+    logger.info(f"开始生成标题,platform={plat}, model={model_name}, method={method}")
+    logger.debug(f"info长度: {len(info) if info else 0}, tags: {tags}, reference_title: {referencr_title}")
     
     if tags:
         tags_sen=",".join(tags)
         info="\n'      '以下是该衣服的关键点:"+tags_sen
+        logger.debug(f"添加标签信息: {tags_sen}")
+        
     if referencr_title:
         info="\n'      '请以这条标题样例的结构作为借鉴来写这条标题:"+referencr_title
-    sysp,usrp = plugins[method](info)
+        logger.debug(f"添加参考标题: {referencr_title}")
+        
+    try:
+        sysp,usrp = plugins[method](info)
+        logger.debug(f"Prompt生成成功,system prompt长度: {len(sysp) if sysp else 0}, user prompt长度: {len(usrp) if usrp else 0}")
+    except KeyError as e:
+        logger.error(f"未知的方法: {method}, 可用方法: {list(plugins.keys())}")
+        raise
+    except Exception as e:
+        logger.error(f"生成Prompt时发生错误: {str(e)}")
+        raise
+        
     if plat=="ali":
         key=ali_ky
         model=ali_model[model_name]
+        logger.debug(f"使用阿里云平台,模型: {model}")
     else:
         key=doubao_ky
         model=doubao_model[model_name]
-    
+        logger.debug(f"使用豆包平台,模型: {model}")
+
     llm=llm_request(*key,model)
-    res=llm.llm_text_request(usrp,sysp)
-    res_dict = json.loads(res)
-    return {"title":res_dict["en_tile"]}
-
-# @app.route('/title', methods=['POST'])
-# def request_title():
-#     try:
-#         spu = request.form.get("spu", None)  
-#         referencr_title = request.form.get("referencr_title", None) 
-#         desc = request.form.get("desc")
-#         if  not desc:
-#             return jsonify({"error": "desc is required"}), 400
-
-#         tags=request.form.get("tags", None) 
-#         result=gen_title(desc,tags,referencr_title)
-#         response = {
-#             "spu": spu,
-#             "result": result
-#         }
-#         return jsonify(response), 200
-#     except Exception as e:
-#         return jsonify({"error": str(e)}), 500
-
-# @app.route('/description', methods=['POST'])
-# def request_description():
-#     try:
-#         spu = request.form.get("spu", None)  
-#         reference_url = request.form.get("reference_url", None) 
-#         graphic_label = request.form.get("graphic_label", None)
-#         img = request.form.get("img")
-#         if  not img:
-#             return jsonify({"error": "img is required"}), 400
-#         plm_info = request.form.get("plm_info")
-#         if  not plm_info:
-#             return jsonify({"error": "plm_info is required"}), 400
-#         result=generate_text(plm_info,img,graphic_label)
-#         response = {
-#             "spu": spu,
-#             "result": result
-#         }
-#         return jsonify(response), 200
-#     except Exception as e:
-#         return jsonify({"error": str(e)}), 500
+    
+    try:
+        res=llm.llm_text_request(usrp,sysp)
+        logger.debug(f"API响应长度: {len(res) if res else 0}")
+    except Exception as e:
+        logger.error(f"API调用失败: {str(e)}")
+        raise
+        
+    try:
+        res_dict = json.loads(res)
+        logger.debug(f"JSON解析成功,keys: {list(res_dict.keys())}")
+    except json.JSONDecodeError as e:
+        logger.error(f"JSON解析失败: {str(e)}, 响应内容: {res[:200] if res else 'None'}...")
+        raise
+        
+    title = res_dict.get("en_tile")  # 注意:这里可能是拼写错误,但保持原样
+    if not title:
+        logger.warning(f"响应中未找到 'en_tile' 字段,可用字段: {list(res_dict.keys())}")
+    
+    logger.info(f"标题生成成功,标题长度: {len(title) if title else 0}")
+    return {"title": title}
 
 if __name__ == "__main__":
     
-
-
-    id_image,id_price, id_color, id_ingredient, id_selling_point, id_details=search_json_files("1A6H4K7V0")
-    id_image=id_image[2:]
-    id_image=os.path.join("/data/data/luosy/project/sku_search",id_image)
-    print(id_details,id_image)
+    # inf="'Meet your new best friend in fashion—this unisex sweater that whispers comfort and style. Crafted from premium cotton, it feels like a gentle hug on your skin. The heart embroidery adds a touch of whimsy, making you the star of any casual outing. Perfect for layering or wearing solo, this soft companion keeps you cozy all season long."
+    # print(gen_title(inf))
+    # id_image,id_price, id_color, id_ingredient, id_selling_point, id_details=search_json_files("1A6H4K7V0")
+    # id_image=id_image[2:]
+    # id_image=os.path.join("/data/data/luosy/project/sku_search",id_image)
+    id_image="https://img2.goelia.com.au/prod/product/1ENC6E220/material/main/Shopify/-1/72736752b0ad405382d5ed277dabc660.jpg"
+    graphic_label=['-100% Merino wool', '-With pockets', '-H-line fit']
+    plm_info='1、手工流苏边设计 \xa0 2、贴袋设计 \xa0 3、金属纽扣'
+    # print(id_details,id_image)
     for _ in range(3):
-        result=generate_text_new('1、16mm平纹香云纱;2、香蕉裤型;3、裤身立体收省;4、后腰包橡筋;5、两侧插袋;两边插袋里面有网布贴袋,一大一小,大的可套手机,小的可套耳机或卡片;6、真门牌,大溪地贝壳扣',"/data/data/Mia/product_env_project/gen_sellpoint/企业微信截图_17439887354277.png")
+        result=generate_text(plm_info,id_image,graphic_label)
+        # result=gen_title("This maxi dress features unparalleled comfort and a unique texture with its <b>tencel blend fabric</b>. The square neckline and smocked bodice create a flattering silhouette, while the layered skirt adds romantic flair. <b>Side pockets and an included scarf scrunchie</b> enhance both style and functionality, elevating its versatility for everyday wear and beyond.")
         print(result)
     # from tqdm import tqdm
     # def image_to_base64(image):

+ 0 - 0
chat_old1.py


+ 0 - 0
chat_v2.py


+ 4 - 3
conf.py

@@ -30,9 +30,10 @@ doubao_model={
     "mm_doubao":"doubao-1-5-vision-pro-32k-250115"
 }
 ali_model={
+    "text_qwen":"qwen-max-2025-01-25",
     "text_ds":"deepseek-r1",
     "text_dsv3":"deepseek-v3",
-    "text_qwen":"qwen-max-latest",
-    "mm_tyqw":"qwen-vl-max-latest",
-    "mm_qwen":"qwen-vl-plus"
+    "text_qwen":"qwen3-max",
+    "mm_tyqw":"qwen-vl-max",
+    "mm_qwen":"qwen3-vl-plus"
 }

+ 0 - 0
git


+ 81 - 18
llm.py

@@ -1,42 +1,104 @@
 import io
-from PIL import Image
 import os
+import time
+import base64
+import logging
+
+# 首先导入日志配置模块,确保日志系统在导入其他模块之前就配置好
+# 导入logger_config以自动配置日志系统(如果还没有配置)
+try:
+    import logger_config
+except ImportError:
+    pass
+
+# 使用标准logging
+# 使用__name__会得到"llm",确保日志能正确输出
+logger = logging.getLogger(__name__)
+
+# 确保logger正确配置 - 强制添加handler,确保日志能输出
+logger.setLevel(logging.INFO)
 
+# 为llm logger强制添加handler(直接输出,不依赖传播)
+# 这样即使gunicorn重置了根logger,llm的logger仍能输出
+# 清除已有handler,重新添加(应对gunicorn worker重启的情况)
+for h in logger.handlers[:]:
+    logger.removeHandler(h)
+
+import sys
+handler = logging.StreamHandler(sys.stderr)
+formatter = logging.Formatter(
+    '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+    datefmt='%Y-%m-%d %H:%M:%S'
+)
+handler.setFormatter(formatter)
+handler.setLevel(logging.INFO)
+logger.addHandler(handler)
+
+logger.propagate = False  # 不传播,直接使用自己的handler
+
+# 现在导入其他模块
+from PIL import Image
+import numpy as np
 from openai import OpenAI
 from  conf import *
 from tos import HttpMethodType
-import time
-from openai import OpenAI
-import os
-import base64
-from PIL import Image
+import requests
+from requests.adapters import HTTPAdapter
+from urllib3.util.retry import Retry
 
 def image_to_base64(image):
     # 将Image对象转换为BytesIO对象
     image_io = io.BytesIO()
-    image.save(image_io, format='PNG')
+    image.save(image_io, format='JPEG', quality=95)
     image_io.seek(0)
 
     # 使用base64编码
     image_base64 = base64.b64encode(image_io.read()).decode('utf-8')
 
-    return f"data:image/png;base64,{image_base64}"
+    return image_base64
+
+def download_image_with_retry(url, max_retries=3, timeout=30):
+    """下载图片并重试机制"""
+    session = requests.Session()
+    retry_strategy = Retry(
+        total=max_retries,
+        backoff_factor=1,
+        status_forcelist=[429, 500, 502, 503, 504],
+    )
+    adapter = HTTPAdapter(max_retries=retry_strategy)
+    session.mount("http://", adapter)
+    session.mount("https://", adapter)
+    
+    try:
+        logger.info(f"正在下载图片: {url}")
+        response = session.get(url, timeout=timeout)
+        response.raise_for_status()
+        logger.info("图片下载成功")
+        return Image.open(io.BytesIO(response.content))
+    except Exception as e:
+        logger.error(f"下载图片失败: {e}")
+        return None
 
 def image_reader(image):
     """图片读取器,输出PIL.Image格式的图片"""
     if isinstance(image,str):
         if image.startswith("http"):
-            return image
+            # 先尝试下载图片到本地处理
+            out_image = download_image_with_retry(image)
+            if out_image is None:
+                # 如果下载失败,抛出异常
+                raise Exception(f"无法下载图片: {image}")
         else:
             image_path = image
-        out_image = Image.open(image_path)
+            out_image = Image.open(image_path)
     elif isinstance(image,np.ndarray):
         out_image = Image.fromarray(image)
     else:
         out_image = image
     out_image=out_image.convert('RGB')
     base64_img=image_to_base64(out_image)
-    return base64_img
+    # 返回完整的data URI格式
+    return f"data:image/jpeg;base64,{base64_img}"
 def get_lm_text(sys_prompt,user_prompt):
     completion = LMConfig.lm_client.chat.completions.create(
         messages = [
@@ -79,18 +141,17 @@ def upload_tos(filename,  tos_object_key):
     except Exception as e:
         if isinstance(e, tos.exceptions.TosClientError):
             # 操作失败,捕获客户端异常,一般情况为非法请求参数或网络异常
-            print('fail with client error, message:{}, cause: {}'.format(e.message, e.cause))
+            logger.error('TOS客户端错误, message:{}, cause: {}'.format(e.message, e.cause))
         elif isinstance(e, tos.exceptions.TosServerError):
             # 操作失败,捕获服务端异常,可从返回信息中获取详细错误信息
-            print('fail with server error, code: {}'.format(e.code))
+            logger.error('TOS服务端错误, code: {}'.format(e.code))
             # request id 可定位具体问题,强烈建议日志中保存
-            print('error with request id: {}'.format(e.request_id))
-            print('error with message: {}'.format(e.message))
-            print('error with http code: {}'.format(e.status_code))
+            logger.error('error with request id: {}'.format(e.request_id))
+            logger.error('error with message: {}'.format(e.message))
+            logger.error('error with http code: {}'.format(e.status_code))
 
         else:
-
-            print('fail with unknown error: {}'.format(e))
+            logger.error('TOS上传失败,未知错误: {}'.format(e))
 
         raise e
 
@@ -154,6 +215,7 @@ class llm_request:
             top_p=0.85, 
             presence_penalty=1.5, 
             frequency_penalty=1.5,
+            timeout=120.0
         )
         return completion.choices[0].message.content
     
@@ -175,6 +237,7 @@ class llm_request:
                 }
             ],
             temperature=0.9,
+            timeout=120.0
         )
         return completion.choices[0].message.content
     

+ 69 - 0
logger_config.py

@@ -0,0 +1,69 @@
+"""
+日志配置模块
+统一配置所有模块的日志格式和级别
+"""
+import logging
+import sys
+
+
+def setup_logging():
+    """配置日志系统"""
+    # 获取根日志器
+    root_logger = logging.getLogger()
+    
+    # 创建统一的格式
+    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', 
+                                datefmt='%Y-%m-%d %H:%M:%S')
+    
+    # 如果已经有handlers,先清除(避免重复配置)
+    if root_logger.handlers:
+        root_logger.handlers.clear()
+    
+    # 创建StreamHandler,输出到stderr(gunicorn会捕获stderr)
+    console_handler = logging.StreamHandler(sys.stderr)
+    console_handler.setFormatter(formatter)
+    console_handler.setLevel(logging.INFO)
+    
+    # 配置根日志器
+    root_logger.setLevel(logging.INFO)
+    root_logger.addHandler(console_handler)
+    
+    # 配置第三方库的日志级别,确保能看到相关日志
+    # httpx 是很多HTTP客户端库的底层,需要设置
+    logging.getLogger("httpx").setLevel(logging.INFO)
+    # urllib3 也是常见的HTTP库
+    logging.getLogger("urllib3").setLevel(logging.INFO)
+    # requests库
+    logging.getLogger("requests").setLevel(logging.INFO)
+    # volcenginesdkarkruntime (豆包SDK)
+    logging.getLogger("volcenginesdkarkruntime").setLevel(logging.INFO)
+    logging.getLogger("volcengine").setLevel(logging.INFO)
+    # tos (火山引擎对象存储)
+    logging.getLogger("tos").setLevel(logging.INFO)
+    # OpenAI SDK
+    logging.getLogger("openai").setLevel(logging.INFO)
+    # conf模块相关的日志
+    logging.getLogger("conf").setLevel(logging.INFO)
+    
+    # 配置chat和llm模块的logger(使用模块名作为logger名称)
+    # 这些logger会继承根logger的handler,因为propagate默认是True
+    for logger_name in ["chat", "llm"]:
+        module_logger = logging.getLogger(logger_name)
+        module_logger.setLevel(logging.INFO)
+        module_logger.propagate = True  # 确保传播到根logger
+        # 不添加独立的handler,让它使用根logger的handler
+    
+    # 确保根日志器的级别设置为INFO,以便所有子logger都能输出
+    root_logger.setLevel(logging.INFO)
+    
+    return logging.getLogger("TextGenerationAPI")
+
+
+# 自动配置日志(当模块被导入时)
+_ = setup_logging()
+
+# 提供便捷的logger获取方式
+def get_logger(name="TextGenerationAPI"):
+    """获取指定名称的日志器"""
+    return logging.getLogger(name)
+

+ 0 - 0
module.py


+ 0 - 0
new.py


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 33
out.html


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 33
out——ali.html


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 33
out——ali_v2.html


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 33
out——ali_v3.html


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 33
out——doubao_v1.html


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 33
out——qw_v1.html


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 33
out——qw_v2.html


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 33
out——qw_v3.html


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 33
out——qw_v4.html


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 33
out——qw_v5.html


+ 0 - 31
out——qw_v6.html

@@ -1,31 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<title>Images in Table</title>
-<meta charset="UTF-8">
-<style>
-table {
-border-collapse: collapse;
-width: 100%;
-}
-table, th, td {
-border: 1px solid black;
-}
-img {
-max-width: 100%;
-height: auto;
-display: block;
-margin-left: auto;
-margin-right: auto;
-}
-</style>
-</head>
-<body>
-<table>
-<tr>
-<th>输入的图片</th>
-<th>输入的描述</th>
-<th>输出的商品详情</th>
-<th>输出的商品详情(翻译)</th>
-<th>输出的卖点</th>
-</tr>

+ 0 - 18
pdf_extract.py

@@ -1,18 +0,0 @@
-import fitz  
-
-def extract_text(file_path, page_number):
-    # 打开PDF文件
-    doc = fitz.open(file_path)
-    
-    # 检查页面号是否有效
-    if page_number < 1 or page_number > len(doc):
-        return "页面号超出文档范围"
-    
-    # 提取指定页面的文本(页面索引从0开始,因此减1)
-    page = doc[page_number - 1]
-    text = page.get_text()
-    
-    # 关闭文档
-    doc.close()
-    
-    return text

+ 0 - 0
process_meta.py


+ 168 - 112
prompt.py

@@ -1,120 +1,176 @@
 ## 中文变英文卖点文案
-def get_ch_en_selling_points(info):
-    ch_en_selling_points = f"""
-        ## 你现在是一位欧美独立站服装卖点文案的专家;我现在要在独立站上写衣服的卖点,请根据我提供给你的图片和文本写出对应的卖点文案。输出的文案包括3个部分,服装描述(中文)+服装描述(英文)+3个关键点(英文)。 
-        ## 服装描述(中文):以一句大胆的话语作为开头+服装内容的描述(主要是对衣服面料、细节点和特性的描述),其中字数要在100个字以内。
-            - 引人入胜的开篇:以一句大胆、新颖甚至颠覆常规认知的话语作为开头,瞬间抓住消费者的眼球,最好是形容词+品类的形式。并且句子里面一定要包含衣服的品类的词(比如连衣裙,两件套,外套,裤子,半身裙,衬衫等等)。开头不要用反问句!绝对不要包含词语:这件,这款。
-            - 服装内容的描述:内容包括衣服面料+细节点(包括口袋、弹力橡筋、头花等)+特性(包括适用季节、场合、搭配等)。如果面料属于桑蚕丝、二、三醋酸、香云纱、羊绒、羊毛、美丽诺羊毛、可水洗羊毛、可机洗羊毛、鹅绒、亚麻、棉、羊毛混纺、羊驼毛、马海毛、天丝,那请优先描述面料的材料(很重要)!不然请优先描述细节点和特性。描述时请描述产品的独特设计和材质,加入带有情感的修饰词。用强烈的色彩对比和生动的词语。强调功能性的同时融入时尚表达。强调设计细节的用心。描述产品如何提升穿着者的气场。
-            - 服装描述(中文)例子如下:
-                样例:充满生机与活力的外套,这款外套是兼具实用与美观的优雅通勤气质单品。四面弹斜纹布透气性好,细腻的光泽感和适度的垂坠性更显高级。金色的双排扣设计是其显著的特点之一,复古优雅。可以搭配同色系的裤子,展现出自己的个性与魅力。
-        ## 服装描述(英文)是服装描述(中文)的翻译,其中字数要在300个字符以内(包括标点符号),如果翻译后文本还是超过350个字符请简化一下内容。
-            - 服装描述(英文)例子如下:
-            - 样例:Green symbolizes vitality and energy, making this jacket the perfect blend of practicality and elegance for daily commutes. Crafted from breathable four-way stretch twill, its fine sheen and graceful drape exude sophistication. Gold double-breasted buttons that add a touch of vintage charm. Pair it with matching trousers to showcase your unique style and charisma.
-        ## 3个关键点(英文)是从服装描述中提取三个卖点关键词,每个卖点不超过4个单词,内容尽可能简洁。
-            - 3个关键点(英文)例子如下:
-            - 样例:-Beathable. -H-line fit. -Casual and work wear
-        ## 该衣服信息如下:{info}
-        ## 以下是输出样例的格式给你参考
-            - 样例:{{"服装描述(中文)": "作为日常通勤的完美选择,这款四面弹阔腿裤具有出色的弹性和透气性,即使在炎炎夏日也能保持凉爽舒适。弹力腰带、侧口袋和前褶等贴心细节既提升了舒适度,又增强了时尚感。搭配同款夹克,打造别致的造型。", "服装描述(英文)": "The perfect choice for daily commutes, these four-way stretch wide-leg pants offer exceptional elasticity and breathability, keeping you cool and comfortable even on hot summer days. Thoughtful details like an elastic waistband, side pockets, and front pleats enhance both comfort and style. Pair them with a matching jacket for a chic coordinated look.", "3个关键点(英文)": "-Elastic. -With elastic waistband. -With pockets"}}
-        ## 请严格按照标准的json格式输出结果给我,用英文的双引号而不是单引号!请直接回答结果给我,不要回答我除此之外的任何其他内容!输出不要带有其他格式(如换行加粗等)。
-    """
-    return ch_en_selling_points
-
-
-## 英文变中文卖点文案
-def get_en_ch_selling_points(info):
-    en_ch_selling_points = f"""
-        ## 你现在是一位欧美独立站服装卖点文案的专家;我现在要在独立站上写衣服的卖点,请根据我提供给你的图片和文本写出对应的卖点文案。输出的文案包括3个部分,服装描述(英文)+服装描述(中文)+3个关键点(英文)。 
-        ## 要求:请为衣服注入灵魂,衣服就像人一样,她/他的设计都是有初衷的,有自己的实用性和时尚性,请描述衣服的卖点的时候,像是在与消费者直接对话,营造出一种亲切、友好的氛围。且运用时尚且专业的词汇,通过细腻的描述来突出产品的特点和优势。巧妙地将产品与具体的生活场景相结合。整体表述简洁,没有过多冗长复杂的句子,信息传达直接高效,能够让读者快速抓住重点,了解产品的主要特点、优势和适用场景。
-        ## 内容如下:
-            ### 服装描述(英文):引人入胜的开篇+服装内容的描述(主要是对衣服面料、细节点和特性的描述),其中字数要在300个字符以内(包括标点符号)。
-                - 引人入胜的开篇:用新颖含有故事性的话语作为开头,瞬间抓住消费者的眼球。为衣服注入灵魂。衣服是有生命力的,不要用it表述衣服,而是拟人化。使用 “you” 直接与读者交流,亲切且对话感强,运用形象的比喻和富有情感的描述。不要老是以Imagine slipping into作为开头
-                - 服装内容的描述:内容包括衣服面料+细节点(包括口袋、弹力橡筋、头花等)+特性(包括适用季节、场合、搭配等)。如果面料属于桑蚕丝、二、三醋酸、香云纱、羊绒、羊毛、美丽诺羊毛、可水洗羊毛、可机洗羊毛、鹅绒、亚麻、棉、羊毛混纺、羊驼毛、马海毛、天丝,那请优先描述面料的材料(很重要)!不然请优先描述细节点和特性。描述时请描述产品的独特设计和材质,加入带有情感的修饰词。用强烈的色彩对比和生动的词语。强调功能性的同时融入时尚表达。强调设计细节的用心。描述产品如何提升穿着者的气场。
-                - 服装描述(英文)例子如下:
-                    样例1:"When you've got no time to waste, wrap up your look in seconds with this sleek vest that lets you face the day head-on. Made from crease-ease fabric, this low-maintenance charmer features a collared neckline and a detachable sash that keeps things looking sharp and always ready."
-                    样例2:"Like a familiar hug from Ahma, Farah is made of soft knit that gently wraps your curves and will continue to do so without losing shape. With a fixed sash for an added layer of style, she’s an essential that works under blazers or on her own."
-                    样例3:"Throw Riley on or layer her, and you’ll see why you need her in every colour. We one-upped ourselves and made her with a more refined finish, so you can look every bit the sharp shooter you already are. Comes with a functional front pocket for stashing your essentials."
-                    样例4:"For whatever the weekend brings, this skort's built-in shorts mean you can conquer the day without worrying about your bottoms riding up or sliding down. Plus, we designed her with a detachable belt that chinches your waist so good, you'll want to wear it forever. Comes with functional side pockets and closes with a back zip."
-                    样例5:"Consider Dahlia your fool-proof office look. A classic silhouette, but made better with a back zip, edgier angular lines, and a flattering square neck that’s appropriately feminine, yet modern. You’ll look like a 10/10 on that pitch, so go win it."
-            ### 服装描述(中文)是服装描述(英文)的翻译。
-            ### 3个关键点(英文)是从服装描述中提取三个卖点关键词,每个卖点不超过4个单词,内容尽可能简洁。
-                - 3个关键点(英文)例子如下:
-                - 样例:-Beathable. -H-line fit. -Casual and work wear
-        ## 该衣服信息如下:{info}
-        ## 以下是输出样例的格式给你参考
-            - 样例:{{"服装描述(英文)": "Some skirts are meant for you to dance in—Wei’s one of them. Featuring sunray pleats crafted from lightweight crepe, this midaxi skirt flows with ease while her fully elastic waistband ensures maximum comfort. And the best part? She’s machine washable, so you can wear her on repeat. No laundry or dry cleaning required", "服装描述(中文)":"有些裙子是专为让你尽情跳舞而设计的,薇这款裙子就是其中之一。这款中长款迷笛裙采用轻盈的绉纱面料打造出放射状褶裥,能轻松飘逸摆动,而全松紧腰头则确保了极致的舒适感。最棒的是什么呢?这条裙子可机洗,所以你可以反复穿着。无需手洗或干洗。","3个关键点(英文)": "-Bloat-friendly. -Bump-friendly. -Machine Washable"}}
-        ## 请严格按照标准的json格式输出结果给我,用英文的双引号而不是单引号!请直接回答结果给我,不要回答我除此之外的任何其他内容!输出不要带有其他格式(如换行加粗等)。
-    """
-    return en_ch_selling_points          
-
-def get_en_ch_selling_points_his(info,history):
-    en_ch_selling_points = f"""
-        ## 你现在是一位欧美独立站服装卖点文案的专家;我现在要在独立站上写衣服的卖点,请根据我提供给你的图片和文本写出对应的卖点文案。输出的文案包括3个部分,服装描述(英文)+服装描述(中文)+3个关键点(英文)。 
-        ## 要求:请为衣服注入灵魂,衣服就像人一样,她/他的设计都是有初衷的,有自己的实用性和时尚性,请描述衣服的卖点的时候,像是在与消费者直接对话,营造出一种亲切、友好的氛围。且运用时尚且专业的词汇,通过细腻的描述来突出产品的特点和优势。巧妙地将产品与具体的生活场景相结合。整体表述简洁,没有过多冗长复杂的句子,信息传达直接高效,能够让读者快速抓住重点,了解产品的主要特点、优势和适用场景。
-        ## 内容如下:
-            ### 服装描述(英文):引人入胜的开篇+服装内容的描述(主要是对衣服面料、细节点和特性的描述),其中字数要在300个字符以内(包括标点符号)。
-
-                - 引人入胜的开篇:用新颖含有故事性的话语作为开头,瞬间抓住消费者的眼球。为衣服注入灵魂。衣服是有生命力的,不要用it表述衣服,而是拟人化。使用 “you” 直接与读者交流,亲切且对话感强,运用形象的比喻和富有情感的描述。不要老是以Imagine slipping into或者Embrace或者Discovery作为开头
-                    注意!!以下是常用的开篇,请不要再重复使用或者出现相同的词汇(如果使用重复的词汇则判断为类似),请生成比下面更加新颖的开头:
-                        {history}
-                - 服装内容的描述:内容包括衣服面料+细节点(包括口袋、弹力橡筋、头花等)+特性(包括适用季节、场合、搭配等)。如果面料属于桑蚕丝、二、三醋酸、香云纱、羊绒、羊毛、美丽诺羊毛、可水洗羊毛、可机洗羊毛、鹅绒、亚麻、棉、羊毛混纺、羊驼毛、马海毛、天丝,那请优先描述面料的材料(很重要)!不然请优先描述细节点和特性。描述时请描述产品的独特设计和材质,加入带有情感的修饰词。用强烈的色彩对比和生动的词语。强调功能性的同时融入时尚表达。强调设计细节的用心。描述产品如何提升穿着者的气场。
-                - 服装描述(英文)例子如下,请重点参考下面的样例的结构和语言风格进行生成卖点文案,但是不能输出一样的句子:
-                    样例1:"When you've got no time to waste, wrap up your look in seconds with this sleek vest that lets you face the day head-on. Made from crease-ease fabric, this low-maintenance charmer features a collared neckline and a detachable sash that keeps things looking sharp and always ready."
-                    样例2:"Like a familiar hug from Ahma, Farah is made of soft knit that gently wraps your curves and will continue to do so without losing shape. With a fixed sash for an added layer of style, she’s an essential that works under blazers or on her own."
-                    样例3:"Throw Riley on or layer her, and youll see why you need her in every colour. We one-upped ourselves and made her with a more refined finish, so you can look every bit the sharp shooter you already are. Comes with a functional front pocket for stashing your essentials."
-                    样例4:"For whatever the weekend brings, this skort's built-in shorts mean you can conquer the day without worrying about your bottoms riding up or sliding down. Plus, we designed her with a detachable belt that chinches your waist so good, you'll want to wear it forever. Comes with functional side pockets and closes with a back zip."
-                    样例5:"Consider Dahlia your fool-proof office look. A classic silhouette, but made better with a back zip, edgier angular lines, and a flattering square neck that’s appropriately feminine, yet modern. You’ll look like a 10/10 on that pitch, so go win it."
-                    样例6:"You balance it all — and even if you do break a sweat doing it, we promise Rachel will make you look like you have it all together. For the go-getter that you are, she comes with front and inner pockets, and an elongated silhouette that sits well everything on your weekday calendar."
-                    样例7:"Amiera’s birdseye knit and spongey softness give you that cosy, double-faced wool look without any of the heaviness. And, with her relaxed fit and minimal design, this knit cardigan is as plush as it is practical—like bringing your duvet to brunch."
-                    样例8:"You can’t quite top Joey for her versatility. She's the one to call for your everyday and in-betweens, and when you need a piece of clothing to count on for extra coverage under your layers. Also, she comes with removable bust paddings that are shaped better. How titillating."
-            ### 服装描述(中文)是服装描述(英文)翻译。
-            ### 3个关键点(英文)是从服装描述中提取三个卖点关键词,每个卖点不超过4个单词,内容尽可能简洁。
-                - 3个关键点(英文)例子如下:
-                - 样例:-Beathable. -H-line fit. -Casual and work wear
-        ## 该衣服信息如下:{info}
-        ## 以下是输出样例的格式给你参考
-            - 样例:{{"服装描述(英文)": "Some skirts are meant for you to dance in—Wei’s one of them. Featuring sunray pleats crafted from lightweight crepe, this midaxi skirt flows with ease while her fully elastic waistband ensures maximum comfort. And the best part? She’s machine washable, so you can wear her on repeat. No laundry or dry cleaning required", "服装描述(中文)":"有些裙子是专为让你尽情跳舞而设计的,薇这款裙子就是其中之一。这款中长款迷笛裙采用轻盈的绉纱面料打造出放射状褶裥,能轻松飘逸摆动,而全松紧腰头则确保了极致的舒适感。最棒的是什么呢?这条裙子可机洗,所以你可以反复穿着。无需手洗或干洗。","3个关键点(英文)": "-Bloat-friendly. -Bump-friendly. -Machine Washable"}}
-        ## 请严格按照标准的json格式输出结果给我,用英文的双引号而不是单引号!请直接回答结果给我,不要回答我除此之外的任何其他内容!输出不要带有其他格式(如换行加粗等)。
-    """
-    return en_ch_selling_points  
+# def get_ch_en_selling_points(info):
+#     ch_en_selling_points = f"""
+#         ## 你现在是一位欧美独立站服装卖点文案的专家;我现在要在独立站上写衣服的卖点,请根据我提供给你的图片和文本写出对应的卖点文案。输出的文案包括3个部分,服装描述(中文)+服装描述(英文)+3个关键点(英文)。 
+#         ## 服装描述(中文):以一句大胆的话语作为开头+服装内容的描述(主要是对衣服面料、细节点和特性的描述),其中字数要在100个字以内。
+#             - 引人入胜的开篇:以一句大胆、新颖甚至颠覆常规认知的话语作为开头,瞬间抓住消费者的眼球,最好是形容词+品类的形式。并且句子里面一定要包含衣服的品类的词(比如连衣裙,两件套,外套,裤子,半身裙,衬衫等等)。开头不要用反问句!绝对不要包含词语:这件,这款。
+#             - 服装内容的描述:内容包括衣服面料+细节点(包括口袋、弹力橡筋、头花等)+特性(包括适用季节、场合、搭配等)。如果面料属于桑蚕丝、二、三醋酸、香云纱、羊绒、羊毛、美丽诺羊毛、可水洗羊毛、可机洗羊毛、鹅绒、亚麻、棉、羊毛混纺、羊驼毛、马海毛、天丝,那请优先描述面料的材料(很重要)!不然请优先描述细节点和特性。描述时请描述产品的独特设计和材质,加入带有情感的修饰词。用强烈的色彩对比和生动的词语。强调功能性的同时融入时尚表达。强调设计细节的用心。描述产品如何提升穿着者的气场。
+#             - 服装描述(中文)例子如下:
+#                 样例:充满生机与活力的外套,这款外套是兼具实用与美观的优雅通勤气质单品。四面弹斜纹布透气性好,细腻的光泽感和适度的垂坠性更显高级。金色的双排扣设计是其显著的特点之一,复古优雅。可以搭配同色系的裤子,展现出自己的个性与魅力。
+#         ## 服装描述(英文)是服装描述(中文)的翻译,其中字数要在300个字符以内(包括标点符号),如果翻译后文本还是超过350个字符请简化一下内容。
+#             - 服装描述(英文)例子如下:
+#             - 样例:Green symbolizes vitality and energy, making this jacket the perfect blend of practicality and elegance for daily commutes. Crafted from breathable four-way stretch twill, its fine sheen and graceful drape exude sophistication. Gold double-breasted buttons that add a touch of vintage charm. Pair it with matching trousers to showcase your unique style and charisma.
+#         ## 3个关键点(英文)是从服装描述中提取三个卖点关键词,每个卖点不超过4个单词,内容尽可能简洁。
+#             - 3个关键点(英文)例子如下:
+#             - 样例:-Beathable. -H-line fit. -Casual and work wear
+#         ## 该衣服信息如下:{info}
+#         ## 以下是输出样例的格式给你参考
+#             - 样例:{{"服装描述(中文)": "作为日常通勤的完美选择,这款四面弹阔腿裤具有出色的弹性和透气性,即使在炎炎夏日也能保持凉爽舒适。弹力腰带、侧口袋和前褶等贴心细节既提升了舒适度,又增强了时尚感。搭配同款夹克,打造别致的造型。", "服装描述(英文)": "The perfect choice for daily commutes, these four-way stretch wide-leg pants offer exceptional elasticity and breathability, keeping you cool and comfortable even on hot summer days. Thoughtful details like an elastic waistband, side pockets, and front pleats enhance both comfort and style. Pair them with a matching jacket for a chic coordinated look.", "3个关键点(英文)": "-Elastic. -With elastic waistband. -With pockets"}}
+#         ## 请严格按照标准的json格式输出结果给我,用英文的双引号而不是单引号!请直接回答结果给我,不要回答我除此之外的任何其他内容!输出不要带有其他格式(如换行加粗等)。
+#     """
+#     return ch_en_selling_points
+
+
+# ## 英文变中文卖点文案
+# def get_en_ch_selling_points(info):
+#     en_ch_selling_points = f"""
+#         ## 你现在是一位欧美独立站服装卖点文案的专家;我现在要在独立站上写衣服的卖点,请根据我提供给你的图片和文本写出对应的卖点文案。输出的文案包括3个部分,服装描述(英文)+服装描述(中文)+3个关键点(英文)。 
+#         ## 要求:请为衣服注入灵魂,衣服就像人一样,她/他的设计都是有初衷的,有自己的实用性和时尚性,请描述衣服的卖点的时候,像是在与消费者直接对话,营造出一种亲切、友好的氛围。且运用时尚且专业的词汇,通过细腻的描述来突出产品的特点和优势。巧妙地将产品与具体的生活场景相结合。整体表述简洁,没有过多冗长复杂的句子,信息传达直接高效,能够让读者快速抓住重点,了解产品的主要特点、优势和适用场景。
+#         ## 内容如下:
+#             ### 服装描述(英文):引人入胜的开篇+服装内容的描述(主要是对衣服面料、细节点和特性的描述),其中字数要在300个字符以内(包括标点符号)。
+#                 - 引人入胜的开篇:用新颖含有故事性的话语作为开头,瞬间抓住消费者的眼球。为衣服注入灵魂。衣服是有生命力的,不要用it表述衣服,而是拟人化。使用 “you” 直接与读者交流,亲切且对话感强,运用形象的比喻和富有情感的描述。不要老是以Imagine slipping into作为开头
+#                 - 服装内容的描述:内容包括衣服面料+细节点(包括口袋、弹力橡筋、头花等)+特性(包括适用季节、场合、搭配等)。如果面料属于桑蚕丝、二、三醋酸、香云纱、羊绒、羊毛、美丽诺羊毛、可水洗羊毛、可机洗羊毛、鹅绒、亚麻、棉、羊毛混纺、羊驼毛、马海毛、天丝,那请优先描述面料的材料(很重要)!不然请优先描述细节点和特性。描述时请描述产品的独特设计和材质,加入带有情感的修饰词。用强烈的色彩对比和生动的词语。强调功能性的同时融入时尚表达。强调设计细节的用心。描述产品如何提升穿着者的气场。
+#                 - 服装描述(英文)例子如下:
+#                     样例1:"When you've got no time to waste, wrap up your look in seconds with this sleek vest that lets you face the day head-on. Made from crease-ease fabric, this low-maintenance charmer features a collared neckline and a detachable sash that keeps things looking sharp and always ready."
+#                     样例2:"Like a familiar hug from Ahma, Farah is made of soft knit that gently wraps your curves and will continue to do so without losing shape. With a fixed sash for an added layer of style, she’s an essential that works under blazers or on her own."
+#                     样例3:"Throw Riley on or layer her, and you’ll see why you need her in every colour. We one-upped ourselves and made her with a more refined finish, so you can look every bit the sharp shooter you already are. Comes with a functional front pocket for stashing your essentials."
+#                     样例4:"For whatever the weekend brings, this skort's built-in shorts mean you can conquer the day without worrying about your bottoms riding up or sliding down. Plus, we designed her with a detachable belt that chinches your waist so good, you'll want to wear it forever. Comes with functional side pockets and closes with a back zip."
+#                     样例5:"Consider Dahlia your fool-proof office look. A classic silhouette, but made better with a back zip, edgier angular lines, and a flattering square neck that’s appropriately feminine, yet modern. You’ll look like a 10/10 on that pitch, so go win it."
+#             ### 服装描述(中文)是服装描述(英文)的翻译。
+#             ### 3个关键点(英文)是从服装描述中提取三个卖点关键词,每个卖点不超过4个单词,内容尽可能简洁。
+#                 - 3个关键点(英文)例子如下:
+#                 - 样例:-Beathable. -H-line fit. -Casual and work wear
+#         ## 该衣服信息如下:{info}
+#         ## 以下是输出样例的格式给你参考
+#             - 样例:{{"en": "Some skirts are meant for you to dance in—Wei’s one of them. Featuring sunray pleats crafted from lightweight crepe, this midaxi skirt flows with ease while her fully elastic waistband ensures maximum comfort. And the best part? She’s machine washable, so you can wear her on repeat. No laundry or dry cleaning required", "ch":"有些裙子是专为让你尽情跳舞而设计的,薇这款裙子就是其中之一。这款中长款迷笛裙采用轻盈的绉纱面料打造出放射状褶裥,能轻松飘逸摆动,而全松紧腰头则确保了极致的舒适感。最棒的是什么呢?这条裙子可机洗,所以你可以反复穿着。无需手洗或干洗。","kw": "-Bloat-friendly. -Bump-friendly. -Machine Washable"}}
+#         ## 请严格按照标准的json格式输出结果给我,用英文的双引号而不是单引号!请直接回答结果给我,不要回答我除此之外的任何其他内容!输出不要带有其他格式(如换行加粗等)。
+#     """
+#     return en_ch_selling_points          
+
+# def get_en_ch_selling_points_his(info,history):
+#     en_ch_selling_points = f"""
+#         ## 你现在是一位欧美独立站服装卖点文案的专家;我现在要在独立站上写衣服的卖点,请根据我提供给你的图片和文本写出对应的卖点文案。输出的文案包括3个部分,服装描述(英文)+服装描述(中文)+3个关键点(英文)。 
+#         ## 要求:请为衣服注入灵魂,衣服就像人一样,她/他的设计都是有初衷的,有自己的实用性和时尚性,请描述衣服的卖点的时候,像是在与消费者直接对话,营造出一种亲切、友好的氛围。且运用时尚且专业的词汇,通过细腻的描述来突出产品的特点和优势。巧妙地将产品与具体的生活场景相结合。整体表述简洁,没有过多冗长复杂的句子,信息传达直接高效,能够让读者快速抓住重点,了解产品的主要特点、优势和适用场景。
+#         ## 内容如下:
+#             ### 服装描述(英文):引人入胜的开篇+服装内容的描述(主要是对衣服面料、细节点和特性的描述),其中字数要在300个字符以内(包括标点符号)内容里面不得包含款号和价格信息!!!要用英文描述,不能夹杂着中文!!!
+
+#                 - 引人入胜的开篇:用新颖含有故事性的话语作为开头,瞬间抓住消费者的眼球。为衣服注入灵魂。衣服是有生命力的。使用 “you” 直接与读者交流,亲切且对话感强,运用形象的比喻和富有情感的描述。不要老是以Imagine slipping into或者Embrace或者Discovery作为开头
+#                     注意!!以下是常用的开篇,请不要再重复使用或者出现相同的词汇(如果使用重复的词汇则判断为类似),请生成比下面更加新颖的开头:
+#                         {history}
+#                 - 服装内容的描述:内容包括衣服面料+细节点(包括口袋、弹力橡筋、头花等)+特性(包括适用季节、场合、搭配等)。如果面料属于桑蚕丝、二、三醋酸、香云纱、羊绒、羊毛、美丽诺羊毛、可水洗羊毛、可机洗羊毛、鹅绒、亚麻、棉、羊毛混纺、羊驼毛、马海毛、天丝,那请优先描述面料的材料(很重要)!不然请优先描述细节点和特性。描述时请描述产品的独特设计和材质,加入带有情感的修饰词。用强烈的色彩对比和生动的词语。强调功能性的同时融入时尚表达。强调设计细节的用心。描述产品如何提升穿着者的气场。
+#                 - 服装描述(英文)例子如下,请重点参考下面的样例的结构和语言风格进行生成卖点文案,但是不能输出一样的句子:
+#                     样例1:"When you've got no time to waste, wrap up your look in seconds with this sleek vest that lets you face the day head-on. Made from crease-ease fabric, this low-maintenance charmer features a collared neckline and a detachable sash that keeps things looking sharp and always ready."
+#                     样例2:"Like a familiar hug from Ahma, Farah is made of soft knit that gently wraps your curves and will continue to do so without losing shape. With a fixed sash for an added layer of style, It's an essential that works under blazers or on her own."
+#                     样例3:"Throw Riley on or layer her, and you'll see why you need her in every colour. We one-upped ourselves and made her with a more refined finish, so you can look every bit the sharp shooter you already are. Comes with a functional front pocket for stashing your essentials."
+#                     样例4:"For whatever the weekend brings, this skort's built-in shorts mean you can conquer the day without worrying about your bottoms riding up or sliding down. Plus, we designed her with a detachable belt that chinches your waist so good, you'll want to wear it forever. Comes with functional side pockets and closes with a back zip."
+#                     样例5:"Consider Dahlia your fool-proof office look. A classic silhouette, but made better with a back zip, edgier angular lines, and a flattering square neck that's appropriately feminine, yet modern. You'll look like a 10/10 on that pitch, so go win it."
+#                     样例6:"You balance it all — and even if you do break a sweat doing it, we promise Rachel will make you look like you have it all together. For the go-getter that you are, It comes with front and inner pockets, and an elongated silhouette that sits well everything on your weekday calendar."
+#                     样例7:"Amiera's birdseye knit and spongey softness give you that cosy, double-faced wool look without any of the heaviness. And, with her relaxed fit and minimal design, this knit cardigan is as plush as it is practical—like bringing your duvet to brunch."
+#                     样例8:"You can't quite top Joey for her versatility. It's the one to call for your everyday and in-betweens, and when you need a piece of clothing to count on for extra coverage under your layers. Also, It comes with removable bust paddings that are shaped better. How titillating."
+#             ### 服装描述(中文)是服装描述(英文)翻译成中文
+#             ### 3个关键点(英文)是从服装描述中提取三个卖点关键词,每个卖点不超过4个单词,内容尽可能简洁。
+#                 - 3个关键点(英文)例子如下:
+#                 - 样例:-Beathable. -H-line fit. -Casual and work wear
+#         ## 该衣服信息如下:{info}
+#         ## 以下是输出样例的格式给你参考,请参考该格式的key输出字典给我:
+#             - 样例:{{"en": "Some skirts are meant for you to dance in—Wei’s one of them. Featuring sunray pleats crafted from lightweight crepe, this midaxi skirt flows with ease while her fully elastic waistband ensures maximum comfort. And the best part? It's machine washable, so you can wear her on repeat. No laundry or dry cleaning required", "ch":"有些裙子是专为让你尽情跳舞而设计的,薇这款裙子就是其中之一。这款中长款迷笛裙采用轻盈的绉纱面料打造出放射状褶裥,能轻松飘逸摆动,而全松紧腰头则确保了极致的舒适感。最棒的是什么呢?这条裙子可机洗,所以你可以反复穿着。无需手洗或干洗。","kw": "-Bloat-friendly. -Bump-friendly. -Machine Washable"}}
+#         ## 请严格按照标准的字典格式输出结果给我,用英文的双引号而不是单引号!请直接回答结果给我,不要回答我除此之外的任何其他内容!输出不要带有其他格式(如换行加粗等)。
+#     """
+#     return en_ch_selling_points  
         
+system_prompt = f"""## 角色
+你是一位顶级的欧美独立站(DTC, Direct-to-Consumer)服装文案专家。
+
+## 绝对规则 (最高优先级,必须严格遵守!)
+1.  你【必须】严格按照下面指定的JSON字典格式输出。使用英文双引号,不要有任何其他多余的解释或文本。
+2.  输出的JSON必须包含且仅包含 "en", "ch", "kw" 三个键。
+3.  "en" 键的值(英文描述)的长度【必须严格控制在450个字符以内】(包括标点符号)。这是死规定!(STRICTLY under 450 characters!)
+4.  "ch" 键的值必须是 "en" 值的准确、自然的中文翻译。
+5.  "kw" 键的值是一个包含3个英文卖点的列表,**必须**根据用户输入中的**“标签信息”**提炼生成,每个卖点不超过4个单词。
+
+
+## JSON输出格式样例
+{{
+    "en": "Some skirts are meant for you to dance in—Wei’s one of them. Featuring sunray pleats crafted from lightweight crepe, this midaxi skirt flows with ease while her fully elastic waistband ensures maximum comfort. And the best part? It's machine washable, so you can wear her on repeat. No laundry or dry cleaning required", 
+    "ch": "有些裙子是专为让你尽情跳舞而设计的,薇这款裙子就是其中之一。这款中长款迷笛裙采用轻盈的绉纱面料打造出放射状褶裥,能轻松飘逸摆动,而全松紧腰头则确保了极致的舒适感。最棒的是什么呢?这条裙子可机洗,所以你可以反复穿着。无需手洗或干洗。",
+    "kw": ["Bloat-friendly", "Bump-friendly", "Machine Washable"]
+}}
+
+## 风格指南
+1.  **对话感 (Conversational)**: 像在与消费者(用"you")直接对话,营造亲切、友好的氛围。
+2.  **注入灵魂 (Soulful)**: 描述衣服的初衷、设计故事或它能带来的独特感受。
+3.  **场景化 (Vivid Scenes)**: 巧妙地将产品与具体的生活场景(如办公室、周末早午餐、度假)相结合。
+4.  **专业且生动 (Professional & Vivid)**: 运用时尚且专业的词汇,通过细腻、带有情感的描述突出产品特点。
+5.  **高效简洁 (Concise)**: 信息传达直接,没有冗长复杂的句子。
+
+## "en" 描述的具体要求
+    * **高优先级面料**: 如果面料属于(桑蚕丝、二/三醋酸、香云纱、羊绒、羊毛、美丽诺羊毛、可水洗/可机洗羊毛、鹅绒、亚麻、棉、羊驼毛、马海毛、天丝),请【优先重点描述】面料的珍贵和触感。
+    * **其他**: 否则,请优先描述独特的设计细节点(如口袋、弹力橡筋、剪裁)和特性(适用季节、场合、搭配)。
+    * 强调设计如何提升穿着者的气场和体验。
+"""
+
+user_prompt = """## 任务
+请根据随此消息传入的**图片**和**衣服基本信息**,并结合**历史记录规避要求**,生成卖点文案。
+你必须先【仔细观察图片】,分析出衣服的**风格、剪裁、色彩搭配**等视觉信息,然后结合下面的基本信息进行文案创作。
+
+## 衣服基本信息 (请结合图片分析结果来使用)
+{basic_info_string}
+
+## 历史记录规避 (避免重复!)
+你【必须】生成一个全新的开篇。请【绝对避免】使用以下历史样例中的开篇句式或相似词汇:
+{history_string}
+
+## 优秀样例 (请学习风格,但不要抄袭句子)
+* 样例1: "Zero bulk, just a cosy number that keeps you feeling light no matter what comes your way. We reinvented this best-seller with a shorter length to sidestep the fuss of tucking. Crafted from full-needle knit, so you can have that body-hugging fit that snugly highlights your curves while offering just the right amount of stretch. Whether layered under blazers or worn solo, Mikaela’s that one solution to all your layering dilemmas."
+* 样例2: "Like a familiar hug from Ahma, Farah is made of soft knit that gently wraps your curves and will continue to do so without losing shape. With a fixed sash for an added layer of style, It's an essential that works under blazers or on her own."
+* 样例3: "Throw Riley on or layer her, and you'll see why you need her in every colour. We one-upped ourselves and made her with a more refined finish, so you can look every bit the sharp shooter you already are. Comes with a functional front pocket for stashing your essentials."
+* 样例4: "For whatever the weekend brings, this skort's built-in shorts mean you can conquer the day without worrying about your bottoms riding up or sliding down. Plus, we designed her with a detachable belt that chinches your waist so good, you'll want to wear it forever. Comes with functional side pockets and closes with a back zip."
+* 样例5: "Consider Dahlia your fool-proof office look. A classic silhouette, but made better with a back zip, edgier angular lines, and a flattering square neck that's appropriately feminine, yet modern. You'll look like a 10/10 on that pitch, so go win it."
+* 样例6: "You balance it all — and even if you do break a sweat doing it, we promise Rachel will make you look like you have it all together. For the go-getter that you are, It comes with front and inner pockets, and an elongated silhouette that sits well everything on your weekday calendar."
+* 样例7: "Amiera's birdseye knit and spongey softness give you that cosy, double-faced wool look without any of the heaviness. And, with her relaxed fit and minimal design, this knit cardigan is as plush as it is practical—like bringing your duvet to brunch."
+* 样例8: "You can't quite top Joey for her versatility. It's the one to call for your everyday and in-betweens, and when you need a piece of clothing to count on for extra coverage under your layers. Also, It comes with removable bust paddings that are shaped better. How titillating."
+## 最终输出
+请直接输出严格符合System规则的JSON字典。
+"""
+
+
 ## 文本简化控制字数
 
-def TextControl_his(info,his):
-    sys_prompt="你是一个服装商品详情描述美化专家,请对用户输入的英文卖点进行美化,并返回结果"
-    usr_prompt = f"""
-        ## 你现在是一位欧美独立站服装卖点文案的专家;我现在要在独立站上写衣服的卖点,请根据我提供给你的图片和文本写出对应的卖点文案。输出的文案包括3个部分,服装描述(英文)+服装描述(中文)+3个关键点(英文)。 
-        ## 要求:请为衣服注入灵魂,衣服就像人一样,她/他的设计都是有初衷的,有自己的实用性和时尚性,请描述衣服的卖点的时候,像是在与消费者直接对话,营造出一种亲切、友好的氛围。且运用时尚且专业的词汇,通过细腻的描述来突出产品的特点和优势。巧妙地将产品与具体的生活场景相结合。整体表述简洁,没有过多冗长复杂的句子,信息传达直接高效,能够让读者快速抓住重点,了解产品的主要特点、优势和适用场景。
-        ## 内容如下:
-            ### 服装描述(英文):引人入胜的开篇+服装内容的描述(主要是对衣服面料、细节点和特性的描述),其中字数要在300个字符以内(包括标点符号)。
-
-                - 引人入胜的开篇:用新颖含有故事性的话语作为开头,瞬间抓住消费者的眼球。为衣服注入灵魂。衣服是有生命力的,不要用it表述衣服,而是拟人化。使用 “you” 直接与读者交流,亲切且对话感强,运用形象的比喻和富有情感的描述。不要老是以Imagine slipping into或者Embrace或者Discovery作为开头
-                    注意!!以下是常用的开篇,请不要再重复使用或者出现相同的词汇(如果使用重复的词汇则判断为类似),请生成比下面更加新颖的开头:
-                        {his}
-                - 服装内容的描述:内容包括衣服面料+细节点(包括口袋、弹力橡筋、头花等)+特性(包括适用季节、场合、搭配等)。如果面料属于桑蚕丝、二、三醋酸、香云纱、羊绒、羊毛、美丽诺羊毛、可水洗羊毛、可机洗羊毛、鹅绒、亚麻、棉、羊毛混纺、羊驼毛、马海毛、天丝,那请优先描述面料的材料(很重要)!不然请优先描述细节点和特性。描述时请描述产品的独特设计和材质,加入带有情感的修饰词。用强烈的色彩对比和生动的词语。强调功能性的同时融入时尚表达。强调设计细节的用心。描述产品如何提升穿着者的气场。
-                - 服装描述(英文)例子如下,请重点参考下面的样例的结构和语言风格进行生成卖点文案,但是不能输出一样的句子:
-                    样例1:"When you've got no time to waste, wrap up your look in seconds with this sleek vest that lets you face the day head-on. Made from crease-ease fabric, this low-maintenance charmer features a collared neckline and a detachable sash that keeps things looking sharp and always ready."
-                    样例2:"Like a familiar hug from Ahma, Farah is made of soft knit that gently wraps your curves and will continue to do so without losing shape. With a fixed sash for an added layer of style, she’s an essential that works under blazers or on her own."
-                    样例3:"Throw Riley on or layer her, and you’ll see why you need her in every colour. We one-upped ourselves and made her with a more refined finish, so you can look every bit the sharp shooter you already are. Comes with a functional front pocket for stashing your essentials."
-                    样例4:"For whatever the weekend brings, this skort's built-in shorts mean you can conquer the day without worrying about your bottoms riding up or sliding down. Plus, we designed her with a detachable belt that chinches your waist so good, you'll want to wear it forever. Comes with functional side pockets and closes with a back zip."
-                    样例5:"Consider Dahlia your fool-proof office look. A classic silhouette, but made better with a back zip, edgier angular lines, and a flattering square neck that’s appropriately feminine, yet modern. You’ll look like a 10/10 on that pitch, so go win it."
-                    样例6:"You balance it all — and even if you do break a sweat doing it, we promise Rachel will make you look like you have it all together. For the go-getter that you are, she comes with front and inner pockets, and an elongated silhouette that sits well everything on your weekday calendar."
-                    样例7:"Amiera’s birdseye knit and spongey softness give you that cosy, double-faced wool look without any of the heaviness. And, with her relaxed fit and minimal design, this knit cardigan is as plush as it is practical—like bringing your duvet to brunch."
-                    样例8:"You can’t quite top Joey for her versatility. She's the one to call for your everyday and in-betweens, and when you need a piece of clothing to count on for extra coverage under your layers. Also, she comes with removable bust paddings that are shaped better. How titillating."
-            ### 服装描述(中文)是服装描述(英文)的翻译。
-            ### 3个关键点(英文)是从服装描述中提取三个卖点关键词,每个卖点不超过4个单词,内容尽可能简洁。
-                - 3个关键点(英文)例子如下:
-                - 样例:-Beathable. -H-line fit. -Casual and work wear
-        ## 该衣服信息如下:{info}
-        ## 以下是输出样例的格式给你参考
-            - 样例:{{"服装描述(英文)": "Some skirts are meant for you to dance in—Wei’s one of them. Featuring sunray pleats crafted from lightweight crepe, this midaxi skirt flows with ease while her fully elastic waistband ensures maximum comfort. And the best part? She’s machine washable, so you can wear her on repeat. No laundry or dry cleaning required", "服装描述(中文)":"有些裙子是专为让你尽情跳舞而设计的,薇这款裙子就是其中之一。这款中长款迷笛裙采用轻盈的绉纱面料打造出放射状褶裥,能轻松飘逸摆动,而全松紧腰头则确保了极致的舒适感。最棒的是什么呢?这条裙子可机洗,所以你可以反复穿着。无需手洗或干洗。","3个关键点(英文)": "-Bloat-friendly. -Bump-friendly. -Machine Washable"}}
-        ## 请严格按照标准的json格式输出结果给我,用英文的双引号而不是单引号!请直接回答结果给我,不要回答我除此之外的任何其他内容!输出不要带有其他格式(如换行加粗等)。
-    """
-    return sys_prompt,usr_prompt
+# def TextControl_his(info,his):
+#     sys_prompt="你是一个服装商品详情描述美化专家,请对用户输入的英文卖点进行美化,并返回结果"
+#     usr_prompt = f"""
+#         ## 你现在是一位欧美独立站服装卖点文案的专家;我现在要在独立站上写衣服的卖点,请根据我提供给你的图片和文本写出对应的卖点文案。输出的文案包括3个部分,服装描述(英文)+服装描述(中文)+3个关键点(英文)。 
+#         ## 要求:请为衣服注入灵魂,衣服就像人一样,她/他的设计都是有初衷的,有自己的实用性和时尚性,请描述衣服的卖点的时候,像是在与消费者直接对话,营造出一种亲切、友好的氛围。且运用时尚且专业的词汇,通过细腻的描述来突出产品的特点和优势。巧妙地将产品与具体的生活场景相结合。整体表述简洁,没有过多冗长复杂的句子,信息传达直接高效,能够让读者快速抓住重点,了解产品的主要特点、优势和适用场景。
+#         ## 内容如下:
+#             ### 服装描述(英文):引人入胜的开篇+服装内容的描述(主要是对衣服面料、细节点和特性的描述),其中字数要在300个字符以内(包括标点符号)。
+
+#                 - 引人入胜的开篇:用新颖含有故事性的话语作为开头,瞬间抓住消费者的眼球。为衣服注入灵魂。衣服是有生命力的,不要用it表述衣服,而是拟人化。使用 “you” 直接与读者交流,亲切且对话感强,运用形象的比喻和富有情感的描述。不要老是以Imagine slipping into或者Embrace或者Discovery作为开头
+#                     注意!!以下是常用的开篇,请不要再重复使用或者出现相同的词汇(如果使用重复的词汇则判断为类似),请生成比下面更加新颖的开头:
+#                         {his}
+#                 - 服装内容的描述:内容包括衣服面料+细节点(包括口袋、弹力橡筋、头花等)+特性(包括适用季节、场合、搭配等)。如果面料属于桑蚕丝、二、三醋酸、香云纱、羊绒、羊毛、美丽诺羊毛、可水洗羊毛、可机洗羊毛、鹅绒、亚麻、棉、羊毛混纺、羊驼毛、马海毛、天丝,那请优先描述面料的材料(很重要)!不然请优先描述细节点和特性。描述时请描述产品的独特设计和材质,加入带有情感的修饰词。用强烈的色彩对比和生动的词语。强调功能性的同时融入时尚表达。强调设计细节的用心。描述产品如何提升穿着者的气场。
+#                 - 服装描述(英文)例子如下,请重点参考下面的样例的结构和语言风格进行生成卖点文案,但是不能输出一样的句子:
+#                     样例1:"When you've got no time to waste, wrap up your look in seconds with this sleek vest that lets you face the day head-on. Made from crease-ease fabric, this low-maintenance charmer features a collared neckline and a detachable sash that keeps things looking sharp and always ready."
+#                     样例2:"Like a familiar hug from Ahma, Farah is made of soft knit that gently wraps your curves and will continue to do so without losing shape. With a fixed sash for an added layer of style, she’s an essential that works under blazers or on her own."
+#                     样例3:"Throw Riley on or layer her, and you’ll see why you need her in every colour. We one-upped ourselves and made her with a more refined finish, so you can look every bit the sharp shooter you already are. Comes with a functional front pocket for stashing your essentials."
+#                     样例4:"For whatever the weekend brings, this skort's built-in shorts mean you can conquer the day without worrying about your bottoms riding up or sliding down. Plus, we designed her with a detachable belt that chinches your waist so good, you'll want to wear it forever. Comes with functional side pockets and closes with a back zip."
+#                     样例5:"Consider Dahlia your fool-proof office look. A classic silhouette, but made better with a back zip, edgier angular lines, and a flattering square neck that’s appropriately feminine, yet modern. You’ll look like a 10/10 on that pitch, so go win it."
+#                     样例6:"You balance it all — and even if you do break a sweat doing it, we promise Rachel will make you look like you have it all together. For the go-getter that you are, she comes with front and inner pockets, and an elongated silhouette that sits well everything on your weekday calendar."
+#                     样例7:"Amiera’s birdseye knit and spongey softness give you that cosy, double-faced wool look without any of the heaviness. And, with her relaxed fit and minimal design, this knit cardigan is as plush as it is practical—like bringing your duvet to brunch."
+#                     样例8:"You can’t quite top Joey for her versatility. She's the one to call for your everyday and in-betweens, and when you need a piece of clothing to count on for extra coverage under your layers. Also, she comes with removable bust paddings that are shaped better. How titillating."
+#             ### 服装描述(中文)是服装描述(英文)的翻译。
+#             ### 3个关键点(英文)是从服装描述中提取三个卖点关键词,每个卖点不超过4个单词,内容尽可能简洁。
+#                 - 3个关键点(英文)例子如下:
+#                 - 样例:-Beathable. -H-line fit. -Casual and work wear
+#         ## 该衣服信息如下:{info}
+#         ## 以下是输出样例的格式给你参考
+#             - 样例:{{"en": "Some skirts are meant for you to dance in—Wei’s one of them. Featuring sunray pleats crafted from lightweight crepe, this midaxi skirt flows with ease while her fully elastic waistband ensures maximum comfort. And the best part? She’s machine washable, so you can wear her on repeat. No laundry or dry cleaning required", "ch":"有些裙子是专为让你尽情跳舞而设计的,薇这款裙子就是其中之一。这款中长款迷笛裙采用轻盈的绉纱面料打造出放射状褶裥,能轻松飘逸摆动,而全松紧腰头则确保了极致的舒适感。最棒的是什么呢?这条裙子可机洗,所以你可以反复穿着。无需手洗或干洗。","kw": "-Bloat-friendly. -Bump-friendly. -Machine Washable"}}
+#         ## 请严格按照标准的json格式输出结果给我,用英文的双引号而不是单引号!请直接回答结果给我,不要回答我除此之外的任何其他内容!输出不要带有其他格式(如换行加粗等)。
+#     """
+#     return sys_prompt,usr_prompt
 
 def TextControl(info):
-    sys_prompt="你是一个文案总结高手,请对用户输入的文案进行简化,并返回结果,不要对内容进行拓展"
-    usr_prompt=f"""请对下面的英文进行缩短简化,简化后的英文字符数量必须在150-200之间(包括标点符号,请记住不是单词数量,是每个字符数)。请直接返回结果,不要输出其他内容。内容如下:{info}"""
+    sys_prompt="你是一位资深的中英翻译专家,擅长处理中英混杂的文案。请将用户提供的内容翻译为纯英文,保持原意,不进行任何内容扩展或润色。仅输出翻译后的英文文案,不添加解释或注释。例如,将“欢迎来到我们的 store,享受 best deals!”转换为“Welcome to our store, enjoy the best deals!”。"
+    usr_prompt=f"""请将以下文案转换为纯英文:{info}"""
     return sys_prompt,usr_prompt
 
 
@@ -127,7 +183,7 @@ def get_ch_en_selling_title(info):
     sys_prompt="你是一个产品标题专家,请对用户输入的服装信息写标题"
     usr_prompt=f"""## 我现在要在独立站上写衣服商品的标题,请根据我提供给你的衣服英文商品详情描述提取出对应的信息并写出对应的英文卖点标题。输出的标题包括2个部分,标题(英文)和把标题翻译成中文后的结果。标题单词数:7—10个单词,不能超过这个数量,要是超过请重新思考后再生成。
         ## 标题框架:卖点+Women/Unisex+二级品类。请严格按照这个框架写卖点的英文标题,语句不需要通顺,只需要从服装商品描述中提取相关关键信息进行组合。
-            ### 卖点内容包括面料、颜色印花和版型设计,其中面料是最重要的信息,其次才是颜色印花和版型设计
+            ### 卖点内容包括面料、颜色印花和版型设计,其中面料是最重要的信息(如果里面有面料信息一定要说明是哪种面料!!!),其次才是颜色印花和版型设计
                 - 面料:属于香云纱、羊绒、羊毛、羊毛混纺、牦牛绒、骆驼绒、粗花呢、亮片、牛仔、真丝、天丝、醋酸、三醋酸、100%棉、仿麂皮、Pu 革等,如果服装商品描述中包含这些面料,那请优先描述面料的材料(很重要)!
                 - 颜色印花:提花、刺绣、各式印花花色、条纹、渐变色等,这些是很关键的信息
                 - 版型设计:圆领、V领、荷叶边领口、荷叶边下摆、一字肩、露肩、单排扣、双排扣、收腰、高腰、可调节纽扣、抽绳、拼接、撞色边、撞色领、侧边开视、镂空、一片式设计等。

+ 62 - 0
supervisor_flask_app.conf

@@ -0,0 +1,62 @@
+[program:flask_app]
+# Gunicorn 启动命令
+# 添加 --capture-output 和 --enable-stdio-inheritance 确保日志正确捕获
+# --log-level info 确保gunicorn也输出INFO级别日志
+command=/data/Miniconda3/envs/tool/bin/gunicorn -w 4 -b 0.0.0.0:5555 --timeout 300 --capture-output --enable-stdio-inheritance --log-level info app_v2:app
+
+# 工作目录
+directory=/data/data/Mia/product_env_project/gen_sellpoint
+
+# 运行用户
+user=root
+
+# 自动启动
+autostart=true
+
+# 自动重启
+autorestart=true
+
+# 重启延迟(秒)
+startsecs=3
+
+# 重试次数
+startretries=3
+
+# 进程退出码,如果是这些值则不重启(正常退出)
+exitcodes=0
+
+# 停止前等待时间(秒)
+stopwaitsecs=10
+
+# 停止信号
+stopsignal=TERM
+
+# 标准错误输出日志文件
+stderr_logfile=/data/data/Mia/product_env_project/gen_sellpoint/flask_app.err.log
+
+# 标准输出日志文件(如果需要区分stderr和stdout,可以分开)
+stdout_logfile=/data/data/Mia/product_env_project/gen_sellpoint/flask_app.out.log
+
+# 日志文件最大大小(0表示不限制)
+stdout_logfile_maxbytes=50MB
+
+# 日志文件备份数量
+stdout_logfile_backups=10
+
+# 标准错误日志文件最大大小
+stderr_logfile_maxbytes=50MB
+
+# 标准错误日志文件备份数量
+stderr_logfile_backups=10
+
+# 环境变量 - 非常重要!
+# PYTHONUNBUFFERED=1 确保Python输出不被缓冲,日志能立即写入
+# 这样可以确保chat和llm模块的日志能及时输出
+environment=PYTHONUNBUFFERED=1,PYTHONIOENCODING=utf-8
+
+# 重定向stderr到stdout(可选,如果需要合并日志)
+# redirect_stderr=true
+
+# 优先级(数字越小优先级越高)
+priority=999
+

+ 0 - 102
test.py

@@ -1,102 +0,0 @@
-import io
-import numpy as np
-from openai import OpenAI
-import os
-import base64
-from PIL import Image
-
-def image_to_base64(image):
-    # 将Image对象转换为BytesIO对象
-    image_io = io.BytesIO()
-    image.save(image_io, format='PNG')
-    image_io.seek(0)
-
-    # 使用base64编码
-    image_base64 = base64.b64encode(image_io.read()).decode('utf-8')
-
-    return f"data:image/png;base64,{image_base64}"
-
-def image_reader(image):
-    """图片读取器,输出PIL.Image格式的图片"""
-    if isinstance(image,str):
-        if image.startswith("http"):
-            return image
-        else:
-            image_path = image
-        out_image = Image.open(image_path)
-    elif isinstance(image,np.ndarray):
-        out_image = Image.fromarray(image)
-    else:
-        out_image = image
-    out_image=out_image.convert('RGB')
-    base64_img=image_to_base64(out_image)
-    return base64_img
-
-#  base 64 编码格式
-# def encode_image(image_path):
-#     with open(image_path, "rb") as image_file:
-#         return base64.b64encode(image_file.read()).decode("utf-8")
-    
-
-
-
-# 将xxxx/test.png替换为你本地图像的绝对路径
-# base64_image = encode_image("/data/data/Mia/product_env_project/gen_sellpoint/企业微信截图_17372766091671.png")
-img="/data/data/Mia/product_env_project/gen_sellpoint/扫描全能王 2025-03-12 15.12_01(1).jpg"
-client = OpenAI(
-    # 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx"
-    api_key='sk-04b63960983445f980d85ff185a17876',
-    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
-)
-completion = client.chat.completions.create(
-    model="qwen-vl-max-latest",#
-    messages=[
-    	{
-    	    "role": "system",
-            "content": [{"type":"text","text": "You are a helpful assistant."}]},
-        {
-            "role": "user",
-            "content": [
-                {
-                    "type": "image_url",
-                    # 需要注意,传入Base64,图像格式(即image/{format})需要与支持的图片列表中的Content Type保持一致。"f"是字符串格式化的方法。
-                    # PNG图像:  f"data:image/png;base64,{base64_image}"
-                    # JPEG图像: f"data:image/jpeg;base64,{base64_image}"
-                    # WEBP图像: f"data:image/webp;base64,{base64_image}"
-                    "image_url": {"url": image_reader(img)}, 
-                },
-                {"type": "text", "text": "输入图片的字段信息,输出的时候有格式化一点"},
-            ],
-        }
-    ],
-)
-print(completion.choices[0].message.content)
-
-
-# client = OpenAI(
-#     # 此为默认路径,您可根据业务所在地域进行配置
-#     base_url="https://ark.cn-beijing.volces.com/api/v3",
-#     # 从环境变量中获取您的 API Key。此为默认方式,您可根据需要进行修改
-#     api_key='817dff39-5586-4f9b-acba-55004167c0b1',
-# )
-
-# response = client.chat.completions.create(
-#     # 指定您创建的方舟推理接入点 ID,此处已帮您修改为您的推理接入点 ID
-#     model="doubao-1-5-vision-pro-32k-250115",
-#     messages=[
-#         {
-#             "role": "user",
-#             "content": [
-#                 {"type": "text", "text": "如果图片里有文字的话,请结合图片里的衣服和文本信息进行描述一下衣服,还要具体到衣服的风格"},
-#                 {
-#                     "type": "image_url",
-#                     "image_url": {
-#                         "url": image_reader(img)
-#                     },
-#                 },
-#             ],
-#         }
-#     ],
-# )
-
-# print(response.choices[0])

+ 179 - 0
test_api.py

@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+API 测试脚本
+用于测试 /api/description 和 /api/title 两个端点
+"""
+import requests
+import json
+import sys
+from typing import Dict, Any
+
+# API 基础配置
+BASE_URL = "http://60.165.238.181:5555"  # 修改为你的服务地址
+
+def test_api_description():
+    """测试商品描述生成接口"""
+    print("=" * 80)
+    print("测试 /api/description 接口")
+    print("=" * 80)
+    
+    url = f"{BASE_URL}/api/description"
+    data = {'img': 'https://img2.goelia.com.au/prod/product/1ENC6E220/material/main/Shopify/-1/72736752b0ad405382d5ed277dabc660.jpg', 
+        'graphic_label': ['-100% Merino wool', '-With pockets', '-H-line fit'], 
+        'spu': '1ENC6E220', 
+        'plm_info': '1、手工流苏边设计   2、贴袋设计   3、金属纽扣'}
+    
+    print(f"\n请求 URL: {url}")
+    print(f"请求数据: {json.dumps(data, ensure_ascii=False, indent=2)}")
+    
+    try:
+        response = requests.post(url, json=data, timeout=120)
+        
+        print(f"\n响应状态码: {response.status_code}")
+        print(f"响应内容:")
+        print(json.dumps(response.json(), ensure_ascii=False, indent=2))
+        
+        if response.status_code == 200:
+            print("\n✅ 测试通过")
+            return True
+        else:
+            print("\n❌ 测试失败")
+            return False
+            
+    except requests.exceptions.ConnectionError:
+        print("\n❌ 连接错误: 无法连接到服务器,请确保服务已启动")
+        return False
+    except requests.exceptions.Timeout:
+        print("\n❌ 超时错误: 请求超时")
+        return False
+    except Exception as e:
+        print(f"\n❌ 发生错误: {str(e)}")
+        return False
+
+
+def test_api_title():
+    """测试商品标题生成接口"""
+    print("\n" + "=" * 80)
+    print("测试 /api/title 接口")
+    print("=" * 80)
+    
+    url = f"{BASE_URL}/api/title"
+    data = {
+        "spu": "TEST_SPU_002",
+        "desc": "This maxi dress features unparalleled comfort and a unique texture with its tencel blend fabric. The square neckline and smocked bodice create a flattering silhouette, while the layered skirt adds romantic flair.",
+        "tags": ["舒适", "优雅", "修身"],
+        "reference_title": "Elegant Maxi Dress with Floral Pattern"
+    }
+    
+    print(f"\n请求 URL: {url}")
+    print(f"请求数据: {json.dumps(data, ensure_ascii=False, indent=2)}")
+    
+    try:
+        response = requests.post(url, json=data, timeout=120)
+        
+        print(f"\n响应状态码: {response.status_code}")
+        print(f"响应内容:")
+        print(json.dumps(response.json(), ensure_ascii=False, indent=2))
+        
+        if response.status_code == 200:
+            print("\n✅ 测试通过")
+            return True
+        else:
+            print("\n❌ 测试失败")
+            return False
+            
+    except requests.exceptions.ConnectionError:
+        print("\n❌ 连接错误: 无法连接到服务器,请确保服务已启动")
+        return False
+    except requests.exceptions.Timeout:
+        print("\n❌ 超时错误: 请求超时")
+        return False
+    except Exception as e:
+        print(f"\n❌ 发生错误: {str(e)}")
+        return False
+
+
+def test_error_cases():
+    """测试错误情况"""
+    print("\n" + "=" * 80)
+    print("测试错误情况处理")
+    print("=" * 80)
+    
+    # 测试缺少必需字段
+    print("\n1. 测试缺少必需字段 (description)")
+    url = f"{BASE_URL}/api/description"
+    data = {
+        "spu": "TEST_SPU_003",
+        # 缺少 plm_info, img, graphic_label
+    }
+    
+    try:
+        response = requests.post(url, json=data, timeout=10)
+        print(f"响应状态码: {response.status_code}")
+        print(f"响应内容: {json.dumps(response.json(), ensure_ascii=False, indent=2)}")
+    except Exception as e:
+        print(f"发生错误: {str(e)}")
+    
+    # 测试无效的图片URL
+    print("\n2. 测试无效的图片URL")
+    data = {
+        "spu": "TEST_SPU_004",
+        "plm_info": "测试商品信息",
+        "img": "https://invalid-url-test.com/nonexistent.jpg",
+        "graphic_label": ["测试标签"]
+    }
+    
+    try:
+        response = requests.post(url, json=data, timeout=30)
+        print(f"响应状态码: {response.status_code}")
+        print(f"响应内容: {json.dumps(response.json(), ensure_ascii=False, indent=2)}")
+    except Exception as e:
+        print(f"发生错误: {str(e)}")
+
+
+def main():
+    """主函数"""
+    print("\n" + "=" * 80)
+    print("开始 API 测试")
+    print("=" * 80)
+    
+    # 检查服务器是否运行
+    try:
+        response = requests.get(f"{BASE_URL}/", timeout=5)
+        print("✅ 服务器正在运行")
+    except requests.exceptions.ConnectionError:
+        print("⚠️  警告: 无法连接到服务器")
+        print("请确保 Flask 应用已启动 (app_v2.py)")
+        response = input("\n是否继续测试? (y/n): ")
+        if response.lower() != 'y':
+            sys.exit(0)
+    except Exception as e:
+        print(f"⚠️  警告: {str(e)}")
+    
+    results = []
+    
+    # 测试描述生成接口
+    results.append(("Description API", test_api_description()))
+    
+    # 测试标题生成接口
+    # results.append(("Title API", test_api_title()))
+    
+    # 测试错误情况
+    # test_error_cases()
+    
+    # 打印测试总结
+    print("\n" + "=" * 80)
+    print("测试总结")
+    print("=" * 80)
+    for test_name, result in results:
+        status = "✅ 通过" if result else "❌ 失败"
+        print(f"{test_name}: {status}")
+    
+    passed = sum(1 for _, result in results if result)
+    total = len(results)
+    print(f"\n总计: {passed}/{total} 个测试通过")
+
+
+if __name__ == "__main__":
+    main()

+ 393 - 0
test_chat.py

@@ -0,0 +1,393 @@
+import sys
+import time
+import os
+from PIL import Image
+import requests
+from  prompt import *
+from llm import *
+import json
+from conf import *
+import re
+MAX_RETRIES = 5
+MAX_HISTORY = 20
+MAX_CHAR_LIMIT = 400
+MIN_CHAR_LIMIT = 150 # 假设的最小长度限制
+
+history_list=[]
+
+plugins = {
+#     "ch_en_selling_points":get_ch_en_selling_points,
+#     "en_ch_selling_points":get_en_ch_selling_points,
+    "ch_en_selling_title":get_ch_en_selling_title,
+#     "en_ch_selling_points_his":get_en_ch_selling_points_his,
+#     "TextControl_his":TextControl_his,
+#     "TextControl":TextControl
+}
+
+
+
+
+
+def contains_chinese(text):
+    pattern = re.compile(r'[\u4e00-\u9fa5]')
+    return bool(pattern.search(text))
+
+def format_history(strings, indent="    "):
+    result = ""
+    for i, string in enumerate(strings, start=1):
+        # 拼接序号、缩进和字符串,并添加换行符
+        result += f"{indent}{i}. {string}\n"
+    return result
+
+def get_history():
+    """获取格式化的历史记录(用于原始prompt)"""
+    global history_list
+    if len(history_list)==0:
+        history=''
+    else:
+        history=format_history(history_list)
+    return history
+
+
+def add_history(input,max_num=20):
+    global history_list
+    text = re.split(r'[,\.\!\?\;\:]+', input)
+    text=text[0].strip()
+    history_list.insert(0, text)
+    if len(history_list)>max_num:
+        history_list=history_list[:max_num]
+
+
+def generate_text(plm_info,img,graphic_label=None,plat="ali",model_name="mm_qwen"):
+
+    history_string=get_history()
+    # print(his)
+    if graphic_label:
+        tags_sen=",".join(graphic_label)
+        plm_info+="\n'      '以下是该衣服的关键点:"+tags_sen
+    if plat=="ali":
+        key=ali_ky
+        model=ali_model[model_name]
+    else:
+        key=doubao_ky
+        model=doubao_model[model_name]
+
+    llm=llm_request(*key,model)
+    en,kw='',''
+    result_json = None
+    for attempt in range(MAX_RETRIES):
+        # --- 构造Prompt ---
+        if attempt == 0:
+            # 第一次尝试:使用您的主Prompt
+            usrp = user_prompt.format(basic_info_string=plm_info,history_string=history_string)
+        else:
+            # 后续尝试:使用“修正Prompt”
+            usrp = get_refinement_prompt(plm_info, history_string, result_json)
+            
+        print(f"--- 尝试 {attempt + 1} ---")
+        # print(prompt) # Debug: 打印Prompt
+
+        response_text = llm.llm_mm_request(usrp,img,sys_text=system_prompt)
+        try:
+     
+            is_valid, validation_error, result_json = validate_response(response_text)
+            
+            if is_valid:
+                # 成功!
+                print("生成成功!")
+                en,kw=result_json['en'],result_json['kw']
+                add_history(en)
+                break
+            
+            else:
+                # 失败,记录错误,循环将继续
+                print(f"尝试 {attempt + 1} 失败: {validation_error}")
+                # result_json 已经包含了失败的文本和错误信息,将用于下一次修正
+                continue
+                
+        except Exception as e:
+            # API调用本身失败
+            print(f"API 调用失败: {e}")
+            result_json = {"error": "API_FAILURE", "raw_response": str(e)}
+            continue   
+ 
+    if result_json and result_json.get("error") == "EN_TOO_LONG":
+        # 如果是因为超长而失败,且 raw_response 有效
+        try:
+            failed_data = json.loads(result_json.get("raw_response", "{}"))
+            long_en_text = failed_data.get("en")
+            
+            if long_en_text and len(long_en_text) > MAX_CHAR_LIMIT:
+                en = smart_truncate_by_sentence(long_en_text, max_chars=MAX_CHAR_LIMIT)
+                kw = failed_data.get("kw", '')
+                
+                print("执行智能截断成功,返回修正后的文案。")
+                
+                add_history(en)
+        
+        except (json.JSONDecodeError, KeyError, TypeError):
+            # 无法截断,进入最终错误
+            pass
+
+    if isinstance(kw,str):
+        kw = [item.strip() for item in kw.split('.') if item.strip()]
+    return en,kw
+
+
+def validate_response(response_text):
+    """验证模型的输出是否符合所有规则"""
+    try:
+        # 规则1: 是否是有效JSON?
+        data = json.loads(response_text.strip())
+    except json.JSONDecodeError:
+        return False, "INVALID_JSON", {"error": "INVALID_JSON", "raw_response": response_text}
+
+    # 规则2: 键是否齐全?
+    if not all(k in data for k in ["en", "ch", "kw"]):
+        return False, "MISSING_KEYS", {"error": "MISSING_KEYS", "raw_response": json.dumps(data)}
+        
+    en_text = data.get("en", "")
+    
+    # 规则3: 长度是否超标? 
+    if len(en_text) > MAX_CHAR_LIMIT:
+        return False, "EN_TOO_LONG", {"error": "EN_TOO_LONG", "raw_response": json.dumps(data)}
+        
+    # 规则4: 长度是否太短?
+    if len(en_text) < MIN_CHAR_LIMIT:
+        return False, "EN_TOO_SHORT", {"error": "EN_TOO_SHORT", "raw_response": json.dumps(data)}
+    if contains_chinese(en_text):
+        return False, "EN_CONTAINS_CHINESE", {"error": "EN_CONTAINS_CHINESE", "raw_response": json.dumps(data)}
+    return True, "SUCCESS", data
+
+def smart_truncate_by_sentence(text, max_chars=MAX_CHAR_LIMIT):
+    if len(text) <= max_chars:
+        return text
+    sentence_endings = re.compile(r'[.!?](?:\s+|$)') 
+    best_truncate_point = -1 
+
+    for match in sentence_endings.finditer(text):
+        end_position = match.end()
+        
+        if end_position <= max_chars:
+            best_truncate_point = end_position
+        else:
+            break
+
+    if best_truncate_point > 0:
+        truncated_text = text[:best_truncate_point].strip()
+        if not truncated_text.endswith(('.', '!', '?')):
+            truncated_text += '.'
+        return truncated_text.strip()
+    else:
+        return text[:max_chars-3].strip() + '...'
+
+
+def get_refinement_prompt(basic_info_string, history_string, failed_result):
+    """
+    根据上一次的失败原因,生成一个“引导式修正”的Prompt
+    """
+    failure_reason = failed_result.get("error", "UNKNOWN")
+    raw_response = failed_result.get("raw_response", "")
+
+    feedback = ""
+    # 尝试提取上次失败的文案
+    last_text_en = ""
+    try:
+        if raw_response:
+            last_text_en = json.loads(raw_response).get("en", "")
+    except json.JSONDecodeError:
+        pass # 无法解析,last_text_en 保持空
+
+    if failure_reason == "INVALID_JSON":
+        feedback = f"你上次的输出不是一个有效的JSON。请【严格】按照JSON格式输出。你上次的错误输出是:\n{raw_response}"
+    elif failure_reason == "EN_TOO_LONG":
+        feedback = f"""
+        你上次生成的 "en" 描述【超过了{MAX_CHAR_LIMIT}个字符】!
+        【你生成的超长原文】:\n{last_text_en}
+        【修正任务】: 请【大幅精简】上述原文,保留核心卖点,使其长度【绝对】在{MIN_CHAR_LIMIT}-{MAX_CHAR_LIMIT}字符以内。
+        """
+    elif failure_reason == "EN_TOO_SHORT":
+        feedback = f"""
+        你上次生成的 "en" 描述太短了(小于{MIN_CHAR_LIMIT}字符)。
+        【你生成的原文】:\n{last_text_en}
+        【修正任务】: 请在原文案基础上,围绕核心卖点再丰富一些细节,使其达到{MIN_CHAR_LIMIT}-{MAX_CHAR_LIMIT}字符。
+        """
+    elif failure_reason == "MISSING_KEYS":
+        feedback = f"你上次输出的JSON缺少 'en', 'ch' 或 'kw' 键。请确保三者齐全。"
+    elif failure_reason == "TOO_SIMILAR":
+        feedback = "你上次生成的文案与历史记录太相似了。请换一个角度(比如从'材质'或'穿搭场景')重新构思,字数保持在要求的范围内。"
+    elif failure_reason == "EN_CONTAINS_CHINESE":
+        feedback = f"""
+        你上次生成的 "en" 描述中包含了中文汉字(例如:{last_text_en})。
+        【修正任务】: "en" 字段【必须是纯英文】,【绝对禁止】出现任何中文字符。请严格修正并重新输出。
+        """
+    else:
+        feedback = "你上次的生成失败了。请重新严格按照所有规则生成一次。"
+
+    # 修正Prompt模板
+    refinement_prompt = f"""## 角色
+你是一个文案修正专家。
+
+## 原始任务
+根据以下信息和随消息传入的图片生成文案:{basic_info_string}
+
+## 上次失败的反馈 (你必须修正!)
+{feedback}
+
+## 核心规则 (必须再次遵守)
+1.  【必须】输出严格的JSON格式。
+2.  "en" 描述【必须严格在{MAX_CHAR_LIMIT}字符以内】。
+3.  【不要】使用历史开篇:\n{history_string}
+
+## 最终输出
+请直接输出修正后的、严格符合要求的JSON字典。
+"""
+    return refinement_prompt
+
+
+
+
+def gen_title(info,tags=None,referencr_title=None,method="ch_en_selling_title",plat="ali",model_name="text_dsv3"):
+    
+    if tags:
+        tags_sen=",".join(tags)
+        info="\n'      '以下是该衣服的关键点:"+tags_sen
+    if referencr_title:
+        info="\n'      '请以这条标题样例的结构作为借鉴来写这条标题:"+referencr_title
+    sysp,usrp = plugins[method](info)
+    if plat=="ali":
+        key=ali_ky
+        model=ali_model[model_name]
+    else:
+        key=doubao_ky
+        model=doubao_model[model_name]
+
+    llm=llm_request(*key,model)
+    res=llm.llm_text_request(usrp,sysp)
+    res_dict = json.loads(res)
+    return {"title":res_dict["en_tile"]}
+
+if __name__ == "__main__":
+    
+    # inf="'Meet your new best friend in fashion—this unisex sweater that whispers comfort and style. Crafted from premium cotton, it feels like a gentle hug on your skin. The heart embroidery adds a touch of whimsy, making you the star of any casual outing. Perfect for layering or wearing solo, this soft companion keeps you cozy all season long."
+    # print(gen_title(inf))
+    # id_image,id_price, id_color, id_ingredient, id_selling_point, id_details=search_json_files("1A6H4K7V0")
+    # id_image=id_image[2:]
+    # id_image=os.path.join("/data/data/luosy/project/sku_search",id_image)
+    id_image="https://img2.goelia.com.au/prod/product/1ENC6E220/material/main/Shopify/-1/72736752b0ad405382d5ed277dabc660.jpg"
+    graphic_label=['-100% Merino wool', '-With pockets', '-H-line fit']
+    plm_info='1、手工流苏边设计 \xa0 2、贴袋设计 \xa0 3、金属纽扣'
+    # print(id_details,id_image)
+    for _ in range(3):
+        result=generate_text(plm_info,id_image,graphic_label)
+        # result=gen_title("This maxi dress features unparalleled comfort and a unique texture with its <b>tencel blend fabric</b>. The square neckline and smocked bodice create a flattering silhouette, while the layered skirt adds romantic flair. <b>Side pockets and an included scarf scrunchie</b> enhance both style and functionality, elevating its versatility for everyday wear and beyond.")
+        print(result)
+    # from tqdm import tqdm
+    # def image_to_base64(image):
+    #     # 将Image对象转换为BytesIO对象
+    #     image_io = io.BytesIO()
+    #     image.save(image_io, format='PNG')
+    #     image_io.seek(0)
+
+    #     # 使用base64编码
+    #     image_base64 = base64.b64encode(image_io.read()).decode('utf-8')
+
+    #     return image_base64
+    
+    # def create_html_with_base64_images(root, output_html):
+    #     with open(output_html, 'w', encoding='utf-8') as html_file:
+    #         html_file.write('<!DOCTYPE html>\n<html>\n<head>\n<title>Images in Table</title>\n')
+    #         html_file.write('<meta charset="UTF-8">\n')  # 添加字符编码声明
+    #         html_file.write('<style>\n')
+    #         html_file.write('table {\nborder-collapse: collapse;\nwidth: 100%;\n}\n')
+    #         html_file.write('table, th, td {\nborder: 1px solid black;\n}\n')
+    #         html_file.write('img {\nmax-width: 100%;\nheight: auto;\ndisplay: block;\nmargin-left: auto;\nmargin-right: auto;\n}\n')
+    #         html_file.write('</style>\n')
+    #         html_file.write('</head>\n<body>\n')
+    #         html_file.write('<table>\n')
+    #         html_file.write('<tr>\n')
+    #         html_file.write('<th>输入的图片</th>\n')  # 第一列:索引
+    #         html_file.write('<th>输入的描述</th>\n')  # 第二列:标题
+    #         html_file.write('<th>输出的商品详情</th>\n')  # 第二列:标题
+    #         html_file.write('<th>输出的商品详情(翻译)</th>\n')  # 第三列:图表
+    #         html_file.write('<th>输出的卖点</th>\n')  # 第三列:图表
+    #         # for i in range(1, 100):  # 添加序号列1到13
+    #         #     html_file.write(f'<th>{i}</th>\n')
+    #         html_file.write('</tr>\n')
+
+    #         for file in tqdm(os.listdir(root)[:100], desc="Processing", unit="iter"):
+    #             if '.ipynb_checkpoints' in file:
+    #                 continue
+    #             file_path = os.path.join(root, file)
+    #             with open(file_path, 'r') as f:
+    #                 data = json.load(f)
+    #             if data and "商品图像" in data.keys():
+    #                 id_image,id_details=data["商品图像"][2:],  data["商品细节"]
+    #             else:
+    #                 continue
+    #             id_image=os.path.join("/data/data/luosy/project/sku_search",id_image)
+                
+    #             img_base64 = image_to_base64(Image.open(id_image))
+
+    #             ch,en,kw=generate_text_new(id_details,id_image)
+    #             html_file.write('<tr>\n')
+    #             # html_file.write(f'<td>{index+1}</td>\n')  # 添加序号
+                
+    # #             html_file.write('<td>\n')
+    # #             html_file.write(f'<img src="data:image/png;base64,{frame_title_img}" alt="Image">\n')
+    # #             html_file.write('</td>\n')
+    #             html_file.write('<td>\n')
+    #             html_file.write(f'<img src="data:image/png;base64,{img_base64}" alt="Image">\n')
+    #             html_file.write('</td>\n')
+    #             html_file.write(f'<td>{id_details}</td>\n')  # 添加序号
+    #             html_file.write(f'<td>{en}</td>\n')  # 添加序号
+    #             html_file.write(f'<td>{ch}</td>\n')  # 添加序号
+    #             html_file.write(f'<td>{kw}</td>\n')  # 添加序号
+    #             # html_file.write('</td>\n')
+    #             # for img in image_data:
+    #             #     html_file.write('<td>\n')
+    #             #     html_file.write(f'<img src="data:image/jpeg;base64,{img}" alt="Image" style="max-width: 100px; max-height: 100px; margin: 5px;">\n')
+    #             #     html_file.write('</td>\n')
+    #             # html_file.write('</td>\n')
+    #             html_file.write('</tr>\n')
+
+    #         html_file.write('</table>\n')
+    #         html_file.write('</body>\n</html>')
+    # root='/data/data/luosy/project/sku_search/database/meta'
+    # create_html_with_base64_images(root, "out——qw_v6.html")
+
+
+
+    # app.run(host="0.0.0.0",port=2222,debug=True)
+
+    # print(gen_title(info= "This sweatshirt is a wardrobe essential with its simple yet stylish design and 3D heart pattern that adds a fun visual pop. <b>The unisex design is perfect for couples</b>, and it pairs effortlessly with jeans, cargo pants, or a pleated skirt. Ideal for school, work, or casual outings, it's comfortable and trendy all day long!"))
+#     from PIL import Image
+#     img1=Image.open("/data/data/luosy/project/sku_search/temp_img/企业微信截图_17372766091671.png")
+
+#     ch_sen,en_sen,key_point,id_image,id_price, id_color, id_ingredient, id_selling_point, id_details=generate_text("",img1,"""-With elastic waistband
+# -With hairband
+# -X-line fit
+# 1.腰部橡筋 2.袖子橡
+# 筋 3.前中绳子可调
+# 节大小""")
+#     print(len(en_sen),end=" ")
+#     print(ch_sen,en_sen,key_point)
+#     ###############################
+#     img2=Image.open("/data/data/luosy/project/sku_search/temp_img/企业微信截图_17389065463149[1](1).png")
+
+#     ch_sen,en_sen,key_point,id_image,id_price, id_color, id_ingredient, id_selling_point, id_details=generate_text("",img2,"""-Washable wool
+# -Unisex
+# -With silver threads
+# 1.后中开衩;2.双扣可调节袖袢;3.暗门筒设计,天然果实扣;4.可水洗羊毛含银葱人字纹面料;5.里面左右两侧均有内袋,左侧最外层内袋是手机袋,防丢失""")
+#     print(len(en_sen),end=" ")
+#     print(ch_sen,en_sen,key_point)
+#     ###############################
+#     img3=Image.open("/data/data/luosy/project/sku_search/temp_img/企业微信截图_17392379937637.png")
+
+#     ch_sen,en_sen,key_point,id_image,id_price, id_color, id_ingredient, id_selling_point, id_details=generate_text("",img3,"""-Acetate
+# -With pockets
+# -Workwear
+# 1.描述二醋酸面料:2.扣子为镶钻布包扣;3.半裙后腰包橡筋;4.半裙有
+# 侧插袋;5.半裙有侧开隐形拉链,这是两件套套装""")
+#     print(len(en_sen),end=" ")
+#     print(ch_sen,en_sen,key_point)

+ 229 - 0
usage_guide.md

@@ -0,0 +1,229 @@
+# 服装卖点文案生成系统优化指南
+
+## 问题分析
+
+你原来的系统存在以下问题:
+1. **字数经常超过500字符限制**
+2. **重试机制简单,效果不佳**
+3. **全局历史记录处理不当,可能干扰生成**
+4. **缺乏后处理和验证机制**
+5. **没有备用生成策略**
+
+## 解决方案总览
+
+我们提供了**多层次的优化方案**:
+
+### 🎯 方案1:强化Prompt设计
+- **文件**: `improved_prompt.py`
+- **特点**: 更严格的字数控制,分层次的内容要求
+- **字数限制**: 280字符(比原来更严格)
+- **结构化**: 明确的字符数分配策略
+
+### 🧠 方案2:智能历史记录管理
+- **文件**: `smart_history_manager.py`
+- **特点**: 
+  - 根据服装类别筛选相关历史记录
+  - 避免重复的开头模式
+  - 智能相关性评分
+  - 只使用最相关的历史记录
+
+### 🚀 方案3:增强生成器
+- **文件**: `enhanced_generator.py`
+- **特点**:
+  - 多种生成策略(渐进式、模板式、精简式)
+  - 严格的字数验证
+  - 智能文本截断
+  - 备用生成方案
+
+### 🔧 方案4:集成到现有系统
+- **文件**: `chat.py` (已修改)
+- **特点**: 向后兼容,渐进式升级
+
+## 使用方法
+
+### 基础使用(推荐)
+
+```python
+# 在你的 chat.py 中,generate_text 函数已经自动集成了所有优化
+# 无需修改调用方式,直接使用即可
+
+en, kw = generate_text(
+    plm_info="商品信息",
+    img="图片URL", 
+    graphic_label=["标签1", "标签2"]
+)
+```
+
+### 高级使用(手动控制)
+
+```python
+from enhanced_generator import EnhancedTextGenerator
+from llm import llm_request
+
+# 创建LLM客户端
+llm = llm_request(api_key, base_url, model)
+
+# 获取全局历史记录
+global_history = get_global_history()
+
+# 创建增强生成器
+generator = EnhancedTextGenerator(
+    llm_client=llm,
+    max_chars=280,  # 字数限制
+    max_retries=5,  # 重试次数
+    global_history=global_history
+)
+
+# 生成文案
+en, kw = generator.generate_with_strategy(
+    plm_info="商品信息",
+    img="图片URL",
+    graphic_label=["标签"],
+    strategy="progressive"  # 可选: "progressive", "template", "concise"
+)
+```
+
+## 生成策略说明
+
+### 1. Progressive Strategy (渐进式) - 推荐
+- 从宽松到严格的字数要求
+- 智能历史记录处理
+- 多次尝试,逐步优化
+
+### 2. Template Strategy (模板式)
+- 使用预定义模板
+- 适合特定风格需求
+- 模板类型:casual, luxury, functional
+
+### 3. Concise Strategy (精简式)
+- 直接使用最严格限制
+- 适合对字数要求极严格的场景
+
+## 核心改进特性
+
+### ✅ 严格字数控制
+- **硬限制**: 280字符(包括标点和空格)
+- **智能截断**: 在句号、逗号、空格处截断,保持语义完整
+- **多层验证**: 生成前、生成中、生成后三重验证
+
+### ✅ 智能历史记录处理
+- **类别匹配**: 只参考相同类别的服装历史记录
+- **避免重复**: 自动识别和避免重复的开头模式
+- **相关性评分**: 根据面料、风格、情感词汇计算相关性
+
+### ✅ 增强重试机制
+- **渐进式prompt**: 每次重试使用更严格的prompt
+- **多策略切换**: 失败时自动切换生成策略
+- **备用方案**: 所有策略失败时使用规则生成
+
+### ✅ 后处理优化
+- **格式标准化**: 确保JSON格式正确
+- **关键词处理**: 自动格式化关键词列表
+- **中英文检测**: 自动翻译混合文本
+
+## 配置参数
+
+### 字数控制
+```python
+max_chars = 280  # 最大字符数(推荐280)
+min_chars = 50   # 最小字符数(避免过短)
+```
+
+### 历史记录
+```python
+max_history = 50        # 最大历史记录数
+max_relevant = 8        # 最大相关历史记录数
+max_avoid_patterns = 15 # 最大避免模式数
+```
+
+### 重试策略
+```python
+max_retries = 5  # 最大重试次数
+char_limits = [300, 280, 260, 240, 220]  # 渐进式字数限制
+```
+
+## 监控和调试
+
+### 日志输出
+系统会输出详细的生成过程日志:
+```
+✅ 增强生成成功: 275 字符
+⚠️ 第2次尝试:文案过长(320字符),进行截断
+🔄 所有尝试失败,生成备用文案
+```
+
+### 性能指标
+- **成功率**: 应该达到95%以上
+- **字数合规率**: 应该达到100%
+- **平均重试次数**: 应该在2次以内
+
+## 故障排除
+
+### 问题1:仍然超过字数限制
+**原因**: prompt设计或截断逻辑问题
+**解决**: 检查 `smart_truncate` 函数,调整 `max_chars` 参数
+
+### 问题2:生成质量下降
+**原因**: 字数限制过严格
+**解决**: 适当放宽 `max_chars` 到300-320
+
+### 问题3:历史记录干扰
+**原因**: 历史记录过多或不相关
+**解决**: 调整 `max_relevant` 参数,清理历史记录
+
+### 问题4:生成失败率高
+**原因**: 网络问题或模型问题
+**解决**: 检查API连接,增加重试次数
+
+## 最佳实践
+
+### 1. 字数设置
+- **电商平台**: 280字符(推荐)
+- **社交媒体**: 200字符
+- **详细描述**: 350字符
+
+### 2. 历史记录管理
+- **定期清理**: 保持50条以内
+- **分类存储**: 按服装类别分别存储
+- **质量筛选**: 只保留高质量的历史记录
+
+### 3. 监控优化
+- **定期检查**: 每周检查生成质量
+- **A/B测试**: 对比不同策略的效果
+- **用户反馈**: 收集用户对文案质量的反馈
+
+## 升级路径
+
+### 阶段1:基础集成(当前)
+- 使用修改后的 `chat.py`
+- 自动字数控制和截断
+
+### 阶段2:智能优化
+- 启用智能历史记录管理
+- 使用增强生成器
+
+### 阶段3:深度定制
+- 根据业务需求调整模板
+- 添加特定行业词汇
+- 优化生成策略
+
+## 总结
+
+这套优化方案通过**多层次、多策略**的方法,有效解决了:
+- ✅ 字数超限问题(通过严格验证和智能截断)
+- ✅ 生成失败问题(通过多策略重试和备用方案)
+- ✅ 历史记录干扰问题(通过智能筛选和相关性匹配)
+- ✅ 质量不稳定问题(通过结构化prompt和后处理)
+
+**预期效果**:
+- 字数合规率:100%
+- 生成成功率:95%+
+- 文案质量:显著提升
+- 重复率:大幅降低
+
+
+
+
+
+
+

BIN
企业微信截图_17372766091671.png


BIN
企业微信截图_17439887354277.png


BIN
扫描全能王 2025-03-12 15.12_01(1).jpg


Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor