补充品牌品名改为预填输入框且空骨架不跳步;痛点确认角色后「继续完善方案」可继续推进;点击换款要求每次落点不同;鱼眼换装改用小云雀连环换装 prompt 模板;模特详情标签自动换行。
596 lines
42 KiB
Python
596 lines
42 KiB
Python
"""全能创作 · 预设(契约 §6)。
|
||
|
||
首页的预设卡不只是个名字 —— 每个预设代表一套**明确的拍法**。
|
||
只把「达人口播种草」四个字塞进提示词,模型只能靠猜;这里给它可执行的约束。
|
||
|
||
新增预设 = 在下面加一条,前端 `omni-create.tsx` 的 PRESET 列表加一张卡。两边的
|
||
key 必须是同一个中文名(会话建的时候原样存进 CreationConversation.preset)。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
|
||
CLICK_SWAP_PRESETS = frozenset({"点击换款", "点触换款", "多色商品换款"})
|
||
_CLICK_SWAP_FINGER_GUIDANCE = (
|
||
"形态:只出手指出镜。固定机位的商品点击换款短片。必须先确认要展示的颜色/款式/SKU 及顺序,"
|
||
"画面只围绕同一件商品的逐款切换:机位、背景与灯光稳定,商品构图稳定。"
|
||
"用手指点击/轻触触发换款,接触后以干净 match cut 切到下一款。"
|
||
"每一次换款的手指点击落点必须与上一次不同(点商品的不同区域/角点),禁止写或暗示「每次点击同一位置 / 点同一点 / 手指始终点同一处」;分镜与 video_prompt 要体现「本次点击位置不同于上次」,不要复读同一落点。"
|
||
"禁止完整人物出镜、禁止生成角色定妆图、禁止改成口播/剧情/使用教程;最后才用同一构图做全款式收束。"
|
||
)
|
||
_CLICK_SWAP_CHARACTER_GUIDANCE = (
|
||
"形态:角色在日常使用场景中变色换款。锁定同一位角色与同一生活场景,角色自然使用或展示商品;"
|
||
"商品颜色/款式必须按用户确认顺序切换,人物五官、发型、身形、基础服装与场景保持一致。"
|
||
"允许自然动作与生活感,但不要改成口播带货腔、复杂剧情反转或频繁换景;结尾给出全款式收束。"
|
||
)
|
||
_CLICK_SWAP_GUIDANCE = _CLICK_SWAP_FINGER_GUIDANCE # 未选形态前的默认提示
|
||
|
||
VIDEO_PRESETS: dict[str, str] = {
|
||
"痛点解决演示": (
|
||
"真实问题解决型短片。按「具体痛点 → 商品自然登场 → 正常使用步骤 → 可见结果 → 轻行动引导」推进。"
|
||
"痛点必须来自用户提供的商品用途;使用方法、效果与结果只写能被素材或商品事实证明的内容,禁止夸大功效或虚构测试。"
|
||
),
|
||
"真实使用演示": (
|
||
"可信实拍型商品短片。先按商品真实用途、可见结构和已有卖点安排演示,再谈氛围和转化;"
|
||
"每一个卖点都必须由看得见的使用步骤、材质细节或结果承接,不能凭空加性能。"
|
||
"商品始终保持正常、完好的使用状态:禁止无依据拍成破损、漏液、渗水、变形、脏污或失效,"
|
||
"也禁止拿商品做其用途和已知能力之外的压力测试。"
|
||
"例如垃圾袋默认呈现铺入垃圾桶、投放干燥日常垃圾、提起与扎口收纳;"
|
||
"没有明确的防漏/承重/容量事实时,不用装水、尖锐物或超载来证明性能。"
|
||
"用户没给关键功能或使用边界时,只问一次缺失事实;其余镜头用可观察的正常动作补齐。"
|
||
),
|
||
"剧情反转带货": (
|
||
"剧情反转带货不是给商品套一个故事壳。商品必须作为解决困境、解除误会、证明事实、完成翻盘或回收伏笔的关键物,"
|
||
"并在最终 Prompt、镜头、动作、对白和反转结果中反复可见地承担这个作用。"
|
||
"不得在故事结束后突然停下来介绍商品,也不能只在最后两秒放商品图。"
|
||
"必须先确定故事深度,再按所选 15 秒、30 秒或 60 秒的结构写人物关系、冲突铺垫、商品介入时机、反转和情绪落点;"
|
||
"禁止把同一条 15 秒故事机械加长或缩短。"
|
||
),
|
||
"商品拟人广告": (
|
||
"把商品当成有性格的角色,但默认采用无脸拟人:商品本体保持真实完整,不在瓶身、包装或机身上新增卡通五官。"
|
||
"性格通过整个商品轻微倾斜、转向、弹跳、进退,配合镜头、光影、环境反应和音效来表达。"
|
||
"商品需要说台词时使用画外角色声,商品本体不做口型。整体轻快有趣、精致克制,不是儿童贴纸玩具风。"
|
||
"只有用户明确说要可见的卡通五官时才采用有脸拟人。商品材质、颜色、结构和包装必须与参考图一致。"
|
||
),
|
||
"达人口播种草": (
|
||
"真人出镜口播,生活化语气,像朋友分享而不是念广告稿。开头一句话建立停留理由,"
|
||
"中段讲清使用场景和一个核心卖点,结尾给明确行动理由。"
|
||
"只有一位人物时全片锁定同一人;用户已添加多位角色时保留全部,并固定共同出镜或主讲+辅助关系。"
|
||
"禁止万能主播腔和空卖点。"
|
||
),
|
||
"商品图一键成片": (
|
||
"从商品参考图出发建立镜头语言:主图定调 → 补场景 → 补使用动作 → 收尾。"
|
||
"节奏清晰,转场干净。商品外观严格以参考图为准。"
|
||
),
|
||
"鱼眼换装": (
|
||
"超广角鱼眼/近距离透视的连环换装短片。"
|
||
"全片单一机位、同一构图与背景锁定;画面中心人物清晰、四边缘畸变拉伸,高饱和时尚质感,逐拍卡点剪辑。"
|
||
"人物从第一镜起就已经穿着当前套装;换装发生在遮挡/甩头/甩镜等动态瞬间,瞬间切到已穿好的下一套。"
|
||
"禁止拍穿衣服、套袖、拉拉链等穿戴过程。video_prompt 必须按「鱼眼连环换装」制作模板撰写"
|
||
"(时长比例、标题、风格、单机位节拍、转场规则、主体、服装状态机、无对白卡点音效)。"
|
||
),
|
||
"点击换款": _CLICK_SWAP_GUIDANCE,
|
||
"多色商品换款": _CLICK_SWAP_GUIDANCE,
|
||
# 旧会话仍会保存「点触换款」,保留同一拍法约束以支持历史继续创作。
|
||
"点触换款": _CLICK_SWAP_GUIDANCE,
|
||
"探店漫游": (
|
||
"以空间动线串联:入口 → 环境 → 关键细节 → 服务/主推项目。"
|
||
"镜头连续移动有路线感,不要碎切。"
|
||
),
|
||
"品牌质感大片": (
|
||
"强调光影、材质和镜头节奏,建立品牌识别。慢节奏、精致构图、克制的色彩。"
|
||
"少即是多,不要堆信息。"
|
||
),
|
||
"前后对比实测": (
|
||
"围绕同一对象和同一可比较条件,依次拍清使用前状态、正常使用过程、使用后状态与可见对比。"
|
||
"优先使用用户提供的前/中/后素材;没有事实依据时不承诺功效、不制造夸张差异。"
|
||
),
|
||
"AI 宠物拟人": (
|
||
"先确定宠物的外观、性格和可执行动作,再设计一段轻巧剧情。宠物与商品互动要符合商品真实结构和正常操作方式;"
|
||
"趣味来自角色反应和剧情,不把商品改造成不真实的玩具或凭空增加能力。"
|
||
"有台词时必须是宠物第一人称开口(像这只宠物在说话),口语、性格化、短句;"
|
||
"禁止旁白解说、达人口播腔、画外音旁白或第三人称讲述。"
|
||
),
|
||
"人物与音色替换": (
|
||
"先理解原视频的镜头、人物、动作、台词、画外音、字幕、场景和节奏。只替换用户指定的人物、声音或两者;"
|
||
"其他构图、运镜、动作、商品、场景和剪辑节奏尽量保持原样。用户未提供原视频或替换目标时,先问这一个必要信息。"
|
||
),
|
||
}
|
||
|
||
|
||
PLOT_TWIST_PRESET = "剧情反转带货"
|
||
|
||
# 值同时是会话记忆中的稳定标识;label 则是用户在对话里看到的短说明。
|
||
PLOT_TWIST_STORY_DEPTH_OPTIONS = (
|
||
{
|
||
"value": "15s",
|
||
"label": "15秒|快节奏反转",
|
||
"duration": "15 秒",
|
||
"summary": "强冲突快速抓人,商品尽早介入并完成反转。",
|
||
},
|
||
{
|
||
"value": "30s",
|
||
"label": "30秒|轻剧情带货",
|
||
"duration": "30 秒",
|
||
"summary": "补足人物处境和矛盾铺垫,让商品在转折点自然发挥作用。",
|
||
},
|
||
{
|
||
"value": "60s",
|
||
"label": "60秒|完整短剧带货",
|
||
"duration": "60 秒",
|
||
"summary": "建立人物关系与持续矛盾,商品作为伏笔或关键线索完成翻盘。",
|
||
},
|
||
{
|
||
"value": "smart",
|
||
"label": "智能推荐",
|
||
"duration": "",
|
||
"summary": "结合商品卖点、素材和剧情空间推荐时长,仍由你最终决定。",
|
||
},
|
||
)
|
||
|
||
_PLOT_TWIST_DEPTH_BY_VALUE = {item["value"]: item for item in PLOT_TWIST_STORY_DEPTH_OPTIONS}
|
||
|
||
|
||
def plot_twist_story_depth(value: str) -> dict | None:
|
||
"""兼容卡片 value、展示文案和自然语言里带的秒数。
|
||
|
||
180 秒长视频入口已临时关闭:历史「180s」选择落到 60 秒档。
|
||
"""
|
||
raw = str(value or "").strip()
|
||
if raw in _PLOT_TWIST_DEPTH_BY_VALUE:
|
||
return _PLOT_TWIST_DEPTH_BY_VALUE[raw]
|
||
if "180" in raw or "90" in raw or "120" in raw:
|
||
return _PLOT_TWIST_DEPTH_BY_VALUE["60s"]
|
||
if "60" in raw:
|
||
return _PLOT_TWIST_DEPTH_BY_VALUE["60s"]
|
||
if "30" in raw:
|
||
return _PLOT_TWIST_DEPTH_BY_VALUE["30s"]
|
||
if "15" in raw:
|
||
return _PLOT_TWIST_DEPTH_BY_VALUE["15s"]
|
||
if "智能" in raw or "推荐" in raw:
|
||
return _PLOT_TWIST_DEPTH_BY_VALUE["smart"]
|
||
return None
|
||
|
||
|
||
def plot_twist_story_contract(value: str) -> str:
|
||
"""真实写入出片 Prompt 的硬约束,确保预设不会退化成一行风格词。"""
|
||
depth = plot_twist_story_depth(value)
|
||
if depth is None:
|
||
return ""
|
||
if depth["value"] == "15s":
|
||
return (
|
||
"【剧情反转带货·15秒快节奏反转·强制执行】只保留一条主冲突,主角 1 人、最多 1 名辅助人物。"
|
||
"0-3 秒强冲突或意外,3-6 秒问题升级,6-11 秒商品以正常使用动作介入并证明一个核心卖点,"
|
||
"11-15 秒完成结果反转与自然行动引导。商品必须在中段前出现;对白短、直接、有记忆点;"
|
||
"禁止复杂背景、支线、重复卖点和与商品无关的镜头。"
|
||
)
|
||
if depth["value"] == "30s":
|
||
return (
|
||
"【剧情反转带货·30秒轻剧情带货·强制执行】必须交代人物处境,可设置两人互动。"
|
||
"0-4 秒抛出结果预告或冲突钩子,4-10 秒建立处境,10-17 秒让矛盾升级或一次错误尝试,"
|
||
"17-24 秒商品在转折点以完整正常使用过程介入,24-28 秒回收反转与人物反应,28-30 秒自然收束。"
|
||
"商品承担改变局面的实际作用;反转既服务剧情也证明卖点,禁止为了凑时长重复同一卖点。"
|
||
)
|
||
if depth["value"] == "60s":
|
||
return (
|
||
"【剧情反转带货·60秒完整短剧带货·强制执行】必须有人物关系、持续发展的矛盾、一次铺垫和一次回收。"
|
||
"0-5 秒高强度钩子,5-15 秒交代关系和处境,15-28 秒矛盾逐步升级,28-38 秒主角面临选择或第一次失败,"
|
||
"38-48 秒商品作为关键线索、工具、证据或关系转折点推进剧情,48-56 秒完成主要反转和情绪释放,"
|
||
"56-60 秒回扣商品价值并自然转化。商品可前置为伏笔但必须在后半段真正改变结局;"
|
||
"禁止用重复对白、无意义空镜或硬插卖点填满时长。"
|
||
)
|
||
if depth["value"] == "180s":
|
||
return (
|
||
"【剧情反转带货·180秒三幕完整故事·强制执行】全片按三个连续 60 秒章节推进,不能把三条短片简单拼接。"
|
||
"第一章 0-60 秒:0-8 秒以结果预告或关系冲突抓人,8-28 秒建立人物目标与现实阻力,"
|
||
"28-48 秒埋入商品/SKU 与一个可见卖点证据,48-60 秒用第一次选择或失败把行动推入下一章。"
|
||
"第二章 60-120 秒:承接上一章未完成动作,扩大矛盾并安排一次错误尝试;商品通过正常使用过程提供新的证据,"
|
||
"在 105-120 秒完成中段转折,但不得提前总结或重复开场。"
|
||
"第三章 120-180 秒:让前两章伏笔、人物选择和商品证据共同触发主要反转,165 秒前完成结果验证,"
|
||
"165-176 秒释放人物情绪并回扣核心卖点,176-180 秒只做一次自然行动引导。"
|
||
"每章都要推进新的因果关系;同一角色、服装、商品/SKU、场景空间和光线跨六个 30 秒分段连续,"
|
||
"禁止重复对白、重复卖点、空镜凑时长或在章节边界换人换商品。"
|
||
)
|
||
return (
|
||
"【剧情反转带货·智能推荐】先根据商品卖点、已有素材和剧情空间推荐 15、30 或 60 秒之一并说明理由;"
|
||
"随后必须让用户选择实际时长,未确认前不得写剧情方向、策略、方案或出片指令。"
|
||
)
|
||
|
||
|
||
def apply_plot_twist_story_contract(name: str, story_depth: str, prompt: str) -> str:
|
||
"""在最终出片指令里确定性追加所选故事深度,避免模型漏写关键结构。"""
|
||
base = str(prompt or "").strip()
|
||
if (name or "").strip() != PLOT_TWIST_PRESET or not base:
|
||
return base
|
||
contract = plot_twist_story_contract(story_depth)
|
||
if not contract or contract in base:
|
||
return base
|
||
return f"{base}\n\n{contract}"
|
||
|
||
|
||
def format_plot_twist_direction_contract(
|
||
*,
|
||
title: str = "",
|
||
conflict: str = "",
|
||
product_role: str = "",
|
||
reversal: str = "",
|
||
tone: str = "",
|
||
detail: str = "",
|
||
) -> str:
|
||
"""把用户选定的剧情方向写成出片/方案硬约束。"""
|
||
title = str(title or "").strip()
|
||
conflict = str(conflict or "").strip()
|
||
product_role = str(product_role or "").strip()
|
||
reversal = str(reversal or "").strip()
|
||
tone = str(tone or "").strip()
|
||
detail = str(detail or "").strip()
|
||
if not title and not detail and not any((conflict, product_role, reversal)):
|
||
return ""
|
||
lines = ["【剧情反转方向·强制执行·最高优先级】"]
|
||
if title:
|
||
lines.append(f"已选方向标题:{title}")
|
||
if conflict:
|
||
lines.append(f"开场冲突必须是:{conflict}")
|
||
if product_role:
|
||
lines.append(f"商品在剧情中的作用必须是:{product_role}")
|
||
if reversal:
|
||
lines.append(f"最终反转必须是:{reversal}")
|
||
if tone:
|
||
lines.append(f"情绪调性:{tone}")
|
||
if detail and not any((conflict, product_role, reversal)):
|
||
lines.append(f"方向细节:{detail}")
|
||
lines.append(
|
||
"策略、方案时间轴和 video_prompt 必须严格沿用上述冲突→商品作用→反转;"
|
||
"禁止改成另一套常见带货故事(例如擅自换成「出门忘了喂宠/忘记带东西」等默认桥段),"
|
||
"除非用户所选方向本身就是该桥段。"
|
||
)
|
||
return "\n".join(lines)
|
||
|
||
|
||
def apply_plot_twist_direction_contract(prompt: str, *, title: str = "", conflict: str = "", product_role: str = "", reversal: str = "", tone: str = "", detail: str = "") -> str:
|
||
"""确定性把已选剧情方向写入方案/出片 prompt,避免模型另起炉灶。"""
|
||
base = str(prompt or "").strip()
|
||
contract = format_plot_twist_direction_contract(
|
||
title=title,
|
||
conflict=conflict,
|
||
product_role=product_role,
|
||
reversal=reversal,
|
||
tone=tone,
|
||
detail=detail,
|
||
)
|
||
if not base or not contract:
|
||
return base
|
||
if "【剧情反转方向·强制执行·最高优先级】" in base:
|
||
return base
|
||
return f"{base}\n\n{contract}"
|
||
|
||
|
||
# 预设除了决定最终 Prompt,也决定 Agent 在素材整理、追问与生成前检查时的工作重点。
|
||
# 这段进入系统提示词,避免把预设退化成一句风格修饰词。
|
||
VIDEO_PRESET_WORKFLOWS: dict[str, str] = {
|
||
"痛点解决演示": "先确认商品真实解决的具体问题与正常用法;生成前核对痛点、过程和结果都有可见证据。",
|
||
"真实使用演示": "优先核对商品真实用途、关键步骤和可证实卖点;不为氛围而增加不合理测试。",
|
||
"剧情反转带货": "先让用户选故事深度(15秒快节奏反转、30秒轻剧情带货、60秒完整短剧带货或智能推荐;暂不开放更长时长),再给三个与时长匹配的剧情方向;商品必须成为解决问题、解除误会、证明事实、回收伏笔或完成翻盘的关键。",
|
||
"商品拟人广告": "先确认商品外观和性格表达方式;默认无脸拟人,商品保持真实完整,台词用画外声。",
|
||
"达人口播种草": "优先确认人物、多人出镜关系、真实体验和主卖点;生成前核对口播字数能在时长内说完,每个卖点都有画面证明。",
|
||
"商品图一键成片": "优先从商品参考图锁定外观;自动补场景和动作,但不替换或改变用户商品图里的结构、颜色和包装。",
|
||
"鱼眼换装": "优先确认人物参考、服装套数N与展示顺序、本条机位(低机位仰拍/高机位俯拍/荷兰角/贴地滑行/手持环绕等择一全片不变)、场景与转场方式;时长按 N×1.5–2.5 秒估算(约 4–15 秒);write_prompt 严格用「鱼眼连环换装」模板:节拍换装 + 服装状态机,无对白,禁止穿衣过程。",
|
||
"点击换款": "先让用户确认颜色/款式/SKU 和切换顺序;脚本只能是固定机位 + 手指点击触发换款;每次换款点击位置必须不同(换商品不同区域),禁止「每次点击同一位置 / 点同一点」。禁止转成剧情或口播。",
|
||
"多色商品换款": "先让用户确认颜色/款式/SKU 和切换顺序;脚本只能是固定机位 + 手指点击触发换款;每次换款点击位置必须不同(换商品不同区域),禁止「每次点击同一位置 / 点同一点」。禁止转成剧情或口播。",
|
||
"点触换款": "先让用户确认颜色/款式/SKU 和切换顺序;脚本只能是固定机位 + 手指点击触发换款;每次换款点击位置必须不同(换商品不同区域),禁止「每次点击同一位置 / 点同一点」。禁止转成剧情或口播。",
|
||
"探店漫游": "优先确认门店动线与主推项目;用连续移动串联入口、环境、细节和服务,不做碎片化硬切。",
|
||
"品牌质感大片": "优先确认品牌气质、材质和商品主卖点;由 Agent 决定光线和镜头,不把专业选择反复抛给用户。",
|
||
"前后对比实测": "优先确认同一对象的前、中、后素材或可比较条件;生成前核对对比不夸大且没有缺失关键阶段。",
|
||
"AI 宠物拟人": "优先确认宠物角色参考、性格和商品互动方式;台词必须是宠物自己说的第一人称,禁止旁白/口播/画外音感;生成前核对宠物、商品结构和使用动作前后一致。",
|
||
"人物与音色替换": "先确认原视频、替换对象和替换范围;未经要求不得改变原视频的构图、动作、商品、场景或节奏。",
|
||
}
|
||
|
||
# 这一层不是给聊天看的装饰文字,而是实际写入 video_prompt 的制作约束。
|
||
# 预设示例片的效果主要来自结构、镜头和素材处理方式,不能只追加一句风格名。
|
||
VIDEO_PRESET_DELIVERY_CONTRACTS: dict[str, str] = {
|
||
"痛点解决演示": (
|
||
"【预设执行层·痛点解决演示】全片严格按‘真实困扰 → 商品进入 → 正常使用过程 → 可见结果 → 自然收束’推进。"
|
||
"开头先让目标用户看懂具体困扰;中段完整拍清产品的真实操作和一个可观察证据;"
|
||
"结尾只回扣已经被画面证明的价值。不得虚构压力测试、绝对功效、价格或促销。"
|
||
),
|
||
"真实使用演示": (
|
||
"【预设执行层·真实使用演示】像一镜到底可复核的日常实拍:先交代真实场景,再按正确用途完成步骤,"
|
||
"用近景展示材质、动作或结果。商品全程保持正常完整;不以破损、漏液、渗水、超载或超出用途的测试制造戏剧性。"
|
||
),
|
||
"剧情反转带货": (
|
||
"【预设执行层·剧情反转带货】人物关系、冲突、情绪变化和反转必须在每个关键镜头连续推进。"
|
||
"商品不是摆拍道具:必须充当解决问题的工具、解除误会的证据、关系变化的礼物或回收伏笔的关键物;"
|
||
"商品出现、使用和结果必须直接改变剧情走向,禁止剧情结束后再硬插卖点。"
|
||
),
|
||
"商品拟人广告": (
|
||
"【预设执行层·商品拟人广告】商品本体保持真实包装、颜色、材质和结构;默认不长五官、不做口型。"
|
||
"性格由整体运动、镜头、环境反应和画外角色声表达。每次拟人动作都要符合物理状态,趣味服务于一个真实卖点。"
|
||
),
|
||
"达人口播种草": (
|
||
"【预设执行层·达人口播种草】固定已经确认的人物与出镜关系:只有一位人物时全片锁定同一位可信真人;"
|
||
"已添加多位角色时保留全部角色,主讲身份与辅助角色不得在镜头间互换。真实生活场景中自然口播。"
|
||
"结构为:前 2–3 秒具体痛点或体验钩子 → 真实使用/质地/细节证明 → 一句个人感受 → 克制收束。"
|
||
"口播像朋友分享,所有卖点必须被动作或特写佐证;禁止万能主播腔、堆砌卖点、画面字幕和夸张承诺。"
|
||
),
|
||
"商品图一键成片": (
|
||
"【预设执行层·商品图一键成片】以用户商品图作为外观唯一事实源:先用主图确立商品,再补与真实用途匹配的场景、手部动作和细节特写,"
|
||
"最后以干净的产品收束。包装文字、颜色、比例和结构全程稳定,不能用 AI 生成的相似商品替代参考图。"
|
||
),
|
||
"鱼眼换装": (
|
||
"【预设执行层·鱼眼换装·小云雀模板】生成时尚换装短视频。"
|
||
"比例默认 9:16;时长按服装套数 N 估算(每套约 1.5–2.5 秒,通常 4–15 秒)。"
|
||
"风格:超广角鱼眼,中心清晰、四边缘明显畸变拉伸,高饱和,时尚质感,逐拍卡点剪辑。"
|
||
"镜头:全片单一机位(本条自选一种并整条不变:低机位仰拍 / 高机位俯拍 / 荷兰角倾斜 / 贴地滑行 / 手持环绕);"
|
||
"0–1 秒该机位对准主体、第 1 套造型定格 pose;之后逐拍换装,机位始终不变,仅服装与动作逐拍更新;最后一拍同机位定格收尾。"
|
||
"转场:每次换装发生在遮挡/甩镜动态瞬间(抬手遮挡镜头 / 甩头转身划过画面 / 镜头快速甩动出虚影);"
|
||
"转场瞬间服装切换,机位保持原位,下一拍回到同一构图。"
|
||
"主体:只采用参考图五官、发型、妆容、姿态,不采用参考图服装与背景。"
|
||
"服装状态机:S0 造型1 → 转场动作 → S1 造型2 → … → SN 定格收尾;"
|
||
"全过程人物身份/体型/发型一致,机位/构图/背景锁定,仅服装逐拍变化;人物始终已穿好当前套,禁止穿戴过程镜头。"
|
||
"声音:无对白,预留卡点 BGM,每次转场配快门/whoosh 音效。"
|
||
),
|
||
"点击换款": (
|
||
"【预设执行层·点击换款·强制】这不是口播片、剧情片或普通商品展示片。"
|
||
"使用单一固定机位和同一背景,商品构图、尺寸与角度保持稳定。"
|
||
"换款由手指轻触/点击触发,接触后以干净 match cut 切到下一款;"
|
||
"每一次换款的手指点击落点必须与上一次不同(点商品的不同区域/角点),禁止写或暗示「每次点击同一位置 / 点同一点 / 手指始终点同一处」;分镜与 video_prompt 要体现「本次点击位置不同于上次」,不要复读同一落点。"
|
||
"不改场景、不改机位、不运镜、不说话、不出现完整人物、不演剧情、不演示使用步骤。"
|
||
"各款必须按用户确认的顺序逐一出现,不自行发明颜色或款式;结尾停在干净的全款式总览。"
|
||
),
|
||
"多色商品换款": (
|
||
"【预设执行层·点击换款·强制】这不是口播片、剧情片或普通商品展示片。"
|
||
"使用单一固定机位和同一背景,商品构图、尺寸与角度保持稳定。"
|
||
"换款由手指轻触/点击触发,接触后以干净 match cut 切到下一款;"
|
||
"每一次换款的手指点击落点必须与上一次不同(点商品的不同区域/角点),禁止写或暗示「每次点击同一位置 / 点同一点 / 手指始终点同一处」;分镜与 video_prompt 要体现「本次点击位置不同于上次」,不要复读同一落点。"
|
||
"不改场景、不改机位、不运镜、不说话、不出现完整人物、不演剧情、不演示使用步骤。"
|
||
"各款必须按用户确认的顺序逐一出现,不自行发明颜色或款式;结尾停在干净的全款式总览。"
|
||
),
|
||
"点触换款": (
|
||
"【预设执行层·点击换款·强制】这不是口播片、剧情片或普通商品展示片。"
|
||
"使用单一固定机位和同一背景,商品构图、尺寸与角度保持稳定。"
|
||
"换款由手指轻触/点击触发,接触后以干净 match cut 切到下一款;"
|
||
"每一次换款的手指点击落点必须与上一次不同(点商品的不同区域/角点),禁止写或暗示「每次点击同一位置 / 点同一点 / 手指始终点同一处」;分镜与 video_prompt 要体现「本次点击位置不同于上次」,不要复读同一落点。"
|
||
"不改场景、不改机位、不运镜、不说话、不出现完整人物、不演剧情、不演示使用步骤。"
|
||
"各款必须按用户确认的顺序逐一出现,不自行发明颜色或款式;结尾停在干净的全款式总览。"
|
||
),
|
||
"探店漫游": (
|
||
"【预设执行层·探店漫游】用一条连续空间动线讲清入口、环境、关键细节和主推服务/商品。"
|
||
"镜头移动要有明确去向,人物或旁白承担导览;环境、服务和商品在同一体验中自然出现,不做碎片化硬切或虚假热闹。"
|
||
),
|
||
"品牌质感大片": (
|
||
"【预设执行层·品牌质感大片】少信息、高控制:围绕一个商品主角,以材质、光线、留白和镜头节奏建立品牌气质。"
|
||
"用真实可见的表面、边缘光、环境反射和干净构图表达高级感;禁止无意义的漂浮特效、过度 CG 和堆叠卖点。"
|
||
),
|
||
"前后对比实测": (
|
||
"【预设执行层·前后对比实测】同一对象、同一条件、同一机位依次呈现使用前、正常使用过程和使用后。"
|
||
"对比必须由用户素材或可见事实支持,画面明确说明比较对象;禁止伪造夸张效果、偷换条件或把未验证功效拍成结论。"
|
||
),
|
||
"AI 宠物拟人": (
|
||
"【预设执行层·AI 宠物拟人】宠物外观、体型、毛色和性格前后一致;趣味来自拟人动作与真实反应。"
|
||
"宠物和商品的互动必须符合商品真实结构与正常用法,商品不变成玩具或获得不存在的能力;剧情里至少有一个可见卖点证据。"
|
||
"人声必须是宠物角色第一人称台词(我/本汪/本喵等贴合物种的口吻),像宠物对着镜头或商品说话;"
|
||
"禁止写成旁白解说、达人口播、画外音讲述或「今天给大家介绍…」类带货腔。"
|
||
),
|
||
"人物与音色替换": (
|
||
"【预设执行层·人物与音色替换】严格保留原视频的构图、镜头顺序、动作、节奏、商品、场景和剪辑;"
|
||
"只替换用户明确指定的人物、声音或两者。替换人物的脸、身形、服装和新声音要全片一致,未授权内容一律不改。"
|
||
),
|
||
}
|
||
|
||
IMAGE_PRESETS: dict[str, str] = {
|
||
"电影感": "电影级摄影质感,宽容度高,强烈但自然的明暗层次,深邃阴影,柔和高光,冷暖色彩对比,轻微青橙调,真实环境光,局部轮廓光,适度暗角,细腻胶片颗粒,低饱和高级色调,电影调色,富有叙事感,真实摄影质感,避免过度HDR。",
|
||
"日式清新": "日系清新摄影,高明度,低对比,柔和自然光,干净通透的空气感,白色与浅色占比高,低饱和淡彩,轻微偏冷色温,柔和肤色与高光,阴影浅淡,画面轻盈,曝光稍高但保留细节,清透自然,简洁治愈。",
|
||
"复古胶片": "复古胶片摄影,模拟35mm胶片质感,暖棕与米黄色调,轻微褪色,黑位微微抬起,低对比度,柔和高光,细腻胶片颗粒,轻微色偏,适度暗角,少量灰尘与划痕,真实模拟胶片扫描质感,怀旧而自然,避免强烈漏光与大面积炫光。",
|
||
"暖调生活感": "温暖生活方式摄影,自然暖阳,柔和金黄色调,真实环境光,低至中等对比,温暖肤色,柔和阴影,轻微逆光,舒适自然的曝光,细腻质感,生活气息浓厚,真实、不刻意、治愈温馨,轻微暖色电影调色。",
|
||
"高质感杂志": "高级时尚杂志摄影,编辑大片质感,中低饱和,高级灰色调,精准曝光,硬朗且具有雕塑感的光影,深邃阴影,清晰轮廓,高级商业摄影质感,细节锐利但不过度锐化,极简色彩控制,克制、高级、时尚、冷静。",
|
||
"梦幻柔光": "梦幻柔光摄影,低对比度,柔焦效果,柔和高光扩散,轻微Bloom光晕,浅色粉彩色调,柔和暖白曝光,低饱和,空气感与雾化感,细腻柔软的光线,背景高光形成柔美散景,浪漫、轻盈、唯美,保留主体细节。",
|
||
"黑金高级": "黑金奢华商业摄影,深黑背景,金色高光,低调曝光,高对比度,强烈明暗塑形,精准轮廓光,金属反射质感,高级镜面反射,深邃黑位,暖金色点缀,低饱和、精致、克制、奢华,高端广告摄影质感。",
|
||
"冷峻科技": "现代科技商业摄影,冷灰蓝色调,低饱和,高级冷色温,干净硬朗的光线,精准边缘光,金属与玻璃质感清晰,适度高对比,深灰阴影,简洁现代,锐利清晰但不过度数字化,真实产品摄影质感,理性、专业、克制,避免科幻全息、机器人和过度AI视觉元素。",
|
||
"赛博朋克": "赛博朋克摄影风格,蓝紫与洋红霓虹色调,高饱和霓虹光,强烈冷暖色彩碰撞,高对比,深黑阴影,彩色轮廓光,霓虹反射,局部RGB光效,未来都市视觉语言,强烈视觉冲击,暗部层次丰富,真实摄影质感,科技感与潮流感结合,避免雨天效果。",
|
||
"日系手绘动画": "经典日系手绘动画电影质感,柔和自然配色,清新青绿与天空蓝色调,温暖阳光,细腻手绘线条,水彩与赛璐璐结合质感,柔和阴影,自然光影,丰富植物细节,轻微纸张与颜料肌理,童话般自然环境,宁静治愈,清新、温暖、诗意、生活感,具有传统二维动画电影质感,避免3D塑料感、过度数字化和强烈锐化。",
|
||
"国风水墨": "中国传统水墨画风格,黑白灰墨色体系,淡青与赭石少量点染,水墨晕染,浓淡干湿变化,焦墨、淡墨与泼墨结合,宣纸自然肌理,山水皴法,虚实相生,大面积留白,云雾朦胧,柔和层次,写意而不过度抽象,空灵、含蓄、诗意、东方禅意,强调墨韵与意境,避免高饱和、现代摄影感和西式油画质感。",
|
||
"新中式": "现代新中式东方美学,传统中国元素与现代极简设计融合,黑白灰、原木色、墨绿与低饱和暖色体系,大面积留白,克制高级配色,简洁几何构图,中式格栅、山石、竹、松、水景等东方元素适度点缀,柔和自然光,细腻材质,现代空间感与传统意境结合,宋式审美,安静、雅致、克制、高级,商业视觉质感,避免大红大金、复杂雕花和俗艳传统元素。",
|
||
}
|
||
|
||
# 来源于「图片创作预设.docx」的统一生成收束。用户界面只展示预设标题;
|
||
# 风格文字和负面约束只在真正提交出图时拼进模型 Prompt。
|
||
IMAGE_PRESET_QUALITY_SUFFIX = "photorealistic, professional photography, realistic lighting, natural texture, high detail"
|
||
IMAGE_PRESET_NEGATIVE_CONSTRAINTS = "避免过度HDR、过度锐化、塑料质感、廉价AI感、异常高饱和、过曝、死黑阴影、过强光晕、明显CG感、过度磨皮。"
|
||
|
||
ALL_PRESETS: dict[str, str] = {**VIDEO_PRESETS, **IMAGE_PRESETS}
|
||
|
||
|
||
def preset_guidance(name: str) -> str:
|
||
"""预设名 → 拍法约束。认不出的名字返回 "" —— 前端加了新卡但这里还没写时,
|
||
退回「只有名字」的行为,不要报错。"""
|
||
return ALL_PRESETS.get((name or "").strip(), "")
|
||
|
||
|
||
def preset_workflow_guidance(name: str) -> str:
|
||
"""预设名 → Agent 的素材、追问和检查重点。"""
|
||
return VIDEO_PRESET_WORKFLOWS.get((name or "").strip(), "")
|
||
|
||
|
||
_CLICK_SWAP_FINGER_CONTRACT = (
|
||
"【预设执行层·点击换款·只出手】这不是口播片、剧情片或普通商品展示片。"
|
||
"使用单一固定机位和同一背景,商品构图、尺寸与角度保持稳定。"
|
||
"换款由手指轻触/点击触发,接触后以干净 match cut 切到下一款;"
|
||
"每一次换款的手指点击落点必须与上一次不同(点商品的不同区域/角点),"
|
||
"禁止写或暗示「每次点击同一位置 / 点同一点 / 手指始终点同一处」;"
|
||
"分镜与 video_prompt 要体现「本次点击位置不同于上次」。"
|
||
"不改场景、不改机位、不运镜、不说话、不出现完整人物、不演剧情、不演示使用步骤、不生成角色定妆图。"
|
||
"各款必须按用户确认的顺序逐一出现,不自行发明颜色或款式;结尾停在干净的全款式总览。"
|
||
)
|
||
_CLICK_SWAP_CHARACTER_CONTRACT = (
|
||
"【预设执行层·点击换款·角色日常换款】锁定同一位角色与同一日常使用场景。"
|
||
"角色在自然生活动作中展示/使用商品,商品颜色或款式按用户确认顺序切换;"
|
||
"人物五官、发型、年龄感、身形与基础服装全程一致,场景与光线保持连贯。"
|
||
"允许生活感动作,禁止口播带货腔、复杂剧情反转、频繁换景或随机换人;结尾给出全款式收束。"
|
||
)
|
||
|
||
|
||
def video_preset_delivery_contract(name: str, *, click_swap_mode: str = "") -> str:
|
||
"""预设名 → 最终视频生成阶段必须执行的结构与镜头约束。"""
|
||
preset = (name or "").strip()
|
||
if preset in CLICK_SWAP_PRESETS:
|
||
if (click_swap_mode or "").strip() == "character":
|
||
return _CLICK_SWAP_CHARACTER_CONTRACT
|
||
return _CLICK_SWAP_FINGER_CONTRACT
|
||
return VIDEO_PRESET_DELIVERY_CONTRACTS.get(preset, "")
|
||
|
||
|
||
|
||
FISH_EYE_OUTFIT_PRESET = "鱼眼换装"
|
||
|
||
_FISH_EYE_OUTFIT_PROMPT_TEMPLATE = """
|
||
【鱼眼连环换装 · video_prompt 强制模板(来自小云雀写法)】
|
||
当预设为「鱼眼换装」时,write_plan / write_prompt 的 video_prompt 必须按下面骨架填写,
|
||
用会话里真实的人物、服装套数、机位、场景与顺序替换【】占位;不要改回通用「制作级分镜四行」长文,也不要编口播。
|
||
|
||
时长:【4-15秒,按N推算,每套服装约1.5-2.5秒】秒
|
||
比例:9:16
|
||
任务:生成时尚换装短视频。
|
||
|
||
标题:《鱼眼连环换装·【机位名】版》
|
||
|
||
风格与视觉参考:
|
||
超广角鱼眼镜头风格,画面中心人物清晰、四边缘明显畸变拉伸,
|
||
高饱和色彩,时尚质感,逐拍卡点剪辑。
|
||
|
||
镜头语言:
|
||
全片单一机位:【本条视频机位描述,如:低机位仰拍 / 高机位俯拍 /
|
||
荷兰角倾斜 / 贴地滑行 / 手持环绕——每条视频自选一种,整条不变】
|
||
0-1秒:该机位对准【主体】,第1套造型定格pose
|
||
之后逐拍换装,机位始终不变,仅服装与动作逐拍更新:
|
||
节拍1:造型1,随节拍做动作
|
||
节拍2:造型2,随节拍做动作
|
||
节拍3:造型3,随节拍做动作
|
||
……按N套延伸
|
||
最后一拍:同机位稳定构图,定格收尾pose
|
||
|
||
转场规则:每次换装发生在遮挡/甩镜的动态瞬间——
|
||
【转场方式:抬手遮挡镜头 / 甩头转身划过画面 / 镜头快速甩动出虚影,
|
||
转场瞬间服装切换,机位保持原位,下一拍回到同一构图】
|
||
|
||
主体:
|
||
主体1:【人物描述】,对应@图片1
|
||
(只采用五官、发型、妆容、姿态,不采用服装与背景)
|
||
|
||
服装状态机:
|
||
S0 造型1:【服装1描述】
|
||
→ 动作:【转场方式】
|
||
→ S1 造型2:【服装2描述】
|
||
→ 动作:【转场方式】
|
||
→ ……按N套延伸
|
||
→ SN 造型N:【服装N描述】定格收尾
|
||
全过程不变量:人物身份、体型、发型全程一致;
|
||
机位、构图、背景全程锁定不变,仅服装逐拍变化;
|
||
人物始终已穿好当前套,禁止穿衣/套袖/拉拉链等穿戴过程镜头。
|
||
|
||
声音:无对白,预留卡点BGM位,每次转场配快门/whoosh音效
|
||
|
||
全片保持主体1身份一致、鱼眼畸变风格统一、
|
||
机位与构图全片同一,背景【场景描述】不变。
|
||
"""
|
||
|
||
|
||
def is_fish_eye_outfit_preset(name: str) -> bool:
|
||
return (name or "").strip() == FISH_EYE_OUTFIT_PRESET
|
||
|
||
|
||
def fish_eye_outfit_prompt_template() -> str:
|
||
return _FISH_EYE_OUTFIT_PROMPT_TEMPLATE.strip()
|
||
|
||
def is_click_swap_preset(name: str) -> bool:
|
||
return (name or "").strip() in CLICK_SWAP_PRESETS
|
||
|
||
|
||
|
||
_SAME_CLICK_POS_RE = re.compile(
|
||
r"(?:每次(?:都)?)?(?:点击|点触|轻触|手指(?:点击|点)?)(?:在|到)?(?:完全)?(?:相?同的?)?(?:一个位置|一(?:个)?位置|一点|同一处|同一点|同一个位置)"
|
||
r"|每次点击同一位置"
|
||
r"|点同一点"
|
||
r"|手指点在完全相同的位置"
|
||
r"|手指始终点同一"
|
||
r"|点击位置保持不变"
|
||
r"|固定点击位置"
|
||
r"|不写点击同一位置"
|
||
r"|不必强调每次点同一位置"
|
||
)
|
||
|
||
|
||
def scrub_click_swap_same_position_wording(text: str) -> str:
|
||
"""去掉提示词里「每次点击同一位置」类表述,并在缺失时补上「每次点击位置不同」。"""
|
||
raw = str(text or "")
|
||
if not raw:
|
||
return raw
|
||
cleaned = _SAME_CLICK_POS_RE.sub("", raw)
|
||
cleaned = re.sub(r"[,。;]{2,}", ",", cleaned)
|
||
cleaned = re.sub(r"\s{2,}", " ", cleaned).strip()
|
||
if "点击位置必须不同" not in cleaned and "落点必须与上一次不同" not in cleaned and "落点必须不同于上一次" not in cleaned:
|
||
cleaned = (
|
||
f"{cleaned}\n\n【点击落点硬性要求】每一次换款的手指点击落点必须与上一次不同"
|
||
"(点商品的不同区域/角点),禁止「每次点击同一位置 / 点同一点」。"
|
||
).strip()
|
||
return cleaned
|
||
|
||
|
||
def apply_video_preset_prompt(name: str, prompt: str, *, click_swap_mode: str = "") -> str:
|
||
"""把视频预设确定性并入最终出片指令,避免只依赖对话模型主动复述。"""
|
||
base = str(prompt or "").strip()
|
||
preset = (name or "").strip()
|
||
mode = (click_swap_mode or "").strip()
|
||
contract = video_preset_delivery_contract(preset, click_swap_mode=mode)
|
||
if not base or not contract:
|
||
return base
|
||
marker = f"【视频预设】{preset}"
|
||
if marker in base:
|
||
return base
|
||
if is_click_swap_preset(preset):
|
||
if mode == "character":
|
||
return (
|
||
f"{marker}\n{contract}\n\n"
|
||
"【原始商品与 SKU 信息】仅提取下文中的商品外观、颜色、款式、顺序与角色/场景事实;"
|
||
"下文任何与「同一角色、同一日常场景、按序换款」冲突的拍法一律忽略。\n"
|
||
f"{base}\n\n"
|
||
"【最终执行检查】全片锁定同一位角色与同一生活场景;"
|
||
"商品颜色/款式严格按确认顺序切换,禁止随机换人、口播带货腔或复杂剧情反转。"
|
||
)
|
||
base = scrub_click_swap_same_position_wording(base)
|
||
return (
|
||
f"{marker}\n{contract}\n\n"
|
||
"【原始商品与 SKU 信息】仅提取下文中的商品外观、颜色、款式和顺序事实;"
|
||
"下文任何与「固定机位、手指点击触发换款、构图稳定」冲突的拍法一律忽略。\n"
|
||
f"{base}\n\n"
|
||
"【最终执行检查】换款由手指点击触发,机位、背景、光线与商品构图保持稳定;"
|
||
"每一次换款点击落点必须不同于上一次,禁止「每次点击同一位置 / 点同一点」;"
|
||
"不出现完整人物、不生成角色图。"
|
||
)
|
||
if is_fish_eye_outfit_preset(preset):
|
||
return (
|
||
f"{marker}\n{contract}\n\n"
|
||
"【原始人物与服装信息】仅提取下文中的人物身份、服装套数/顺序、机位、场景与转场事实;"
|
||
"下文任何与「全片单机位、鱼眼畸变、节拍换装、服装状态机、无对白」冲突的拍法一律忽略。\n"
|
||
f"{base}\n\n"
|
||
"【最终执行检查】全片单一机位与构图锁定;鱼眼中心清晰、边缘畸变;"
|
||
"换装发生在遮挡/甩镜瞬间且人物始终已穿好当前套;无对白,转场配快门/whoosh;"
|
||
"禁止穿衣过程与口播带货腔。"
|
||
)
|
||
return f"{base}\n\n{marker}\n{contract}"
|
||
|
||
def apply_image_preset_prompt(name: str, prompt: str) -> str:
|
||
"""将已选图片预设的风格字段确定性并入实际出图指令,不依赖 Agent 自行复述。"""
|
||
base = str(prompt or "").strip()
|
||
preset = (name or "").strip()
|
||
style = IMAGE_PRESETS.get(preset)
|
||
if not base or not style:
|
||
return base
|
||
marker = f"【图片风格预设】{preset}"
|
||
if marker in base:
|
||
return base
|
||
return (
|
||
f"{base}\n\n{marker}\n{style}\n"
|
||
f"画质基线:{IMAGE_PRESET_QUALITY_SUFFIX}\n"
|
||
f"负面约束:{IMAGE_PRESET_NEGATIVE_CONSTRAINTS}"
|
||
)
|