196 lines
9.1 KiB
Python
196 lines
9.1 KiB
Python
"""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)
|