feat: 修改模型提示词
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
"""套图提示词引擎:按模型家族分发,各家族独立封装。
|
||||
|
||||
不同家族的生图语义差异极大,共用一套提示词会导致语义错配
|
||||
(gpt-image-2 按文字重造商品即由此而来),故按家族各自成册:
|
||||
|
||||
alibaba 通义 wan*/qwen*(DashScope)—— 主体参考语义
|
||||
doubao 豆包 Seedream(火山方舟)—— 主体参考语义,与通义共用装配
|
||||
gpt gpt-image-2 / gpt-image-2-vip(RightAPI)—— /v1/images/edits 编辑语义
|
||||
google nano-banana 系列(RightAPI)—— 原生主体保持语义
|
||||
|
||||
路由规则:provider 为主;rightapi 内再按模型名细分 gpt / google。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from . import alibaba, doubao, google, gpt
|
||||
from .common import build_context, type_name
|
||||
|
||||
_MODULE_BY_FAMILY = {
|
||||
"alibaba": alibaba,
|
||||
"doubao": doubao,
|
||||
"gpt": gpt,
|
||||
"google": google,
|
||||
}
|
||||
|
||||
|
||||
def prompt_family(provider: str, model: str | None) -> str:
|
||||
"""(provider, model) → 提示词家族名。"""
|
||||
if provider == "rightapi":
|
||||
if (model or "").lower().startswith("nano-banana"):
|
||||
return "google"
|
||||
return "gpt" # gpt-image-* 及未知中转模型默认按 edits 语义处理
|
||||
if provider == "tongyi":
|
||||
return "alibaba"
|
||||
return "doubao" # doubao 及默认 provider
|
||||
|
||||
|
||||
def build_prompt(provider: str, model: str | None, type_id: str, ctx: dict, style_set: int,
|
||||
lang: str, extra: dict | None = None, style_prompt: str | None = None,
|
||||
requirements: str | None = None) -> str:
|
||||
"""按模型家族构造指定图类型的完整生图 prompt。参数含义见各家族 build_prompt。"""
|
||||
module = _MODULE_BY_FAMILY[prompt_family(provider, model)]
|
||||
return module.build_prompt(
|
||||
type_id, ctx, style_set, lang,
|
||||
extra=extra, style_prompt=style_prompt, requirements=requirements,
|
||||
)
|
||||
@@ -0,0 +1,169 @@
|
||||
"""阿里通义(wan* 万相 / qwen* 千问)提示词:国产"主体参考"语义。
|
||||
|
||||
生图 API 把参考图当商品锚(subject reference)、prompt 当场景描述,
|
||||
风格词/文字商品描述不会反噬商品本体,负面清单也可以安全写入 prompt。
|
||||
豆包(doubao.py)与此语义一致,直接复用本模块装配。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from .common import (
|
||||
STYLE_SETS, TEXT_RENDER, requirements_block, resolve_style, selling_point_lines,
|
||||
)
|
||||
|
||||
# ── 公共组件(主体参考语义专用)────────────────────────────────────────────
|
||||
|
||||
QUALITY = (
|
||||
"Shot on Sony A7R V with 85mm lens at f/2.0, ultra-detailed, photorealistic, "
|
||||
"8K commercial image quality, professional retouching."
|
||||
)
|
||||
|
||||
PRODUCT_REF_LOCK = (
|
||||
"CRITICAL: The product must look EXACTLY the same as in the reference image — "
|
||||
"identical silhouette, proportions, colors, print pattern, stitching and every design detail. "
|
||||
"Only the background, camera angle, lighting and styling may change. "
|
||||
"Do not redesign, add or remove any element of the product."
|
||||
)
|
||||
|
||||
DEFAULT_NEGATIVE_INTENT = (
|
||||
"no AI-generated look, no CGI quality, no plastic appearance, no watermark, "
|
||||
"no distorted text, no deformed product, no extra limbs, no blurry areas"
|
||||
)
|
||||
|
||||
# ── 各图类型 Prompt ───────────────────────────────────────────────────────
|
||||
|
||||
def _prompt_white_bg(ctx: dict, style: dict, lang: str) -> str:
|
||||
return (
|
||||
f"E-commerce main product image on pure white background (RGB 255,255,255), "
|
||||
f"product \"{ctx['title']}\" centered and filling about 85% of the frame, "
|
||||
f"front view, even shadowless studio lighting with a faint natural contact shadow, "
|
||||
f"{style['tone']}. No text, no props, no background elements. {QUALITY} {PRODUCT_REF_LOCK}"
|
||||
)
|
||||
|
||||
def _prompt_key_features(ctx: dict, style: dict, lang: str) -> str:
|
||||
sp = selling_point_lines(ctx, lang) or ctx["title"]
|
||||
return (
|
||||
f"E-commerce key-features infographic for product \"{ctx['title']}\", square layout: "
|
||||
f"product on the left two-thirds ({style['bg']}), right column lists 3 feature callouts "
|
||||
f"with minimal line icons, thin leader lines pointing to product details. "
|
||||
f"Feature callouts: {sp}. {style['tone']}. {TEXT_RENDER[lang]} {QUALITY} {PRODUCT_REF_LOCK}"
|
||||
)
|
||||
|
||||
def _prompt_selling_pt(ctx: dict, style: dict, lang: str) -> str:
|
||||
sp = selling_point_lines(ctx, lang, 1) or ctx["title"]
|
||||
return (
|
||||
f"Single-selling-point e-commerce poster for product \"{ctx['title']}\": "
|
||||
f"hero product close-up at dynamic angle ({style['bg']}), one large bold headline "
|
||||
f"about \"{sp}\", generous negative space, one small magnified detail circle "
|
||||
f"highlighting material or craft. {style['tone']}. {TEXT_RENDER[lang]} {QUALITY} {PRODUCT_REF_LOCK}"
|
||||
)
|
||||
|
||||
def _prompt_material(ctx: dict, style: dict, lang: str) -> str:
|
||||
return (
|
||||
f"Macro material close-up of product \"{ctx['title']}\": extreme detail shot revealing "
|
||||
f"fabric weave / surface texture / stitching / finish, shallow depth of field, "
|
||||
f"raking light across the surface, {style['tone']}. Small caption label in corner. "
|
||||
f"{TEXT_RENDER[lang]} {QUALITY} {PRODUCT_REF_LOCK}"
|
||||
)
|
||||
|
||||
def _prompt_lifestyle(ctx: dict, style: dict, lang: str) -> str:
|
||||
bg = f" ({style['bg']})" if style.get("bg") else ""
|
||||
return (
|
||||
f"Lifestyle in-context scene for product \"{ctx['title']}\": the product is naturally "
|
||||
f"used / placed in a real environment{bg}, realistic human-scale surroundings, "
|
||||
f"soft daylight, authentic candid mood, product remains the clear visual focus. "
|
||||
f"{style['tone']}. {TEXT_RENDER[lang]} {QUALITY} {PRODUCT_REF_LOCK}"
|
||||
)
|
||||
|
||||
def _prompt_multi_scene(ctx: dict, style: dict, lang: str) -> str:
|
||||
sp = selling_point_lines(ctx, lang)
|
||||
return (
|
||||
f"Triptych multi-scene e-commerce image for product \"{ctx['title']}\": three vertical panels "
|
||||
f"separated by thin gutters, each panel shows the SAME product in a different usage scene "
|
||||
f"(e.g. home interior / outdoor street / office desk), consistent color grading across panels. "
|
||||
f"Panel captions: {sp}. {style['tone']}. {TEXT_RENDER[lang]} {QUALITY} {PRODUCT_REF_LOCK}"
|
||||
)
|
||||
|
||||
def _prompt_ecommerce_detail(ctx: dict, style: dict, lang: str) -> str:
|
||||
sp = selling_point_lines(ctx, lang) or ctx["title"]
|
||||
params = ctx["params_line"]
|
||||
return (
|
||||
f"E-commerce detail-page hero section for product \"{ctx['title']}\", square layout: "
|
||||
f"top half is a hero banner with the product at a 3/4 angle ({style['bg']}); "
|
||||
f"bottom half is a clean spec card listing 3 feature rows with line icons"
|
||||
+ (f" (specs: {params})" if params else "")
|
||||
+ f" and one highlighted row: {sp}. {style['tone']}. {TEXT_RENDER[lang]} {QUALITY} {PRODUCT_REF_LOCK}"
|
||||
)
|
||||
|
||||
def _prompt_size_chart(ctx: dict, style: dict, lang: str) -> str:
|
||||
dims = ctx["params_line"]
|
||||
return (
|
||||
f"Product size chart infographic for \"{ctx['title']}\": product shown in clean front and side views "
|
||||
f"on light background, with thin measurement annotation lines (arrows) marking length, width and height, "
|
||||
f"measurement values rendered next to each line"
|
||||
+ (f" (known specs: {dims})" if dims else "")
|
||||
+ f", small caption row, precise technical drawing aesthetic. {style['tone']}. "
|
||||
f"{TEXT_RENDER[lang]} {QUALITY} {PRODUCT_REF_LOCK}"
|
||||
)
|
||||
|
||||
def _prompt_sku_collection(ctx: dict, style: dict, lang: str) -> str:
|
||||
return (
|
||||
f"Colorway collection image for product \"{ctx['title']}\": the SAME product in all its color/variant "
|
||||
f"options arranged in a neat equal grid (2-4 items per row), each colorway with a small label chip below it, "
|
||||
f"consistent lighting and scale across all items, clean e-commerce presentation. "
|
||||
f"{style['tone']}. {TEXT_RENDER[lang]} {QUALITY} {PRODUCT_REF_LOCK}"
|
||||
)
|
||||
|
||||
def _prompt_custom(ctx: dict, style: dict, lang: str, extra: dict) -> str:
|
||||
hint = (extra.get("prompt_hint") or "").strip()
|
||||
purpose = extra.get("title") or ""
|
||||
detail = extra.get("detail") or ""
|
||||
bg = f" {style['bg']} as environment." if style.get("bg") else ""
|
||||
composed = (
|
||||
f"E-commerce marketing image for product \"{ctx['title']}\""
|
||||
+ (f" — {purpose}" if purpose else "")
|
||||
+ (f": {detail}" if detail else "")
|
||||
+ "."
|
||||
)
|
||||
if hint:
|
||||
composed += f" Composition: {hint}."
|
||||
return f"{composed}{bg} {style['tone']}. {TEXT_RENDER[lang]} {QUALITY} {PRODUCT_REF_LOCK}"
|
||||
|
||||
_PROMPT_BUILDERS = {
|
||||
"white_bg": _prompt_white_bg,
|
||||
"key_features": _prompt_key_features,
|
||||
"selling_pt": _prompt_selling_pt,
|
||||
"material": _prompt_material,
|
||||
"lifestyle": _prompt_lifestyle,
|
||||
"multi_scene": _prompt_multi_scene,
|
||||
"ecommerce_detail": _prompt_ecommerce_detail,
|
||||
"size_chart": _prompt_size_chart,
|
||||
"sku_collection": _prompt_sku_collection,
|
||||
}
|
||||
|
||||
|
||||
def build_prompt(type_id: str, ctx: dict, style_set: int, lang: str, extra: dict | None = None,
|
||||
style_prompt: str | None = None, requirements: str | None = None) -> str:
|
||||
"""构造指定图类型的完整生图 prompt(主体参考语义)。
|
||||
|
||||
extra: 方案项信息 {title, detail, prompt_hint}——custom 类型必需,
|
||||
预设类型也会把 prompt_hint 作为构图补充注入。
|
||||
style_prompt: 用户改写的风格提示词,覆盖 style_set 内置模板(tone/bg 整体替换)。
|
||||
requirements: 生图要求(最高优先级,强制约束),置于 prompt 最前面,
|
||||
声明覆盖一切冲突指令,用户可在此输入强制要求。
|
||||
"""
|
||||
style = resolve_style(style_set, style_prompt)
|
||||
extra = extra or {}
|
||||
if type_id == "custom":
|
||||
prompt = _prompt_custom(ctx, style, lang, extra)
|
||||
else:
|
||||
builder = _PROMPT_BUILDERS.get(type_id)
|
||||
if builder is None:
|
||||
raise ValueError(f"未知图类型: {type_id}")
|
||||
prompt = builder(ctx, style, lang)
|
||||
hint = (extra.get("prompt_hint") or "").strip()
|
||||
if hint:
|
||||
prompt = prompt.rstrip(".") + f". Additional composition guidance: {hint}."
|
||||
req = requirements_block(requirements)
|
||||
if req:
|
||||
prompt = f"{req} {prompt}"
|
||||
return prompt + ". " + DEFAULT_NEGATIVE_INTENT
|
||||
@@ -0,0 +1,154 @@
|
||||
"""提示词公共层:与模型家族无关的商品上下文、风格模板、图类型名与文案组件。
|
||||
|
||||
各家族模块(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)
|
||||
@@ -0,0 +1,9 @@
|
||||
"""豆包(火山方舟 Seedream)提示词。
|
||||
|
||||
豆包与通义同为国产"主体参考"生图模型:参考图即商品锚、prompt 为场景描述,
|
||||
提示词语义一致,直接复用阿里系装配;差异(去 AI 味后缀)在 generator 层追加。
|
||||
独立成文件便于后续按豆包特性分化。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from .alibaba import build_prompt as build_prompt # noqa: F401 主体参考语义与通义共用
|
||||
@@ -0,0 +1,163 @@
|
||||
"""Google 图像模型(nano-banana / nano-banana-2 / nano-banana-2-lite / nano-banana-pro)提示词。
|
||||
|
||||
语义:Gemini 图像编辑 —— 原生主体保持能力强,输入图即"主体 + 底图",
|
||||
对自然语言指令遵循好。不套用 GPT 的编辑契约(冗长的拒绝条款反而稀释指令),
|
||||
也不用负面清单(无 negative_prompt 参数)。要点:
|
||||
- 开头一句话钉死"主体 = 第一张图里的商品,逐像素保持";
|
||||
- 指令自然语言描述目标画面(场景/排版/文案),不重述商品外观;
|
||||
- 标题/参数仅作识别背景并声明以图为准。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from .common import TEXT_RENDER, requirements_block, resolve_style, selling_point_lines
|
||||
|
||||
_SUBJECT_LOCK = (
|
||||
"SUBJECT LOCK (highest priority): the product in the first image is the subject. "
|
||||
"Keep it exactly as photographed — same shape, proportions, colors, print/pattern, "
|
||||
"logo, label and every detail — and place that very product into the result. "
|
||||
"A second image, when present, is another view of the same product for reference only."
|
||||
)
|
||||
|
||||
_QUALITY = (
|
||||
"OUTPUT: photorealistic commercial e-commerce photography, ultra-detailed, "
|
||||
"natural light and shadow, professional retouching."
|
||||
)
|
||||
|
||||
_REMINDER = (
|
||||
"Reminder: keep the product exactly as in the first image; change only its surroundings, "
|
||||
"composition, lighting and overlay graphics."
|
||||
)
|
||||
|
||||
|
||||
def _anchor(ctx: dict) -> str:
|
||||
"""商品文字锚定:仅供识别,明确以图为准(同 gpt 模块,避免文字反噬商品)。"""
|
||||
line = f"Context (identification only): the product is \"{ctx['title']}\""
|
||||
if ctx.get("params_line"):
|
||||
line += f" ({ctx['params_line']})"
|
||||
return line + ". The image, not this text, defines the product's appearance."
|
||||
|
||||
|
||||
# ── 各图类型指令(自然语言编辑口吻)────────────────────────────────────────
|
||||
|
||||
def _task_white_bg(ctx: dict, lang: str) -> str:
|
||||
return (
|
||||
"Replace the background of this product photo with seamless pure white (RGB 255,255,255): "
|
||||
"product centered in front view filling about 85% of the frame, even studio lighting with only "
|
||||
"a faint natural contact shadow. No props, no added text, no background elements."
|
||||
)
|
||||
|
||||
def _task_key_features(ctx: dict, lang: str) -> str:
|
||||
sp = selling_point_lines(ctx, lang) or ctx["title"]
|
||||
return (
|
||||
"Create a square key-features infographic: the unchanged product on the left two-thirds, "
|
||||
"a clean right-hand panel with 3 feature callouts using minimal line icons and thin leader "
|
||||
f"lines pointing at the product. Callout copy: {sp}."
|
||||
)
|
||||
|
||||
def _task_selling_pt(ctx: dict, lang: str) -> str:
|
||||
sp = selling_point_lines(ctx, lang, 1) or ctx["title"]
|
||||
return (
|
||||
"Turn the photo into a single-selling-point poster: hero close-up of the unchanged product at "
|
||||
f"a dynamic angle, one large bold headline about \"{sp}\", generous negative space, and a small "
|
||||
"magnified circle zooming into an existing detail of the product."
|
||||
)
|
||||
|
||||
def _task_material(ctx: dict, lang: str) -> str:
|
||||
return (
|
||||
"Create an extreme macro close-up of an existing area of the product's surface, showing its "
|
||||
"true fabric weave / texture / stitching exactly as in the photo; shallow depth of field, "
|
||||
"raking light, small caption in a corner."
|
||||
)
|
||||
|
||||
def _task_lifestyle(ctx: dict, lang: str) -> str:
|
||||
return (
|
||||
"Place the unchanged product into a realistic everyday scene where it would naturally be used: "
|
||||
"human-scale surroundings, soft daylight, authentic candid mood, the product as the clear visual focus."
|
||||
)
|
||||
|
||||
def _task_multi_scene(ctx: dict, lang: str) -> str:
|
||||
sp = selling_point_lines(ctx, lang)
|
||||
task = (
|
||||
"Build a triptych of three vertical panels separated by thin gutters: each panel shows an "
|
||||
"identical copy of the product in a different usage scene (home interior / outdoor street / "
|
||||
"office desk), with consistent color grading across panels."
|
||||
)
|
||||
if sp:
|
||||
task += f" Panel captions: {sp}."
|
||||
return task
|
||||
|
||||
def _task_ecommerce_detail(ctx: dict, lang: str) -> str:
|
||||
sp = selling_point_lines(ctx, lang) or ctx["title"]
|
||||
params = ctx["params_line"]
|
||||
return (
|
||||
"Compose a square detail-page hero section: top half a hero banner with the unchanged product "
|
||||
"at a 3/4 angle; bottom half a clean spec card with 3 feature rows and line icons"
|
||||
+ (f" (specs: {params})" if params else "")
|
||||
+ f", one highlighted row: {sp}."
|
||||
)
|
||||
|
||||
def _task_size_chart(ctx: dict, lang: str) -> str:
|
||||
dims = ctx["params_line"]
|
||||
return (
|
||||
"Create a size chart: the unchanged product in clean front and side views on a light background, "
|
||||
"thin measurement annotation lines (arrows) marking length, width and height with values beside "
|
||||
"each line"
|
||||
+ (f" (known specs: {dims})" if dims else "")
|
||||
+ ", small caption row, precise technical-drawing aesthetic."
|
||||
)
|
||||
|
||||
def _task_sku_collection(ctx: dict, lang: str) -> str:
|
||||
# 不展开"全部配色":会凭空造出新商品;只排列同一件的多个副本
|
||||
return (
|
||||
"Arrange several identical copies of the product in a neat equal grid (2-4 per row) with a small "
|
||||
"label chip below each copy; identical lighting and scale across copies. Every copy shows this "
|
||||
"exact product — do not invent other colorways or variants."
|
||||
)
|
||||
|
||||
_TASK_BUILDERS = {
|
||||
"white_bg": (_task_white_bg, False),
|
||||
"key_features": (_task_key_features, True),
|
||||
"selling_pt": (_task_selling_pt, True),
|
||||
"material": (_task_material, True),
|
||||
"lifestyle": (_task_lifestyle, True),
|
||||
"multi_scene": (_task_multi_scene, True),
|
||||
"ecommerce_detail": (_task_ecommerce_detail, True),
|
||||
"size_chart": (_task_size_chart, True),
|
||||
"sku_collection": (_task_sku_collection, True),
|
||||
}
|
||||
|
||||
|
||||
def build_prompt(type_id: str, ctx: dict, style_set: int, lang: str, extra: dict | None = None,
|
||||
style_prompt: str | None = None, requirements: str | None = None) -> str:
|
||||
"""构造指定图类型的 prompt:要求块 → 指令 → 主体锁 → 锚定 → 风格 → 文案 → 画质 → 提醒。"""
|
||||
style = resolve_style(style_set, style_prompt)
|
||||
extra = extra or {}
|
||||
hint = (extra.get("prompt_hint") or "").strip()
|
||||
|
||||
if type_id == "custom":
|
||||
purpose = extra.get("title") or ""
|
||||
detail = extra.get("detail") or ""
|
||||
task = "Create an e-commerce marketing image featuring the product from the first image"
|
||||
task += f" — {purpose}" if purpose else ""
|
||||
task += f": {detail}" if detail else ""
|
||||
task += "."
|
||||
wants_text = True
|
||||
else:
|
||||
entry = _TASK_BUILDERS.get(type_id)
|
||||
if entry is None:
|
||||
raise ValueError(f"未知图类型: {type_id}")
|
||||
builder, wants_text = entry
|
||||
task = builder(ctx, lang)
|
||||
if hint:
|
||||
task += f" Composition guidance: {hint}."
|
||||
|
||||
parts = [p for p in (requirements_block(requirements),) if p]
|
||||
parts.append(task)
|
||||
parts.append(_SUBJECT_LOCK)
|
||||
parts.append(_anchor(ctx))
|
||||
parts.append(f"Scene style (scene and background only, never the product): {style['tone']}.")
|
||||
if wants_text:
|
||||
parts.append(f"Text overlay (a graphic layer, never printed on the product): {TEXT_RENDER[lang]}")
|
||||
parts.append(_QUALITY)
|
||||
parts.append(_REMINDER)
|
||||
return "\n\n".join(parts)
|
||||
@@ -0,0 +1,195 @@
|
||||
"""GPT 图像模型(gpt-image-2 / gpt-image-2-vip,RightAPI 中转)提示词。
|
||||
|
||||
语义:/v1/images/edits —— 输入图是"被编辑的照片",prompt 是编辑指令;
|
||||
与通义/豆包的"主体参考"语义完全不同:参考图不是商品锚,模型会按文字指令
|
||||
重新渲染整张图。此前与国产模型共用场景提示词,再用文字锚定商品并要求输出
|
||||
"匹配商品描述",导致模型把商品改造成营销关键词描述的样子(必现商品被改)。
|
||||
|
||||
本模块写法原则:
|
||||
1. 商品只由 Image 1 定义;标题/参数仅作识别背景并声明"以图为准",
|
||||
绝不要求输出匹配文字描述(那等于授权模型改商品);
|
||||
2. 指令只说"改什么"(背景/场景/排版/文案),不描述商品外观;
|
||||
3. 分节精简、首尾重申保真;不用负面清单(gpt 无 negative_prompt 参数,
|
||||
罗列畸形反而往上下文植入概念);
|
||||
4. sku 合集 / 多拼图明确"复制同一件商品,禁止发明新配色或变体"。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from .common import TEXT_RENDER, requirements_block, resolve_style, selling_point_lines
|
||||
|
||||
# 保真锁:商品由 Image 1 唯一定义,其余指令一律不得触碰商品本体
|
||||
_PRESERVE = (
|
||||
"PRESERVE (absolute, overrides every other instruction below): the product shown in Image 1. "
|
||||
"Reuse the photographed product exactly as it is — identical shape, silhouette, proportions, "
|
||||
"colors, print/pattern, logo and label text, materials, stitching and surface details. "
|
||||
"Do not redesign, restyle, recolor, re-pattern, tidy up or substitute the product, "
|
||||
"and do not let any style or text instruction below alter it. Image 2 is a secondary "
|
||||
"view of the same product for reference only."
|
||||
)
|
||||
|
||||
_STYLE = (
|
||||
"SCENE STYLE (applies to background, scene, props and lighting only — never to the product): "
|
||||
)
|
||||
|
||||
_QUALITY = (
|
||||
"OUTPUT: photorealistic commercial e-commerce photography, ultra-detailed, "
|
||||
"natural light and shadow, professional retouching."
|
||||
)
|
||||
|
||||
_REMINDER = (
|
||||
"FINAL CHECK: the product itself must remain exactly as photographed in Image 1 — "
|
||||
"only its surroundings, composition, lighting and overlay graphics may differ."
|
||||
)
|
||||
|
||||
# 图内文案:明确是"排版图层",不落在商品本体上
|
||||
_TEXT_SCOPE = (
|
||||
"TEXT OVERLAY (a graphic layer on the composition, never printed on the product): "
|
||||
)
|
||||
|
||||
|
||||
def _anchor(ctx: dict) -> str:
|
||||
"""商品文字锚定:仅供识别,明确声明以图为准。
|
||||
|
||||
只放标题 + 参数、不放营销描述——描述里的卖点词("卡通""加固""防水"等)
|
||||
在 edits 语义下会被执行到商品上;官逆通道(-vip)参考图被弱化时,
|
||||
文字锚定用于帮模型认出"是哪件商品",而不是"长什么样"。
|
||||
"""
|
||||
line = f"CONTEXT (identification only): the product is \"{ctx['title']}\""
|
||||
if ctx.get("params_line"):
|
||||
line += f" ({ctx['params_line']})"
|
||||
return (
|
||||
line
|
||||
+ ". Image 1 — not this text — defines the product's appearance; "
|
||||
"if they ever conflict, follow Image 1."
|
||||
)
|
||||
|
||||
|
||||
# ── 各图类型的编辑指令(只描述改动,不描述商品)────────────────────────────
|
||||
|
||||
def _task_white_bg(ctx: dict, lang: str) -> str:
|
||||
return (
|
||||
"TASK: Clean up this product photo for a marketplace listing. Replace the entire "
|
||||
"background with seamless pure white (RGB 255,255,255); recompose with the product "
|
||||
"centered in front view filling about 85% of the frame; keep only a faint natural "
|
||||
"contact shadow. No props, no text, no background elements."
|
||||
)
|
||||
|
||||
def _task_key_features(ctx: dict, lang: str) -> str:
|
||||
sp = selling_point_lines(ctx, lang) or ctx["title"]
|
||||
return (
|
||||
"TASK: Feature infographic on a square canvas. Keep the product unchanged on the left "
|
||||
"two-thirds; build the right third as a clean info panel listing 3 feature callouts with "
|
||||
f"minimal line icons and thin leader lines pointing at parts of the product. Callout copy: {sp}."
|
||||
)
|
||||
|
||||
def _task_selling_pt(ctx: dict, lang: str) -> str:
|
||||
sp = selling_point_lines(ctx, lang, 1) or ctx["title"]
|
||||
return (
|
||||
"TASK: Single-selling-point poster. Hero close-up of the unchanged product at a dynamic "
|
||||
f"angle, generous negative space, one large bold headline about \"{sp}\", plus one small "
|
||||
"magnified circle zooming into an existing detail of the product (zoom only — do not "
|
||||
"invent details that are not in the photo)."
|
||||
)
|
||||
|
||||
def _task_material(ctx: dict, lang: str) -> str:
|
||||
return (
|
||||
"TASK: Material close-up. Zoom tightly into an existing area of the product's surface and "
|
||||
"show its true texture — fabric weave, surface finish, stitching — exactly as it appears in "
|
||||
"Image 1; shallow depth of field, raking light across the surface; small caption label in a corner."
|
||||
)
|
||||
|
||||
def _task_lifestyle(ctx: dict, lang: str) -> str:
|
||||
return (
|
||||
"TASK: Lifestyle scene. Place the unchanged product into a realistic everyday environment "
|
||||
"where it would naturally be used: human-scale surroundings, soft daylight, authentic candid "
|
||||
"mood, matched shadows and color temperature, the product remaining the clear visual focus."
|
||||
)
|
||||
|
||||
def _task_multi_scene(ctx: dict, lang: str) -> str:
|
||||
sp = selling_point_lines(ctx, lang)
|
||||
task = (
|
||||
"TASK: Triptych showcase. Build three vertical panels separated by thin gutters; every panel "
|
||||
"contains an IDENTICAL copy of the product from Image 1 (do not re-render it differently per "
|
||||
"panel) placed in a different usage scene (e.g. home interior / outdoor street / office desk), "
|
||||
"with consistent color grading across panels."
|
||||
)
|
||||
if sp:
|
||||
task += f" Panel captions: {sp}."
|
||||
return task
|
||||
|
||||
def _task_ecommerce_detail(ctx: dict, lang: str) -> str:
|
||||
sp = selling_point_lines(ctx, lang) or ctx["title"]
|
||||
params = ctx["params_line"]
|
||||
return (
|
||||
"TASK: Detail-page hero section on a square canvas. Top half: hero banner with the unchanged "
|
||||
"product at a 3/4 angle. Bottom half: clean spec card with 3 feature rows and line icons"
|
||||
+ (f" (specs: {params})" if params else "")
|
||||
+ f", one highlighted row: {sp}."
|
||||
)
|
||||
|
||||
def _task_size_chart(ctx: dict, lang: str) -> str:
|
||||
dims = ctx["params_line"]
|
||||
return (
|
||||
"TASK: Measurement chart. Show the unchanged product in clean front and side views on a light "
|
||||
"background; overlay thin technical annotation lines (arrows) marking length, width and height "
|
||||
"with measurement values rendered beside each line"
|
||||
+ (f" (known specs: {dims})" if dims else "")
|
||||
+ "; precise technical-drawing aesthetic, small caption row."
|
||||
)
|
||||
|
||||
def _task_sku_collection(ctx: dict, lang: str) -> str:
|
||||
# 关键差异:不允许像国产模型那样展开"全部配色"——edits 语义下那会凭空造出新商品
|
||||
return (
|
||||
"TASK: Product lineup. Arrange several IDENTICAL copies of the product from Image 1 in a neat "
|
||||
"equal grid (2-4 per row) with a small label chip below each copy; identical lighting and scale "
|
||||
"across copies. Every copy must show this exact product — do NOT invent other colorways, "
|
||||
"variants or versions."
|
||||
)
|
||||
|
||||
_TASK_BUILDERS = {
|
||||
"white_bg": (_task_white_bg, False),
|
||||
"key_features": (_task_key_features, True),
|
||||
"selling_pt": (_task_selling_pt, True),
|
||||
"material": (_task_material, True),
|
||||
"lifestyle": (_task_lifestyle, True),
|
||||
"multi_scene": (_task_multi_scene, True),
|
||||
"ecommerce_detail": (_task_ecommerce_detail, True),
|
||||
"size_chart": (_task_size_chart, True),
|
||||
"sku_collection": (_task_sku_collection, True),
|
||||
}
|
||||
|
||||
|
||||
def build_prompt(type_id: str, ctx: dict, style_set: int, lang: str, extra: dict | None = None,
|
||||
style_prompt: str | None = None, requirements: str | None = None) -> str:
|
||||
"""构造指定图类型的 edits 语义 prompt:要求块 → 编辑指令 → 保真锁 → 锚定 → 风格 → 文案 → 画质 → 终检。"""
|
||||
style = resolve_style(style_set, style_prompt)
|
||||
extra = extra or {}
|
||||
hint = (extra.get("prompt_hint") or "").strip()
|
||||
|
||||
if type_id == "custom":
|
||||
purpose = extra.get("title") or ""
|
||||
detail = extra.get("detail") or ""
|
||||
task = "TASK: Create an e-commerce marketing image featuring the product from Image 1"
|
||||
task += f" — {purpose}" if purpose else ""
|
||||
task += f": {detail}" if detail else ""
|
||||
task += "."
|
||||
wants_text = True
|
||||
else:
|
||||
entry = _TASK_BUILDERS.get(type_id)
|
||||
if entry is None:
|
||||
raise ValueError(f"未知图类型: {type_id}")
|
||||
builder, wants_text = entry
|
||||
task = builder(ctx, lang)
|
||||
if hint:
|
||||
task += f" Composition guidance: {hint}."
|
||||
|
||||
parts = [p for p in (requirements_block(requirements),) if p]
|
||||
parts.append(task)
|
||||
parts.append(_PRESERVE)
|
||||
parts.append(_anchor(ctx))
|
||||
parts.append(f"{_STYLE}{style['tone']}.")
|
||||
if wants_text:
|
||||
parts.append(f"{_TEXT_SCOPE}{TEXT_RENDER[lang]}")
|
||||
parts.append(_QUALITY)
|
||||
parts.append(_REMINDER)
|
||||
return "\n\n".join(parts)
|
||||
Reference in New Issue
Block a user