Files
yingqing/core/backend/apps/ai/creation_presets.py
Azmat@qq.com 9f15b71f19 全能创作与模特库收口:品牌品名输入、确认角色循环、换款落点与鱼眼模板
补充品牌品名改为预填输入框且空骨架不跳步;痛点确认角色后「继续完善方案」可继续推进;点击换款要求每次落点不同;鱼眼换装改用小云雀连环换装 prompt 模板;模特详情标签自动换行。
2026-09-21 16:47:50 +08:00

596 lines
42 KiB
Python
Raw Permalink 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.
"""全能创作 · 预设(契约 §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 秒估算(约 415 秒);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}"
)