大量优化 模型改成gpt6
Deploy dev / deploy (push) Successful in 41s

This commit is contained in:
Azmat@qq.com
2026-09-28 18:15:08 +08:00
parent a84431c9c5
commit 4421706290
14 changed files with 1878 additions and 540 deletions
+126 -22
View File
@@ -1937,16 +1937,64 @@ _PLATFORM_COVER_BLOCKS = {
),
}
# §3 / §3.1 slot 版式骨架(低信息密度版):默认 4 张按 hero→scene→selling→detail 取,8/12 张再补 multi/promo。
# §3 / §3.1 slot 版式骨架(低信息密度版):默认 4 张按 主图 hero → 卖点图 selling → 细节图 detail → 场景图 scene
# 取(与页面文案「主图、卖点图、细节图与场景图」一致),8/12 张再补 multi/promo 后循环。
_COVER_SLOTS = {
"hero": "正面或四分之三角度的商品 / 上身主视觉,主体占画面 60%-80%,纯净干净背景,经典电商主图构图,无文案",
"scene": "场景主视觉:挂拍 / 衣架 / 生活场景 / 手部整理 / 使用情境,环境自然光,氛围感,无文案或仅 1 个极短标题",
"selling": "轻卖点封面:商品主体居中或偏置,最多 1 个短标题加 1-2 个极短标签,文字区克制,不堆参数",
"detail": "质感特写:放大材质 / 做工 / 关键结构(扣位 / 肩带 / 边缘走线),近景视角,突出质感,无文案或 1 个短标签",
"hero": "主图:正面或四分之三角度的商品 / 上身主视觉,主体占画面 60%-80%,纯净干净背景,经典电商主图构图,无文案",
"selling": "卖点图:商品主体居中或偏置,最多 1 个短标题加 1-2 个极短标签,文字区克制,不堆参数",
"detail": "细节图:放大材质 / 做工 / 关键结构(扣位 / 肩带 / 边缘走线 / 质地),近景视角,突出质感,无文案或 1 个短标签",
"scene": "场景图:挂拍 / 衣架 / 生活场景 / 手部整理 / 使用情境,环境自然光,氛围感,无文案或仅 1 个极短标题",
"multi": "多角度组合:商品换一个朝向 / 视角,几何分区干净背景,现代简约风,无文案",
"promo": "促销封面:主体大、利益点单一,底部或角落留 1 条活动短语,高对比配色,不铺满文字、不编造价格",
"promo": "活动封面:主体大、利益点单一(只能取自用户提供的卖点),底部或角落留 1 条短语,高对比配色,不铺满文字、不编造价格",
}
_COVER_SLOT_ORDER = ["hero", "scene", "selling", "detail", "multi", "promo"]
_COVER_SLOT_ORDER = ["hero", "selling", "detail", "scene", "multi", "promo"]
# 通用电商主图规范:平台套图不再要求选平台(platform_id 为空)时使用;旧记录 / 旧调用方带 platform_id
# 仍走上面的平台块,保持向后兼容。比例由输出 size 控制(页面默认 1:1),这里只描述版式要求。
_ECOM_MAIN_IMAGE_SPEC = (
"请生成一张通用电商商品主图套图中的一张,适配主流电商平台商品主图规范:默认正方形构图,"
"商品主体清晰完整、不被裁切,移动端缩略图下一眼可识别;光线专业、边缘干净、质感真实;"
"整组图保持统一视觉调性(同一色系 / 光线 / 质感),只出主图 / 卖点图 / 细节图 / 场景图,不出详情长图。"
)
# 商品信息字段(前端「商品信息」输入 → request_payload.product_info),每项截断防止撑爆提示词。
_COVER_INFO_FIELDS = ("selling_points", "effect", "audience", "specs", "notes")
_COVER_INFO_MAX_LEN = 600
def normalize_cover_product_info(raw) -> dict[str, str]:
"""把前端传来的商品信息规范成 {selling_points, effect, audience, specs, notes} 的非空字符串字典。
兼容 JSON 字符串 / list 值;未知键丢弃;全空返回 {}(= 旧行为)。"""
if isinstance(raw, str):
try:
raw = json.loads(raw)
except (TypeError, ValueError):
return {}
if not isinstance(raw, dict):
return {}
out: dict[str, str] = {}
for key in _COVER_INFO_FIELDS:
val = raw.get(key)
if isinstance(val, (list, tuple)):
val = "\n".join(str(v).strip() for v in val if str(v or "").strip())
text = str(val or "").strip()
if text:
out[key] = text[:_COVER_INFO_MAX_LEN]
return out
def _split_selling_points(text: str) -> list[str]:
"""卖点文本 → 卖点列表:优先按换行 / 分号拆,只有一行时再按顿号 / 逗号拆;最多 6 条。"""
text = (text or "").strip()
if not text:
return []
# 去掉行首列表符号(「- 」「• 」「1.」「2、」),不误删卖点本身的数字(如「3重玻尿酸」)
parts = [re.sub(r"^\s*(?:[-•·*]+|\d+\s*[.、))])\s*", "", x).strip() for x in re.split(r"[\n\r;;]+", text)]
parts = [x for x in parts if x]
if len(parts) <= 1:
parts = [x.strip() for x in re.split(r"[、,,]+", parts[0] if parts else text) if x.strip()]
return parts[:6]
# §3.1 / §8 头图低信息密度上限:所有平台 / 所有模型都必须遵守,防止漂成详情页。
_COVER_LOW_DENSITY = (
@@ -1999,12 +2047,17 @@ def build_platform_cover_prompt_refs(
index: int = 0,
count: int = 4, # noqa: ARG001 — 透传保留,后续可据张数扩展 slot 选择
product_ref_count: int = 1,
product_info: dict | None = None,
) -> str:
"""平台套图 image_edit 提示词(refs 优化版,对照「平台套图线上提示词优化版.md」):
"""平台套图(电商主图套图)image_edit 提示词:
参考图1~N=同一件真实商品(锁外形/品牌/配色/Logo/比例),有模特时参考图N+1=出镜模特。
按 `platform_id` 注入平台块、按 `index` 选 slot 版式,统一服从「头图低信息密度」与「背景反差」,
内衣 + 有模特时叠加强约束。只出头图 / 主图 / 封面候选,不再出详情。"""
`product_info`(用户在页面填写的核心卖点 / 商品作用 / 适用人群 / 规格 / 补充要求)作为本组图的卖点依据注入,
并按 slot 分配到卖点图 / 细节图 / 场景图;未传时回落为把 `base_prompt` 当作商品信息 / 画面要求(旧记录兼容)。
`platform_id` 已改为可选:传了(旧记录 / 旧调用方)仍注入平台块,不传则用通用电商主图规范。
统一服从「头图低信息密度」与「背景反差」,内衣 + 有模特时叠加强约束。只出主图 / 卖点 / 细节 / 场景,不出详情。"""
name = (getattr(product, "title", "") or "商品").strip()
info = normalize_cover_product_info(product_info) if product_info else {}
selling_points = _split_selling_points(info.get("selling_points", ""))
n = max(1, int(product_ref_count or 1))
if n <= 1:
ref_word = "参考图1"
@@ -2018,15 +2071,37 @@ def build_platform_cover_prompt_refs(
f"请严格锁定{ref_word}中商品的外形、颜色、材质、结构、Logo、品牌文字与比例,"
"严禁重新设计 / 改样 / 改品类;这些参考图只锁商品本体,不锁原图里的床品 / 桌面 / 墙面 / 绿植 / 道具与拍摄光线。"
)
# 平台块(canonical key 命中则用 §4 平台块,否则回落平台名 / 通用)
block = _PLATFORM_COVER_BLOCKS.get(platform_id)
pname = _PLATFORM_NAMES.get(platform_id, "")
# 平台块(可选,旧记录兼容):canonical key 命中则用 §4 平台块;否则用通用电商主图规范
block = _PLATFORM_COVER_BLOCKS.get(platform_id or "")
pname = _PLATFORM_NAMES.get(platform_id or "", "")
if block:
lines.append(block)
elif pname:
lines.append(f"请生成一张适合「{pname}」平台的商品头图 / 主图 / 封面候选,统一视觉风格。")
else:
lines.append("请生成一张电商平台商品头图 / 主图 / 封面候选,统一视觉风格。")
lines.append(_ECOM_MAIN_IMAGE_SPEC)
# 商品信息:用户填写的卖点 / 作用是本组图唯一的卖点依据
if info:
facts = [f"商品名称:{name}"]
if selling_points:
facts.append("核心卖点:" + ";".join(selling_points))
if info.get("effect"):
facts.append("商品作用 / 功效:" + info["effect"])
if info.get("audience"):
facts.append("适用人群:" + info["audience"])
if info.get("specs"):
facts.append("规格参数:" + info["specs"])
lines.append(
"商品信息(用户提供,是本组图唯一的卖点与文案依据):" + ";".join(facts) + "。"
"画面要准确传达这些卖点与商品作用,画面文案只能从中提炼(短标题 ≤8 字),"
"不得编造信息之外的功效 / 成分 / 数据 / 认证 / 价格。"
)
elif base_prompt and base_prompt.strip():
# 旧记录 / 旧调用方:没有结构化商品信息,prompt 即用户的商品信息与画面要求
lines.append(
"用户提供的商品信息与画面要求(卖点 / 作用只能取自这里,不得编造;"
"不得覆盖商品一致性、版式与低信息密度规则):" + base_prompt.strip()
)
# 头图低信息密度上限
lines.append(_COVER_LOW_DENSITY)
# 模特身份 + 内衣强约束
@@ -2037,15 +2112,33 @@ def build_platform_cover_prompt_refs(
)
if _is_underwear_product(product):
lines.append(_UNDERWEAR_ON_MODEL)
# 本张 slot 版式
# 本张 slot 版式(主图 → 卖点图 → 细节图 → 场景图 → …),并把商品信息分配到对应图位
slot_key = _COVER_SLOT_ORDER[index % len(_COVER_SLOT_ORDER)]
lines.append("本张版式:" + _COVER_SLOTS[slot_key] + "。")
slot_line = "本张版式:" + _COVER_SLOTS[slot_key] + "。"
if info:
cycle = index // len(_COVER_SLOT_ORDER)
if slot_key in {"selling", "promo"} and selling_points:
point = selling_points[(cycle * 2 + (1 if slot_key == "promo" else 0)) % len(selling_points)]
slot_line += f"本张重点表现卖点「{point}」:用画面直观演示该卖点,短标题从该卖点提炼。"
elif slot_key == "selling" and info.get("effect"):
slot_line += "本张重点表现商品作用 / 功效,用画面直观演示使用效果,短标题从商品作用中提炼。"
elif slot_key == "detail":
slot_line += "细节要能佐证商品卖点 / 作用(如对应的材质、质地、成分形态、结构或工艺)。"
elif slot_key == "scene":
who = info.get("audience")
slot_line += (
f"场景贴合适用人群「{who}」的真实使用情境,体现商品作用。" if who
else "场景选择能体现商品作用的真实使用情境。"
)
elif slot_key == "hero" and selling_points:
slot_line += "主图不加卖点文案,只把商品本体拍得清楚、高级、可信。"
lines.append(slot_line)
# 背景反差
lines.append(_COVER_BG_CONTRAST)
if base_prompt and base_prompt.strip():
if info.get("notes"):
lines.append(
"用户补充(只影响氛围 / 构图 / 场景 / 光线 / 表达偏好,不得覆盖商品一致性、平台与版式规则):"
+ base_prompt.strip()
"用户补充要求(只影响氛围 / 构图 / 场景 / 光线 / 表达偏好,不得覆盖商品一致性与版式规则):"
+ info["notes"]
)
lines.append(_COVER_NEGATIVE)
return " ".join(lines)
@@ -3533,7 +3626,7 @@ def _reap_stale_standalone_image_tasks(*, team) -> None:
continue
def enqueue_standalone_images(*, team, user, prompt: str, mode: str = "image", count: int = 1, product_id: str | None = None, reference_product: bool = False, model_id: str | None = None, model_entity_id: str | None = None, ratio: str | None = None, image_model: str | None = None, conversation=None, reference_image_ids: list[str] | None = None, platform_id: str | None = None, batch_id: str | None = None, retry_of_task_id: str | None = None, tryon_prompt_v2_override: bool = False, tryon_ab: dict | None = None, feature: str | None = None, dispatch: bool = True) -> list[AITask]:
def enqueue_standalone_images(*, team, user, prompt: str, mode: str = "image", count: int = 1, product_id: str | None = None, reference_product: bool = False, model_id: str | None = None, model_entity_id: str | None = None, ratio: str | None = None, image_model: str | None = None, conversation=None, reference_image_ids: list[str] | None = None, reference_context: str | None = None, platform_id: str | None = None, product_info: dict | None = None, batch_id: str | None = None, retry_of_task_id: str | None = None, tryon_prompt_v2_override: bool = False, tryon_ab: dict | None = None, feature: str | None = None, dispatch: bool = True) -> list[AITask]:
"""独立生图(图片创作 / 模特上身图 / 平台套图)改为**异步**:本函数在 Web 请求里只做「建任务 +
预留额度」这种秒级的活,真正 ~30s 的 ARK 出图交给 Celery worker(generate_standalone_image_task)。
@@ -3591,9 +3684,12 @@ def enqueue_standalone_images(*, team, user, prompt: str, mode: str = "image", c
status__in=(AITask.Status.FAILED, AITask.Status.CANCELLED),
).first()
retry_of_task_id = str(retry_of_task.id) if retry_of_task else None
# 平台套图:规范化平台 id(前端 dy/tb… → canonical),用于注入平台版式块(优化版);非 cover 模式忽略。
# 平台套图:平台 id 已改为可选(页面不再选平台 → 走通用电商主图规范);旧调用方仍可传 canonical id
# 注入平台版式块。非 cover 模式忽略。
platform_key = str(platform_id or "").strip() if mode == "cover" else ""
platform_name = _PLATFORM_NAMES.get(platform_key, "")
# 平台套图:用户填写的商品信息(卖点 / 作用 / 人群 / 规格 / 补充),worker 据此构建主图提示词;非 cover 忽略。
cover_product_info = normalize_cover_product_info(product_info) if mode == "cover" else {}
from apps.billing.pricing import quote_flat
# Step 2.1:只为“模特上身图 + 商品”记录一次确定性分类快照。这里不读取图片、不调用模型;
@@ -3647,8 +3743,12 @@ def enqueue_standalone_images(*, team, user, prompt: str, mode: str = "image", c
for index in range(count):
quote = quote_flat(model_config, team=team)
request_payload = {"model": model_config.name, "endpoint": model_config.endpoint, "prompt": prompt, "mode": mode, "index": index, "product_id": str(product_id) if product_id else None, "reference_product": bool(reference_product), "model_id": str(model_id) if model_id else None, "model_entity_id": str(model_entity_id) if model_entity_id else None, "batch_id": batch_id, "ratio": str(ratio) if ratio else None, "reference_image_ids": ref_ids, "platform_id": platform_key or None, "platform_name": platform_name or None}
if reference_context:
request_payload["reference_context"] = str(reference_context)
if feature:
request_payload["feature"] = str(feature)
if cover_product_info:
request_payload["product_info"] = dict(cover_product_info)
if use_model_routing:
request_payload["model_routing_v1"] = True
if tryon_classification is not None:
@@ -3707,6 +3807,9 @@ def run_standalone_image_task(*, task_id: str) -> None:
user = task.created_by
payload = dict(task.request_payload or {})
prompt = str(payload.get("prompt") or "")
reference_context = str(payload.get("reference_context") or "").strip()
if reference_context:
prompt = f"{prompt}\n\n【已引用素材事实】\n{reference_context}"
mode = str(payload.get("mode") or "image")
index = int(payload.get("index") or 0)
product_id = payload.get("product_id") or None
@@ -3801,7 +3904,7 @@ def run_standalone_image_task(*, task_id: str) -> None:
}
elif mode == "cover" and product_urls:
# 平台套图:参考图1~N=商品真实图(多角度,_product_reference_urls 已优先真实上传图/排除 AI 图),
# 有模特则参考图N+1=模特(锁人脸/身形)。platform_id 注入平台版式块(优化版)。
# 有模特则参考图N+1=模特(锁人脸/身形)。product_info 注入商品卖点 / 作用;platform_id 可选(旧记录兼容)。
edit_images = product_urls + ([model_url] if model_url else [])
edit_prompt = build_platform_cover_prompt_refs(
product,
@@ -3810,6 +3913,7 @@ def run_standalone_image_task(*, task_id: str) -> None:
platform_id=str(payload.get("platform_id") or ""),
index=index,
product_ref_count=len(product_urls),
product_info=payload.get("product_info") or None,
)
elif bool(payload.get("reference_product")) and product_url:
edit_images = [product_url]