155 lines
6.4 KiB
Python
155 lines
6.4 KiB
Python
"""提示词公共层:与模型家族无关的商品上下文、风格模板、图类型名与文案组件。
|
||
|
||
各家族模块(alibaba / doubao / gpt / google)只负责"如何对模型说话",
|
||
商品信息提炼与风格体系统一在这里维护,避免多处漂移。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
|
||
# ── 风格模板(与插件端 STYLE_SET_OPTIONS 对应;提示词可被用户在插件里改写覆盖)───
|
||
|
||
STYLE_SETS: dict[int, dict] = {
|
||
1: {
|
||
"name": "北欧极简",
|
||
"tone": "北欧极简风:浅灰或米白背景,柔和漫射光,低饱和色调,画面留白充足,构图克制干净",
|
||
"bg": "",
|
||
},
|
||
2: {
|
||
"name": "清新明亮",
|
||
"tone": "清新明亮风:明亮的白色到浅蓝渐变背景,高调光线,色彩明快通透,整体轻盈干净",
|
||
"bg": "",
|
||
},
|
||
3: {
|
||
"name": "高级感深色",
|
||
"tone": "高级质感风:深灰或炭黑背景,戏剧性侧光打光,突出商品材质与光泽,沉稳高级",
|
||
"bg": "",
|
||
},
|
||
4: {
|
||
"name": "暖调生活",
|
||
"tone": "温暖生活风:暖米色背景,暖色灯光氛围,温馨的家居质感,亲和力强",
|
||
"bg": "",
|
||
},
|
||
5: {
|
||
"name": "纯净棚拍",
|
||
"tone": "标准电商棚拍:纯色浅背景,均匀的正面柔光,无杂物干扰,商品居中突出",
|
||
"bg": "",
|
||
},
|
||
}
|
||
|
||
# ── 图类型中文名(导出文件名用)───────────────────────────────────────────
|
||
|
||
TYPE_NAMES_ZH: dict[str, str] = {
|
||
"white_bg": "白底主图",
|
||
"key_features": "核心卖点图",
|
||
"selling_pt": "卖点图",
|
||
"material": "材质图",
|
||
"lifestyle": "场景展示图",
|
||
"multi_scene": "多场景拼图",
|
||
"ecommerce_detail": "电商详情图",
|
||
"size_chart": "尺寸标注图",
|
||
"sku_collection": "SKU合集图",
|
||
"custom": "创意图",
|
||
}
|
||
|
||
# ── 图内营销文案渲染规范(各家族共用;语言由平台决定)──────────────────────
|
||
|
||
TEXT_RENDER = {
|
||
"zh": (
|
||
"Render concise Chinese marketing text inside the image: main headline max 8 Chinese characters, "
|
||
"sub-lines max 12 characters each, font is modern clean sans-serif (Source Han Sans style), "
|
||
"high legibility, tasteful typography layout, colors harmonized with the composition. "
|
||
"No spelling errors, no garbled characters."
|
||
),
|
||
"en": (
|
||
"Render concise English marketing text inside the image: headline max 5 words, "
|
||
"sub-lines max 8 words each, Helvetica Neue style sans-serif, high legibility, "
|
||
"tasteful typography layout, colors harmonized with the composition. No spelling errors."
|
||
),
|
||
"ru": (
|
||
"Render concise Russian marketing text inside the image: headline max 4 words, "
|
||
"sub-lines max 6 words each, modern clean sans-serif (Inter / PT Sans style), "
|
||
"proper Cyrillic typography, high legibility, tasteful layout, colors harmonized with the composition. "
|
||
"No spelling errors, no mixed latin/cyrillic gibberish."
|
||
),
|
||
}
|
||
|
||
|
||
def resolve_style(style_set: int, style_prompt: str | None = None) -> dict:
|
||
"""用户改写的风格提示词整体覆盖内置模板(tone/bg 整体替换)。"""
|
||
if style_prompt and style_prompt.strip():
|
||
return {"name": "custom", "tone": style_prompt.strip(), "bg": ""}
|
||
return STYLE_SETS.get(style_set, STYLE_SETS[1])
|
||
|
||
|
||
def requirements_block(requirements: str | None) -> str:
|
||
"""用户强制要求块:最高优先级、置于提示词最前、覆盖冲突指令(原文保留不翻译)。"""
|
||
if requirements and requirements.strip():
|
||
return (
|
||
"STRICT REQUIREMENTS (highest priority, must be followed exactly, "
|
||
"override any conflicting instruction): "
|
||
+ requirements.strip().rstrip(".")
|
||
+ "."
|
||
)
|
||
return ""
|
||
|
||
|
||
# ── 商品上下文提炼 ────────────────────────────────────────────────────────
|
||
|
||
def _shorten(text: str, n: int) -> str:
|
||
text = re.sub(r"\s+", " ", (text or "")).strip()
|
||
return text[:n]
|
||
|
||
def _clean_title(title: str) -> str:
|
||
"""去掉常见堆砌词,让标题更可读。"""
|
||
t = _shorten(title, 60)
|
||
return re.sub(r"[【【】】\\[\\]|/]", " ", t).strip()
|
||
|
||
def build_context(raw: dict, fallback_name: str = "", fallback_desc: str = "") -> dict:
|
||
"""从采集数据提炼生图上下文:标题、描述行、卖点列表、参数行。
|
||
|
||
raw: {title, desc, price, params: [{key, value}], sellingPoints}
|
||
"""
|
||
title = _clean_title(raw.get("title") or fallback_name or "product")
|
||
desc = _shorten(raw.get("desc") or fallback_desc or "", 200)
|
||
|
||
# 卖点:优先显式卖点文本;否则从参数表里挑短而有信息量的键值对
|
||
selling_points: list[dict] = []
|
||
sp_text = raw.get("sellingPoints") or ""
|
||
if sp_text:
|
||
for chunk in re.split(r"[;;\n·]+|(?<!\d)\.(?!\d)", sp_text):
|
||
c = _shorten(chunk, 20)
|
||
if c and len(selling_points) < 5:
|
||
selling_points.append({"zh": c, "en": c})
|
||
if not selling_points:
|
||
for p in (raw.get("params") or [])[:12]:
|
||
k, v = _shorten(p.get("key", ""), 10), _shorten(str(p.get("value", "")), 16)
|
||
if k and v and k.lower() not in {"货号", "sku", "isbn", "上架时间"}:
|
||
selling_points.append({"zh": f"{k} {v}", "en": f"{k} {v}"})
|
||
if len(selling_points) >= 5:
|
||
break
|
||
|
||
params_line = "; ".join(
|
||
f"{p.get('key')}: {p.get('value')}" for p in (raw.get("params") or [])[:8]
|
||
)
|
||
return {
|
||
"title": title,
|
||
"title_en": title, # 采集源多为中文标题,英文场景直接用原词避免乱翻译
|
||
"desc": desc,
|
||
"selling_points": selling_points[:3],
|
||
"params_line": params_line,
|
||
"price": raw.get("price") or "",
|
||
}
|
||
|
||
def selling_point_lines(ctx: dict, lang: str, max_n: int = 3) -> str:
|
||
"""卖点列表 → 单行文案(图内 callout/标题用),无卖点返回空串。"""
|
||
sps = ctx["selling_points"][:max_n]
|
||
if not sps:
|
||
return ""
|
||
key = "zh" if lang == "zh" else "en"
|
||
return "; ".join(s[key] for s in sps if s.get(key))
|
||
|
||
|
||
def type_name(type_id: str) -> str:
|
||
return TYPE_NAMES_ZH.get(type_id, type_id)
|