完成的商品复刻

This commit is contained in:
Azmat@qq.com
2026-08-31 17:27:35 +08:00
parent e24ca1b0de
commit 5bb1a4927b
4 changed files with 481 additions and 248 deletions
+74 -30
View File
@@ -376,24 +376,42 @@ def load_product_semantic_remix_skill() -> str:
def _product_semantic_facts(product: Product) -> dict:
"""只取商品库中已填写的事实,绝不从商品图片猜用途或功效。"""
"""商品库中已填写的事实 + 动作继承策略。
⚠️ 「资料不足」只限制**能说什么**(功效/成分/卖点),不限制**能拍什么动作**。
商品的物理形态(瓶/软管/按压泵/滴管/膏体/盒装)从商品图就能直接看出来,
是可见事实而非推断;拿取、旋开、按压、涂抹这类动作由形态决定,不由功效决定。
早期版本把两者混为一谈,资料一不全就把原片的使用镜全改成「摆在桌上转一转」,
化妆品复刻化妆品也会退化成静物摆拍 —— 这里明确拆开两个维度。
"""
selling_points = [
{"标题": point.title.strip(), "说明": point.detail.strip()}
for point in product.selling_points.all()
if point.title.strip() or point.detail.strip()
]
category = product.category.strip()
has_text = bool(product.description.strip() or selling_points)
if category and has_text:
facts_status = "完整"
elif category or has_text:
facts_status = "部分"
else:
facts_status = "仅外观"
return {
"商品名称": product.title.strip(),
"品牌": product.brand.strip(),
"商品品类": product.category.strip(),
"商品品类": category,
"商品描述": product.description.strip(),
"真实卖点": selling_points,
"目标用户": product.target_audience.strip(),
"规格": product.specs or {},
"允许使用场景": (product.specs or {}).get("allowed_use_cases") or (product.specs or {}).get("使用场景") or [],
"禁止宣称": (product.specs or {}).get("prohibited_claims") or (product.specs or {}).get("禁止宣称") or [],
"资料状态": "完整" if (product.category.strip() and (product.description.strip() or selling_points)) else "不足",
"资料不足时策略": "安全展示兜底:只允许拿取、手持展示、放置、携带、包装/材质/外观特写和在桌面陈列;禁止打开、食用、佩戴、插入、涂抹、连接、充电、清洁、宣称功效或任何未确认用途。",
"资料状态": facts_status,
"资料状态的含义": "该状态只约束口播能说什么(功效、成分、卖点必须有据);不约束画面能拍什么动作。",
"形态判定": "商品的形态、部件、开合方式和使用接口以商品参考图为准,可直接据图判断,这属于可见事实,不是猜测。",
"动作策略": "同族继承优先:目标商品与参考片原商品属于同一形态族或品类族时,逐镜沿用原片的取出、开启、使用、作用部位等动作,只替换商品外观与台词事实。仅当形态确实不兼容时,才按目标商品形态重新设计动作。",
"禁止推断": "不得编造功效、成分、认证、检测结果,以及任何未在本资料中出现的卖点。",
}
@@ -409,23 +427,33 @@ def build_product_semantic_remix_messages(*, digest_text: str, product_facts: di
f"成片时长:{duration} 秒;画面比例:{aspect_ratio}。\n\n"
"【新商品的唯一事实来源】\n"
f"{facts}\n\n"
"【参考视频拆解稿,仅可继承角色关系、环境、镜头语言、剪辑节奏和叙事功能】\n"
"【参考视频拆解稿:镜头骨架、叙事结构与画面表达的基准。"
"同族判定成立时,其中的商品使用动作也在可继承范围内】\n"
f"{_digest_for_seedance(digest_text)}\n\n"
"保留参考片的人物、场景、拍摄视角、镜头构图、运镜、剪辑顺序、节奏、情绪和营销结构;将原商品完整替换为目标商品。"
"先在内部完成原商品生态、依赖道具、使用接口、动作链与逐镜兼容性检查;不要机械复刻原商品交互。"
"原镜用于取出、展示、使用或证明卖点时,保留该镜景别、运镜、时长和表达目的,"
"但仅在动作适合目标商品时保留,否则按目标商品结构、用途和卖点重新设计合理动作。"
"保留参考片的人物、场景、拍摄视角、镜头构图、运镜、剪辑顺序、节奏、情绪和营销结构;将原商品完整替换为目标商品。\n"
"【第一步·同族判定】先对比目标商品与参考片原商品的形态族(瓶、软管、按压泵、滴管、喷雾、罐、盒、"
"卡片、穿戴件、电子设备等)和品类族(护肤彩妆、食品饮料、日化清洁、数码电子、服饰鞋包、家居用品等),"
"判定属于同族还是异族,再决定每一镜怎么写:\n"
"· 判定为同族:逐镜沿用原片的取出、开启、使用方式、作用部位和动作幅度,只把商品外观和台词事实换掉。"
"原片是化妆品的挤出、按压、上脸、涂抹、试色、补妆镜时,目标商品同为化妆品就必须照着拍同样的使用动作,"
"只是换成目标商品的瓶身、质地和取用方式;绝不允许降级成静物展示。\n"
"· 判定为异族:保留该镜的景别、运镜、时长和叙事功能,按目标商品自己的形态和真实用途重新设计合理动作,"
"同样要拍成「在用」,而不是「在摆」。\n"
"所有持握、开启、穿戴、使用、安装和展示必须符合真实物理常识:不得尺寸不匹配、虚构配件、商品变形换款变色,"
"也不得生成未经商品信息支持的功能。"
"如果商品资料状态为‘不足’,不要输出 NEEDS_PRODUCT_FACTS:改用商品资料中的安全展示兜底,"
"把原片所有商品使用镜改写为不涉及具体用途的展示镜头。"
"也不得生成未经商品信息支持的功能。\n"
"【资料不全时怎么办】资料状态为‘部分’或‘仅外观’时,只收紧**口播能说什么**:不说功效、成分、认证和具体卖点,"
"改说看得见的外观、质地、取用手感和使用感受。**画面动作照旧按同族判定继承**,"
"不得因为资料不全就把使用镜改成摆拍,也不要输出 NEEDS_PRODUCT_FACTS。\n"
"【硬性禁止】禁止无理由把原片的使用镜改写成「把商品放在桌面上」「手持缓慢转动展示」「桌面陈列特写」"
"这类静物摆拍。只有目标商品的形态确实无法完成原动作时才可以改,且必须改成目标商品自己的合理使用动作。"
"成片里出现摆拍镜的数量不得超过参考片里原本就是纯展示镜的数量。\n"
"镜头数量、编号、时间区间、时长、景别、机位、运镜、切换顺序和剪辑点必须与拆解稿完全一致,绝不能增删、合并或调换镜头。"
"参考镜为画内人物对白时,成片必须由画面内人物直接开口说话并口型同步,绝不能改成旁白。"
"任何人声都不得说出新商品名称、品牌或旧商品名称,统一用‘这个’、‘它’等自然指代。"
"必须同步重写与原商品有关的口播、音效和画面内容;保留原口播的句数、时间位置、语速节奏和情绪,"
"但不得保留原商品的名称、功能和卖点。"
"最后只输出【SEEDANCE_PROMPT】后的中文分镜稿:保留【镜头 01】格式和每镜时间、时长、景别、机位、运镜、人声方式;"
"每镜的画面、人物动作、台词/旁白必须按新商品真实用途重写。"
"每镜的画面、人物动作、台词/旁白按同族判定结果处理 —— 同族沿用原动作只换商品,异族按目标商品形态重新设计动作。"
"禁止输出分析过程、分类标签或 Markdown。"
),
},
@@ -446,26 +474,34 @@ def validate_product_semantic_remix(text: str) -> str:
def build_safe_product_showcase_prompt(*, digest_text: str, product_name: str) -> str:
"""资料不足时的确定性兜底:不依赖模型猜用途,也绝不沿用原商品动作。"""
"""模型不可用时的确定性兜底:不猜功效,但**保留原片的动作结构**。
⚠️ 这里的「安全」只针对口播和功效宣称,不针对画面动作。早期版本一律写死
「拿起 → 放到桌上 → 转一转 → 特写」,导致模型一失败,化妆品复刻就退化成静物摆拍。
现在改为:沿用原镜的人物姿态、动作幅度和身体关系,由 Seedance 按参考图里
可见的商品形态做自然使用动作;只有口播被收紧到不谈功效。
"""
from .services import enforce_no_embedded_captions
header, shots = _parse_digest(digest_text)
safe_actions = (
"人物从干净桌面拿起@目标商品,保持自然手持展示。",
"人物将@目标商品平稳放在桌面上,镜头展示包装轮廓。",
"人物缓慢转动@目标商品,展示外观、包装和材质细节。",
"镜头靠近@目标商品,拍摄包装与可见结构特写。",
"人物保持原镜的姿态和站位,自然拿起@目标商品,手部动作与原镜一致。",
"人物手持@目标商品,按其在参考图中可见的形态做自然的取用动作,身体与镜头关系保持原镜。",
"人物将@目标商品移到原镜的使用位置,保持原镜的动作幅度、朝向与节奏。",
"镜头贴近人物手中的@目标商品,拍摄外观、材质与取用细节,人物动作不中断。",
)
safe_dialogues = (
"你看这个,整体的外观和质感都很清楚。",
"我把它转过来,细节可以看得更完整。",
"从这个角度看,包装和轮廓很直观。",
"镜头靠近一点,看看它的材质细节。",
"你看这个,拿在手里的质感很清楚。",
"用起来挺顺手的,细节能看得更完整。",
"从这个角度看,它的外观和手感都很直观。",
"镜头靠近一点,材质细节看得很清楚。",
)
lines = [
"【成片规则】",
"@目标商品只按参考图展示外观、包装和材质。",
"不得出现原商品、原容器、原配件、原动作、原台词、功效或任何未经确认的使用方式。",
"@目标商品的外观、包装、材质和形态以参考图为准。",
"保留原镜的人物动作结构、身体朝向、动作幅度和镜头节奏;"
"按@目标商品在参考图中可见的形态做自然使用动作,不要改成静止摆拍或桌面陈列。",
"不得出现原商品、原容器、原配件、原台词,也不得宣称任何功效、成分或未经确认的效果。",
"镜头数量、时间、时长、景别、机位、运镜和切换顺序必须与参考分镜一致;不得增删、合并、拆分或调换镜头。",
"所有人声禁止说出商品名称和品牌。参考镜中人物开口说话时,必须生成画内人物对白和同步口型,禁止改成旁白。",
]
@@ -476,7 +512,7 @@ def build_safe_product_showcase_prompt(*, digest_text: str, product_name: str) -
"",
f"【镜头 {index:02d}】",
*(f"{field}:{shot[field]}" for field in ("时间", "时长", "景别", "机位", "运镜") if shot.get(field)),
f"画面:保持原镜的环境氛围、人物关系与镜头节奏,只出现@目标商品作为展示主体。",
f"画面:保持原镜的环境氛围、人物关系、动作节奏与镜头调度,原商品换成@目标商品。",
f"人物动作:{safe_actions[(index - 1) % len(safe_actions)]}",
"人物表情:自然、专注。",
f"人声方式:{'画内人物对白' if shot.get('台词/旁白') and not _is_empty_value(shot.get('台词/旁白') or '') else '无'}",
@@ -485,8 +521,8 @@ def build_safe_product_showcase_prompt(*, digest_text: str, product_name: str) -
if not shots:
lines.extend([
"", "【镜头 01】", "时长:5秒", "景别:中近景", "机位:平视", "运镜:缓慢推近",
"画面:干净自然的环境中,@目标商品置于桌面中央。",
"人物动作:人物自然手持展示@目标商品。",
"画面:干净自然的环境中,人物手持@目标商品,保持自然的使用状态。",
"人物动作:人物按@目标商品在参考图中可见的形态做自然取用动作。",
"人物表情:自然、专注。",
"人声方式:画内人物对白。",
"台词/旁白:画内人物对白(人物对镜开口说话,非旁白):你看这个,外观和包装细节很清楚。",
@@ -701,8 +737,11 @@ def submit_video_replace(*, team, user, params: dict):
subject_name, image_refs, subject_source = _temporary_image_refs(team, image_ids, noun=noun)
product_facts = {
"商品名称": subject_name,
"资料状态": "不足",
"说明": "临时上传图片只提供外观,未提供品类、用途或真实卖点。",
"资料状态": "仅外观",
"资料状态的含义": "该状态只约束口播能说什么(不得宣称功效、成分和卖点);不约束画面能拍什么动作。",
"形态判定": "商品的形态、部件、开合方式和使用接口以临时上传的参考图为准,可直接据图判断。",
"动作策略": "同族继承优先:参考图中的商品与参考片原商品属于同一形态族或品类族时,逐镜沿用原片的取出、开启、使用、作用部位等动作,只替换商品外观。仅当形态确实不兼容时,才按参考图形态重新设计动作。",
"禁止推断": "不得编造功效、成分、认证和任何具体卖点;口播只描述看得见的外观、质地和使用感受。",
}
# 角色替换是 Seedance 视频编辑:火山要求比例/时长跟随输入视频,不能由前端指定。
@@ -871,7 +910,12 @@ def run_replace_digest(task) -> None:
prompt = rewrite_product_semantic_remix(
task=task,
digest_text=digest,
product_facts=payload.get("product_facts") or {"商品名称": subject, "资料状态": "不足"},
product_facts=payload.get("product_facts") or {
"商品名称": subject,
"资料状态": "仅外观",
"资料状态的含义": "只约束口播能说什么;不约束画面能拍什么动作。",
"动作策略": "同族继承优先:与参考片原商品同族时,逐镜沿用原片的使用动作,只替换商品外观。",
},
duration=output_seconds,
aspect_ratio=str(payload.get("aspect_ratio") or "9:16"),
)