""" 角色差异化分镜模板 扒自:worker/src/image.js buildImageStoryboard storyboardByRole(222-321) 每个分镜角色有【各自不同】的画面/文字/构图策略,这是"小红书风格不雷同"的根因。 之前缩水:6张共用同一个品类 proof 策略 → 图全长一样。 模板里 {占位} 在 storyboard.py 运行时按文案/卖点填充。 字段含义(对齐 promptFromStoryboard 9 字段): goal 本张目标(这张图要让用户产生什么动作/情绪) overlay 图上主文字模板(每张不同,不重复封面标题) visual 画面主体(构图、景别、道具、光线——这是不雷同的关键) basis 文案依据(这张图从文案哪里来,给模型锚点) forbidden 本张禁止事项 """ from __future__ import annotations import re # ── sanitize(扒 sanitizeImagePlanText,防违禁视觉词进 prompt) _SANITIZE_RULES: list[tuple[str, str]] = [ (r"before\s*&\s*after", "质地与使用状态说明"), (r"before\s*/?\s*after", "质地与使用状态说明"), (r"\bbefore\b", "使用前状态"), (r"\bafter\b", "使用后状态"), (r"使用前后|用前用后|用前后|前后对比|使用前|使用后", "质地/场景/使用状态说明"), (r"功效对比|效果对比|改善对比", "质地/场景说明对比"), (r"肤色变白|皮肤变白|变白|美白", "自然光泽感"), (r"瑕疵消失|斑点消失|痘印消失|消除瑕疵|祛斑", "妆感更服帖"), (r"治疗前后|治疗后|医美前后|治愈|修复受损", "日常使用场景说明"), ] def sanitize_text(value: str, max_len: int = 56) -> str: s = str(value) for pattern, repl in _SANITIZE_RULES: s = re.sub(pattern, repl, s, flags=re.IGNORECASE) return re.sub(r"\s+", " ", s).strip()[:max_len] # 北哥6张标准套 + 8张扩展角色,每角色独立画面策略 ROLE_STORYBOARD_TPL: dict[str, dict] = { "hook": { "goal": "让{audience}因为{pain}停下划走,产生点开欲", "overlay": "{hook}", "visual": "自然光生活场景,手持产品或产品在桌面前景,真实使用细节/手部细节,像iPhone随手实拍的封面,不是海报", "basis": "来自选中文案标题、人群{audience}、痛点{pain}", "forbidden": "不要价格、不要重复后续卖点、不要App界面、不要广告海报感", }, "product_closeup": { "goal": "建立单品记忆锚点,让用户记住是哪个产品", "overlay": "{brand}", "visual": "单品高清特写居中,干净浅色台面,柔和顶光,瓶身/包装/标签清晰可读且忠于参考图原样,品牌词只出现在图上标题/贴纸等文字排版层,绝不印到瓶身上", "basis": "来自产品名和品牌词,第2张和第6张都要带品牌词强化记忆", "forbidden": "不要堆多个产品、不要花哨背景抢主体、不要改包装文字", }, "ingredient": { "goal": "用成分/配方/参数信息建立信任,但不夸大不医疗化", "overlay": "看清{point}", "visual": "信息卡片式布局,产品+成分/参数图标+短说明,浅色干净商务风,信息层级清楚(护肤食品讲成分配方,数码家居讲参数原理,服饰讲材质工艺)", "basis": "来自卖点里的成分/参数/工艺点,理性表达不夸大", "forbidden": "不要治疗/改善疾病承诺、不要医生背书、不要绝对化", }, "texture": { "goal": "让用户看到{point}的真实质感证据", "overlay": "{point}看得见", "visual": "质地/材质/细节微距,产品放在旁边,自然光,保留真实纹理(护肤=涂抹延展肤感,食品=色泽质地,服饰=面料织纹,数码家居=做工细节),能看清{point}", "basis": "来自卖点里的质地/材质/做工描述", "forbidden": "不要生成变白效果、不要医疗化对比、不要和封面同构图", }, "applied_proof": { "goal": "用可感知的真实使用证据证明{point}", "overlay": "{proof_overlay}", "visual": "{proof_visual}", "basis": "来自核心卖点{point}和用户对效果的关注", "forbidden": "{proof_forbidden}", }, "closer": { "goal": "用囤货/省钱情报/搜索暗号完成软性转化", "overlay": "这波真的会囤 {brand}", "visual": "拆箱、囤货角或产品放在日常物品旁,真实分享氛围,轻量搜索/品牌词暗号提示,再带一次品牌词引导成交", "basis": "来自价格心智/选择理由,但不做硬广", "forbidden": "不要大促价格牌、不要购买按钮、不要红黄电商风", }, # ── 8张扩展角色 ── "pain_scene": { "goal": "让用户共鸣{pain}", "overlay": "{pain}真的懂", "visual": "{scene}里的真实困扰场景,产品作为解决方案线索出现,不做使用前后对比", "basis": "来自文案痛点和目标人群", "forbidden": "不要夸大焦虑、不要before/after", }, "social_proof": { "goal": "补足信任背书,让内容不像单方面推销", "overlay": "身边人都在问", "visual": "产品在包里/桌面/宿舍囤货角,配简短手写感反馈气泡,真实随手拍", "basis": "来自评论区语言/选择理由,缺评论时用低调口碑表达", "forbidden": "不要假造大量头像评论、不要App评论区截图", }, "scenario": { "goal": "展示{scene}以外的多场景使用代入", "overlay": "这些场景都能用", "visual": "2-3个生活小场景拼贴:宿舍/通勤包/办公桌,产品贯穿其中,统一光线", "basis": "来自目标人群的多场景使用需求", "forbidden": "不要电商详情页拼贴、不要夸大效果", }, "tutorial": { "goal": "降低使用门槛,告诉用户怎么用", "overlay": "三步就上手", "visual": "三步手势教程:取量、点涂/使用、收尾,干净背景,产品在画面内", "basis": "来自文案里的快速/懒人使用场景", "forbidden": "不要复杂说明书、不要过多文字", }, } def role_template(role: str) -> dict: """取角色模板,未知角色用 applied_proof 兜底(和源头一致)""" return ROLE_STORYBOARD_TPL.get(role, ROLE_STORYBOARD_TPL["applied_proof"]) # ── proofStrategy(按品类定 applied_proof 证明页策略,扒 image.js:163-208) # 品类来自 product.category,不硬编码枚举;无匹配走"通用好物"兜底 PROOF_STRATEGIES: dict[str, dict] = { "个护护理": { "overlay_tpl": "{point}看得见", "visual": "手部/身体局部使用证明:少量点涂、推开后吸收状态、真实纹理和自然光;产品只做辅助露出", "asset_use": "优先使用实拍/参考图中的手部、干纹、涂抹、随身场景;产品图保证包装准确", "forbidden": "不要变白、祛斑、医学效果、before/after字样;不要和封面同构图", }, "美妆护肤": { "overlay_tpl": "{point}看得见", "visual": "肤感/质地证明:手背、脸颊局部或质地微距,展示推开前后真实状态,保留皮肤纹理和自然光", "asset_use": "优先使用实拍/参考图中的手背、上脸、质地素材;产品图辅助露出", "forbidden": "不要变白、祛斑、医学效果、before/after字样;不要和封面同构图", }, "食品饮品": { "overlay_tpl": "{point}一眼懂", "visual": "冲泡/开袋/入口证明:展示包装、杯中状态、质地颜色或一口口感,真实桌面光线", "asset_use": "产品图保证包装准确,参考图用于杯子、开袋、冲泡、办公室/居家场景", "forbidden": "不要涂抹、不要护肤肤感、不要医疗健康承诺", }, "营养健康": { "overlay_tpl": "看清{point}", "visual": "理性证明页:包装、成分表、使用场景和每日习惯卡片,信息清晰但不做治疗承诺", "asset_use": "产品图和说明图用于成分/包装准确,参考图用于日常使用场景", "forbidden": "不要治疗、改善疾病、速效、医生背书、前后对比", }, "家居生活": { "overlay_tpl": "{point}真省事", "visual": "使用过程证明:展示痛点场景、产品介入和使用过程细节,强调顺手/收纳/效率", "asset_use": "参考图用于真实家居环境,产品图保证外观准确", "forbidden": "不要护肤涂抹,不要虚假夸大结果", }, "服饰穿搭": { "overlay_tpl": "{point}有细节", "visual": "上身/材质证明:展示面料纹理、版型细节或普通身材上身局部,真实自然", "asset_use": "参考图用于上身/搭配/材质,产品图保证款式颜色准确", "forbidden": "不要护肤涂抹,不要过度精修模特感", }, "通用好物": { "overlay_tpl": "{point}清晰可见", "visual": "产品使用场景证明:真实道具/场景,展示产品细节和使用过程", "asset_use": "产品图保证准确,参考图用于场景辅助", "forbidden": "不要夸大效果,不要硬广式价格牌", }, } def proof_strategy(category: str, point: str) -> dict: """取品类证明策略,无匹配用通用兜底(扒 proofStrategy)""" s = PROOF_STRATEGIES.get(category, PROOF_STRATEGIES["通用好物"]).copy() s["overlay"] = s.pop("overlay_tpl", "{point}").format(point=point) return s