Files
image-suite-studio/server/services/prompts/gpt.py
T
2026-08-20 12:36:00 +08:00

196 lines
9.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""GPT 图像模型(gpt-image-2 / gpt-image-2-vipRightAPI 中转)提示词。
语义:/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)