diff --git a/core/backend/apps/ai/creation_agent.py b/core/backend/apps/ai/creation_agent.py index aa73d73..2ab28ab 100644 --- a/core/backend/apps/ai/creation_agent.py +++ b/core/backend/apps/ai/creation_agent.py @@ -368,6 +368,7 @@ def emit_prompt_gate( return [] prompt = apply_product_voice_visual_guard(conversation, prompt) prompt = apply_product_reality_guard(prompt) + prompt = apply_video_platform_safety_guard(prompt) messages = [] prompt_file = append_message( conversation, @@ -406,6 +407,7 @@ def emit_final_confirm_gate( return None prompt = apply_product_voice_visual_guard(conversation, prompt) prompt = apply_product_reality_guard(prompt) + prompt = apply_video_platform_safety_guard(prompt) credits = 0 if context is not None: try: @@ -516,6 +518,8 @@ video_prompt 是交给出片模型的完整制作文件,不是方案摘要、 - 画面中不出现新增字幕、花字、标题贴片、弹幕、角标、水印、购物浮层或说明性文字;口播只存在于声音,包装本身原有印刷字除外。 - 结尾单列「全片一致性与禁用项」:重申角色、商品、场景、光线、服装/材质的连续性,以及本片最重要的画面禁用项。 - 用户说「商品说话 / 商品自述 / 商品拟人」时,默认无脸拟人:商品声音是画外角色声,商品本体不做口型、不新增卡通五官;性格只通过整体倾斜、转向、弹跳、进退、镜头和音效表达。只有用户明确要求可见卡通五官时才例外。 +- 平台安全优先于戏剧冲突:不要撰写或照抄暴力伤害、血腥、裸体或性暗示、违法犯罪、危险挑战、政治敏感、仇恨歧视、真实名人/IP 模仿等内容。若用户原意涉及上述内容,保留原来的情绪转折和商品卖点,改成成年人之间安全、非暴力、无违法的日常误会、压力或竞争,并采用原创人物与场景。儿童只能出现在安全的日常家庭场景,绝不参与危险、成人或营销煽动情节。 +- 不写医疗诊断、治愈/根治、绝对效果、虚构权威背书或夸大功效;把卖点改为真实可见的使用动作、材质细节和日常体验。最终 video_prompt 必须是可直接提交审核和出片的安全版本。 """ @@ -546,6 +550,42 @@ PRODUCT_REALITY_GUARD = ( "材质等能力拍成已被验证的结果。若关键信息不足,使用可观察的正常操作替代夸张测试。" ) +# 这一层不是替代平台审核,而是在脚本落成最终 video_prompt 前先把最容易被视频模型 +# 拦截的明确高风险表述改成等价的安全叙事。模型仍会收到下方完整约束,避免只靠关键词替换。 +VIDEO_PLATFORM_SAFETY_GUARD = ( + "【平台安全出片约束·最高优先级】全片仅呈现安全、合法、适合公开传播的原创商业内容。" + "不出现或暗示暴力伤害、血腥、自残、裸体、性内容、违法犯罪、赌博、毒品、武器、危险挑战、" + "政治敏感、仇恨歧视、真实名人或受保护 IP 模仿。若原剧情有冲突,只用成年人之间安全、" + "非暴力的日常误会、压力或竞争来表达,并以沟通或轻松反转收束。儿童仅可出现在安全的日常家庭场景," + "不得参与危险、成人或煽动性情节。商品功效只写已知事实和可见使用体验,不作医疗、治愈、" + "绝对化或虚构权威承诺。" +) +_VIDEO_PLATFORM_SAFETY_REWRITES: tuple[tuple[re.Pattern[str], str], ...] = ( + ( + re.compile( + r"暴打|殴打|群殴|互殴|动手伤人|打伤|捅伤|刺伤|砍伤|见血|鲜血(?:直流)?|血泊|" + r"杀人|杀死|自杀|自残|割腕|跳楼|爆炸|绑架|虐待" + ), + "强烈情绪冲突以安全、非暴力方式化解", + ), + ( + re.compile(r"裸体|裸露|色情|性爱|性行为|性暗示|挑逗|床戏"), + "得体着装下的自然成年人互动", + ), + ( + re.compile(r"吸毒|毒品交易|贩毒|赌博|赌钱|诈骗|抢劫|偷窃|枪战|持枪|开枪"), + "不涉及违法或危险行为的日常情节", + ), + ( + re.compile(r"治愈|根治|药到病除|包治|抗癌|无副作用|百分之百(?:有效|治愈)|永久(?:有效|瘦)"), + "真实、可观察的日常使用体验", + ), + ( + re.compile(r"(?:模仿|复刻|扮演|像).{0,16}(?:明星|艺人|名人|网红|演员)"), + "采用原创人物设定与表演", + ), +) + def apply_product_voice_visual_guard(conversation: CreationConversation, prompt: str) -> str: """商品拟人默认不长脸;用户明确要求可见卡通五官时尊重其创作选择。""" @@ -576,6 +616,16 @@ def apply_product_reality_guard(prompt: str) -> str: return f"{base}\n{PRODUCT_REALITY_GUARD}".strip() +def apply_video_platform_safety_guard(prompt: str) -> str: + """将最终出片指令收束为较不易触发视频模型审核的安全版本,且可重复调用。""" + base = str(prompt or "").strip() + if not base or VIDEO_PLATFORM_SAFETY_GUARD in base: + return base + for pattern, replacement in _VIDEO_PLATFORM_SAFETY_REWRITES: + base = pattern.sub(replacement, base) + return f"{base}\n{VIDEO_PLATFORM_SAFETY_GUARD}".strip() + + class AgentError(Exception): """Agent 循环里的业务错误,已经是可以直接给用户看的中文。""" @@ -1748,6 +1798,7 @@ def _video_submit_params(context: AgentContext, prompt: str) -> tuple[dict, list prompt = apply_video_preset_prompt(context.conversation.preset, prompt) prompt = apply_product_voice_visual_guard(context.conversation, prompt) prompt = apply_product_reality_guard(prompt) + prompt = apply_video_platform_safety_guard(prompt) prompt = apply_plot_twist_story_contract( context.conversation.preset, active_plot_twist_story_depth(context.conversation), @@ -3132,6 +3183,7 @@ def _dispatch_tool( video_prompt = apply_video_preset_prompt(context.conversation.preset, video_prompt) video_prompt = apply_product_voice_visual_guard(context.conversation, video_prompt) video_prompt = apply_product_reality_guard(video_prompt) + video_prompt = apply_video_platform_safety_guard(video_prompt) video_prompt = apply_plot_twist_story_contract( context.conversation.preset, active_plot_twist_story_depth(context.conversation), @@ -3202,6 +3254,7 @@ def _dispatch_tool( video_prompt = apply_video_preset_prompt(context.conversation.preset, video_prompt) video_prompt = apply_product_voice_visual_guard(context.conversation, video_prompt) video_prompt = apply_product_reality_guard(video_prompt) + video_prompt = apply_video_platform_safety_guard(video_prompt) video_prompt = apply_plot_twist_story_contract( context.conversation.preset, active_plot_twist_story_depth(context.conversation), diff --git a/core/backend/apps/ai/test_creation_agent.py b/core/backend/apps/ai/test_creation_agent.py index bbae5e0..23830dc 100644 --- a/core/backend/apps/ai/test_creation_agent.py +++ b/core/backend/apps/ai/test_creation_agent.py @@ -1503,6 +1503,32 @@ class PresetGuidanceTests(CreationAgentBaseTests): # 重复走闸门不会把同一条约束堆进 Prompt 两遍。 self.assertEqual(apply_product_reality_guard(prompt), prompt) + def test_video_platform_safety_guard_rewrites_high_risk_story_terms(self): + from .creation_agent import apply_video_platform_safety_guard + + prompt = apply_video_platform_safety_guard( + "妈妈发现孩子被欺负后冲进去暴打对方,鲜血直流;产品能药到病除。" + ) + + self.assertIn("平台安全出片约束", prompt) + self.assertNotIn("暴打", prompt) + self.assertNotIn("鲜血直流", prompt) + self.assertNotIn("药到病除", prompt) + self.assertIn("安全、非暴力方式化解", prompt) + self.assertIn("真实、可观察的日常使用体验", prompt) + # 方案卡、Prompt 文件、最终提交会多次经过这层,规则只能追加一次。 + self.assertEqual(apply_video_platform_safety_guard(prompt), prompt) + + def test_video_system_prompt_requires_audit_safe_script(self): + conversation = CreationConversation.objects.create( + team=self.team, created_by=self.user, mode="video", params={}, + ) + + system = build_system_prompt(AgentContext(conversation=conversation, user=self.user, model_config=self.model)) + + self.assertIn("平台安全优先于戏剧冲突", system) + self.assertIn("最终 video_prompt 必须是可直接提交审核和出片的安全版本", system) + def test_image_preset_style_is_added_only_to_the_actual_image_prompt(self): from .creation_presets import apply_image_preset_prompt