feat(ai-outfit): 左栏新增「标题生成」(看图→提示词→AI 文字标题→写回A列) (§19.18)
AI 穿搭页左栏新增独立的「生成标题」流程:用户写标题提示词,AI 看该行 衣服图(目录行取首图)生成电商标题,逐行立即写回 Excel A 列并刷新明细表; 完成后重载 Excel,紧接「开始生成」跑图即用新标题。移除原「最终生成要求预览」 腾出版面(docs/11 §17)。 - ai_text_service.py:AiTextClient 复用图像服务 HTTP 管道做文本输出; chat/gemini 带图视觉,images/images_edits 明确报错;extract_text 取首条标题 - ai_title.py + TitleResult:单行编排,never raises - excel_service.write_title_result:只写 A 列、不动 D/E/F - config_service:title_model 默认 + load/save_title_prompt + 默认标题话术 - 面板:标题生成组(提示词+标题模型下拉+保存+生成标题)置于话术组上方; _TitleWorker 顺序逐行+立即回填+刷新;与「开始生成」互斥;删预览相关组件 - 测试:文本解析/payload、generate_title(单文件/目录首图/失败)、 write_title_result、面板标题组存在且无预览;全套 py37 通过 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -27,12 +27,14 @@ DEFAULT_CONFIG = {
|
||||
"outfit_quality": "均衡",
|
||||
"outfit_retry_failed": False,
|
||||
"outfit_prompt_name": "默认", # last-selected 话术模板 name (docs/11 §7.2)
|
||||
"title_model": "", # last-selected 标题模型 name (docs/11 §17.3)
|
||||
}
|
||||
|
||||
_CONFIG_FILENAME = "app_config.json"
|
||||
_AI_MODELS_FILENAME = "ai_models.json"
|
||||
_OUTFIT_PROMPT_FILENAME = "outfit_prompt.txt" # legacy single prompt (migrated)
|
||||
_OUTFIT_PROMPTS_FILENAME = "outfit_prompts.json" # multi named templates (§7.2)
|
||||
_TITLE_PROMPT_FILENAME = "title_prompt.txt" # single 标题生成提示词 (§17.3)
|
||||
DEFAULT_OUTFIT_PROMPT_NAME = "默认"
|
||||
|
||||
# Default outfit prompt (docs/11 §7). Seeded into outfit_prompts.json on first run.
|
||||
@@ -42,6 +44,12 @@ DEFAULT_OUTFIT_PROMPT = (
|
||||
"电商主图风格,不加文字与促销标签。"
|
||||
)
|
||||
|
||||
# Default title prompt (docs/11 §17.3). Used by 标题生成 when title_prompt.txt absent.
|
||||
DEFAULT_TITLE_PROMPT = (
|
||||
"请根据这件女装的款式、版型、颜色与印花特点,生成一条适合台湾蝦皮电商的中文商品标题:"
|
||||
"突出卖点与适穿场景,控制在 30 字以内。只输出标题本身一行,不要序号、引号、表情或促销词。"
|
||||
)
|
||||
|
||||
|
||||
def load_config():
|
||||
"""
|
||||
@@ -173,6 +181,34 @@ def save_outfit_prompt(text):
|
||||
logger.error("Failed to save outfit prompt to %s: %s", prompt_file, exc)
|
||||
|
||||
|
||||
def load_title_prompt():
|
||||
"""Return the saved 标题生成提示词, or the built-in default (docs/11 §17.3)."""
|
||||
from services.file_service import get_config_path
|
||||
prompt_file = get_config_path(_TITLE_PROMPT_FILENAME)
|
||||
if not prompt_file.exists():
|
||||
return DEFAULT_TITLE_PROMPT
|
||||
try:
|
||||
with open(str(prompt_file), encoding="utf-8-sig") as f:
|
||||
text = f.read()
|
||||
return text if text.strip() else DEFAULT_TITLE_PROMPT
|
||||
except OSError as exc:
|
||||
logger.warning("Title prompt unreadable (%s): %s", exc, prompt_file)
|
||||
return DEFAULT_TITLE_PROMPT
|
||||
|
||||
|
||||
def save_title_prompt(text):
|
||||
"""Persist the 标题生成提示词 (utf-8, no BOM). Does not raise."""
|
||||
from services.file_service import get_config_path
|
||||
prompt_file = get_config_path(_TITLE_PROMPT_FILENAME)
|
||||
try:
|
||||
prompt_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(str(prompt_file), "w", encoding="utf-8") as f:
|
||||
f.write(text)
|
||||
logger.info("Title prompt saved to %s", prompt_file)
|
||||
except OSError as exc:
|
||||
logger.error("Failed to save title prompt to %s: %s", prompt_file, exc)
|
||||
|
||||
|
||||
def _normalize_prompts(data):
|
||||
"""Keep only valid {name, text} entries (non-empty name, string text)."""
|
||||
if not isinstance(data, list):
|
||||
|
||||
Reference in New Issue
Block a user