Kaynağa Gözat

Refine AI material prompt structure

刘立冬 3 hafta önce
ebeveyn
işleme
6442aa31a6

+ 6 - 2
examples/auto_put_ad_mini/PRODUCTION_AUTOMATION.md

@@ -177,13 +177,17 @@ AI 生成素材链路只改变“素材来源”,不改变视频筛选、人群
 当前每个视频通过 `landing_video_id` 读取本地库 `video_element_feature_cache` 中该视频最新分区的 `解构选题`,
 并使用贡献分最高的 `standard_element` 生成 1 张 `topic` 图片。
 生成图片前会先用 OpenRouter 文本模型清洗原始 `解构选题`,
-去掉转发、关注、公众号回复、加群、领取、底部提示、推广话术等互动/营销引导,同时保留核心事实、对象、数字和主题。
+去掉转发、关注、公众号回复、加群、领取、底部提示、推广话术等互动/营销引导,同时保留核心问题、信息差、反差、情绪价值或生活提醒价值。
+清洗后的主题种子不再强制写入“中老年/老人/退休/晚年”等目标用户词;目标用户不等于画面主体。
 清洗失败或清洗后仍包含互动/营销词时,本轮跳过该视频,不使用原始 topic 兜底。
-最终图片 prompt 由工程内模板和清洗后的动态承接视频描述拼接:
+标题节点会先生成主标题和 `highlight_terms`;图片生成阶段只能高亮这些词,不能自行选择其他高亮词。
+最终图片 prompt 由工程内模板、清洗后的动态承接视频描述、标题/高亮词、以及弱化后的 pattern `visual_direction` 拼接:
 
 - 清洗模板文件:`examples/auto_put_ad_mini/prompts/ai_sanitize_video_description.md`
+- 标题模板文件:`examples/auto_put_ad_mini/prompts/ai_cover_copy.md`
 - 模板文件:`examples/auto_put_ad_mini/prompts/ai_generated_material.md`
 - 动态描述:`解构选题.standard_element` 经文本模型清洗后的主题描述
+- 视觉方向:`material_creative_pattern.visual_rule` 只作为可选参考;如果和画面主体多样性、非默认人物、非默认老人正脸冲突,以全局视觉策略为准
 - 图片比例:`AI_IMAGE_ASPECT_RATIO`,默认 `16:9`
 - 最终输出尺寸:`AI_IMAGE_TARGET_WIDTH` x `AI_IMAGE_TARGET_HEIGHT`,默认 `1280x720`
 

+ 12 - 12
examples/auto_put_ad_mini/configs/material_creative_patterns_seed.json

@@ -6,7 +6,7 @@
     "hook_category": "pension_money,information_gap",
     "visual_template": "explainer_person_or_plain_big_title",
     "title_hook_rule": "围绕中老年明确利益点提出一个通俗问题,用信息差承接,标题要直接、清晰、可一眼看懂。可使用“会发生什么”“答案出来了”“很多人还不知道”等结构。",
-    "visual_rule": "使用强封面感构图,超大中文标题优先,可有人物讲解、资料清单、家庭讨论等元素。画面要真实、清晰、适合45岁以上用户快速阅读。",
+    "visual_rule": "强封面感构图,超大中文标题优先。可使用资料清单、账本票据、讲解现场、桌面道具或简洁背景;人物不是必需,不要默认老人正脸。",
     "relevance_rule": "仅在视频特征包含退休、养老金、社保、补贴、工资、养老等相关主题时优先使用。",
     "compliance_rule": "不得承诺收益,不得伪装官方政策通知,不得制造虚假紧迫,不得出现假按钮或假界面。",
     "positive_examples": ["每个月的养老金不取会发生什么?答案出来了!"],
@@ -36,7 +36,7 @@
     "hook_category": "pension_money,time_anchor,number_list,information_gap",
     "visual_template": "explainer_person_or_plain_big_title",
     "title_hook_rule": "使用“今年/下个月/六月开始/2026年”等时间锚点,结合退休工资、补贴、社保等中老年关心的信息,形成提醒感和信息差。表达要克制,避免假政策通知。",
-    "visual_rule": "大字标题加资料/账单/清单感画面,可出现中老年人在家中查看纸质信息或与家人讨论。",
+    "visual_rule": "大字标题加资料、账单、清单、日历或桌面特写。可有人物手部或弱化人物,不默认家庭讨论和老人正脸。",
     "relevance_rule": "适合视频特征中包含退休、工资、补贴、时间变化、政策解读、注意事项等主题。",
     "compliance_rule": "不能写成官方通告,不能承诺涨钱,不能使用紧急、马上、最后机会等强诱导。",
     "positive_examples": ["六月开始退休工资超5000元的要留意我也是刚知道"],
@@ -65,7 +65,7 @@
     "hook_category": "number_list,information_gap",
     "visual_template": "explainer_person_or_plain_big_title",
     "title_hook_rule": "把视频主题转成清单、排名、几类人、几个原因、几件事等结构,让用户预期点击后能得到完整解释。标题要具体,但不要夸张。",
-    "visual_rule": "封面突出数字、清单、表格或对比,大字标题清晰,背景可以是资料页、家庭桌面或人物讲解。",
+    "visual_rule": "封面突出数字、清单、表格、排行或对比。背景可以是资料页、物品特写、讲解现场或简单真实场景,人物不是必需。",
     "relevance_rule": "适合视频特征中包含排名、清单、步骤、原因、类型、注意事项、对比解释等主题。",
     "compliance_rule": "避免国家排名情绪化、涉政对立、虚假权威背书、假榜单和绝对化表达。",
     "positive_examples": ["中国31省人均养老金排名出来了再忙也要看看"],
@@ -95,7 +95,7 @@
     "hook_category": "family_emotion,number_list",
     "visual_template": "elder_family_emotion_big_title",
     "title_hook_rule": "围绕晚年生活、子女关系、老伴、兄弟姐妹、邻里相处等生活议题,生成现实提醒型标题。可以有一点悬念和情绪,但要避免两性猎奇和伦理冲突。",
-    "visual_rule": "中国本土家庭/客厅/院子/饭桌/社区场景,1-3位中老年或家庭成员,大字标题突出生活提醒。",
+    "visual_rule": "本土生活场景或关系线索,可用家门口、桌面物件、旧照片、空椅子、人物背影或少量人物互动表达。不要默认客厅饭桌和老人正脸。",
     "relevance_rule": "适合视频特征中包含晚年、家庭、子女、老伴、亲情、邻里、人情往来等主题。",
     "compliance_rule": "不得低俗擦边,不得婚配违法敏感,不得哭惨卖惨,不得制造极端恐吓。",
     "positive_examples": ["当你老了建议你少去看望兄弟姐妹3个原因很现实"],
@@ -125,7 +125,7 @@
     "hook_category": "music_stage,family_emotion",
     "visual_template": "stage_or_music_emotion",
     "title_hook_rule": "围绕老歌、乡愁、亲情、回忆、听哭几代人等情绪共鸣生成标题。标题要有情感张力,但不要哭惨卖惨或过度煽动。",
-    "visual_rule": "舞台、麦克风、老照片、家庭听歌、乡村记忆等场景,大字标题突出共鸣和回忆。",
+    "visual_rule": "舞台、麦克风、歌词纸、老照片、收音机、唱片、观众反应或年代感场景,大字标题突出共鸣和回忆。人物可有可无。",
     "relevance_rule": "适合视频特征包含歌曲、音乐、乡愁、回忆、父母、儿女、年代感、情感共鸣等主题。",
     "compliance_rule": "不得使用名人肖像,不得虚构明星背书,不得使用极端哭惨表达。",
     "positive_examples": ["一首歌,唱哭了几代人的乡愁"],
@@ -153,7 +153,7 @@
     "hook_category": "health_life,information_gap",
     "visual_template": "object_closeup_big_title",
     "title_hook_rule": "围绕家中常见物品、食材、证件、手机、账本等生活细节生成注意提醒型标题。用“家里有X的注意了”“原来还有这种用法”等信息差,避免健康恐吓。",
-    "visual_rule": "物品近景 + 大字标题,可搭配中老年手部、厨房、客厅、桌面等真实生活场景。",
+    "visual_rule": "物品近景 + 大字标题优先,可搭配手部、厨房、桌面、柜子、证件、手机等真实生活元素。不要把人物作为默认主体。",
     "relevance_rule": "适合视频特征包含生活用品、食材、家庭常识、手机使用、消息辨别、日常提醒等主题。",
     "compliance_rule": "不得写医疗疗效、慢性中毒、治病保健、专家恐吓,不得出现假按钮。",
     "positive_examples": ["家里有红枣的注意了立马回家翻出来"],
@@ -182,7 +182,7 @@
     "hook_category": "number_list,family_emotion",
     "visual_template": "elder_family_emotion_big_title",
     "title_hook_rule": "把老年生活经验、晚年建议、自我成长、情绪调节等内容转成“几条建议/几个习惯/几件事”的清单型标题。表达要温和、有用、有现实感。",
-    "visual_rule": "中老年人在客厅、院子、社区、饭桌等场景交流或思考,标题清晰突出“建议/经验/几件事”。",
+    "visual_rule": "用生活选择、清单、旧物、路口、院子、桌面道具、背影或少量人物交流表达经验感。标题突出“建议/经验/几件事”,不默认老人正脸。",
     "relevance_rule": "适合视频特征包含老人、晚年、生活建议、自我成长、情绪、习惯、人生经验等主题。",
     "compliance_rule": "不得制造恐惧,不得把建议写成绝对真理,不得贬低老年人或家庭成员。",
     "positive_examples": ["老年人一旦迈入八十岁高龄不妨听听这七条建议"],
@@ -210,7 +210,7 @@
     "hook_category": "information_gap,social_observation",
     "visual_template": "social_observation_scene_big_title",
     "title_hook_rule": "围绕中国本土生活场景、社区见闻、乡村变化、邻里事件、城市生活观察生成见闻型标题。可以有好奇感,但不碰涉政、民族对立和国际冲突。",
-    "visual_rule": "街道、社区、院子、乡间、饭桌、楼道等真实场景,用大字标题突出“原来/没想到/后来才知道”。",
+    "visual_rule": "街道、社区、院子、乡间、楼道、店铺、路口或事件现场等真实场景,用大字标题突出“原来/没想到/后来才知道”。",
     "relevance_rule": "适合视频特征包含生活见闻、社区、乡村、邻里、城市变化、普通人故事等主题。",
     "compliance_rule": "不得煽动地域/民族/国家对立,不得伪装新闻通告,不得使用灾难事故猎奇。",
     "positive_examples": ["就在刚才北京街头发生意外在场的人都看傻了"],
@@ -239,7 +239,7 @@
     "hook_category": "information_gap,life_common_sense",
     "visual_template": "mixed_information_cover",
     "title_hook_rule": "围绕日常生活中容易忽略的常识、误区、原因解释生成信息差标题。标题要像生活经验分享,不要像专家恐吓或医疗建议。",
-    "visual_rule": "真实生活背景 + 大字标题,可使用人物讲解、对比图、桌面物品,但不出现假按钮和伪界面。",
+    "visual_rule": "真实生活背景 + 大字标题,可使用物品特写、对比图、资料页、讲解现场或弱化人物,但不出现假按钮和伪界面。",
     "relevance_rule": "适合视频特征包含生活常识、原因解释、误区、普通人经验、家庭提醒等主题。",
     "compliance_rule": "不得涉及医疗诊断/疗效承诺/健康恐吓,不得使用专家权威背书制造恐慌。",
     "positive_examples": ["早上起来能不能空腹喝水?一番话让人明白了"],
@@ -267,7 +267,7 @@
     "hook_category": "family_emotion,story_reversal",
     "visual_template": "mixed_information_cover",
     "title_hook_rule": "围绕家庭关系、亲友往来、误会、重逢、发现真相等内容生成故事反转型标题。保持生活化和好奇感,不要走低俗伦理猎奇。",
-    "visual_rule": "家庭、院子、饭桌、门口、旧照片等场景,人物表情自然,大字标题突出“后来才知道/没想到”。",
+    "visual_rule": "用门口、院子、旧照片、桌面物件、空座位、背影、人物反应或少量互动制造故事感,不默认饭桌和老人正脸。",
     "relevance_rule": "适合视频特征包含家庭故事、亲情、人情往来、误会、回忆、重逢、生活反转等主题。",
     "compliance_rule": "不得涉及婚配违法、两性隐私、暴力犯罪、军警冒充、极端羞辱或恐吓。",
     "positive_examples": ["男子结婚请生父坐主桌养父靠边 后来才知道原因"],
@@ -297,7 +297,7 @@
     "hook_category": "social_observation,family_emotion,information_gap",
     "visual_template": "elder_family_emotion_big_title",
     "title_hook_rule": "围绕熟人、邻里、亲友之间的意外发现和生活反转生成标题。强调普通生活里的“没想到”,避免冲突、低俗和极端猎奇。",
-    "visual_rule": "社区广场、楼道、家门口、院子、饭桌等本土生活场景,1-3人互动,大字标题突出发现感。",
+    "visual_rule": "社区广场、楼道、家门口、院子、街边或桌面线索等本土生活场景,可有少量人物互动,也可以用场景和物件制造发现感。",
     "relevance_rule": "适合视频特征包含邻里、熟人、亲戚、朋友、社区、人情、普通人故事等主题。",
     "compliance_rule": "不得制造谣言、隐私窥探、低俗伦理冲突或恐吓式标题。",
     "positive_examples": ["邻居一句话让全家人突然明白了"],
@@ -354,7 +354,7 @@
     "hook_category": "light_curiosity,information_gap,story_reversal",
     "visual_template": "mixed_information_cover",
     "title_hook_rule": "围绕真实生活里的反常识、意外发现、稀奇见闻生成轻猎奇标题。重点是“有点稀奇、想知道原因”,不要恶俗、恐吓或假新闻。",
-    "visual_rule": "真实本土生活场景 + 大字标题,可以有人物惊讶、围观、发现物品、旧照片等剧情元素。",
+    "visual_rule": "真实本土场景 + 大字标题,优先用稀奇物件、过程现场、旧照片、机器、自然景象或场景反差制造好奇;人物惊讶或围观不是必需。",
     "relevance_rule": "适合视频特征包含奇闻、没想到、发现、真相、原因、反转、稀奇、意外、普通人见闻等主题。",
     "compliance_rule": "不得血腥暴力、违法犯罪、低俗擦边、迷信预测、假新闻播报、灾难事故猎奇或隐私曝光。",
     "positive_examples": ["今年燕子消失了你知道是怎么回事吗"],

+ 32 - 47
examples/auto_put_ad_mini/prompts/ai_cover_copy.md

@@ -1,11 +1,10 @@
 【system】
 你是中文信息流广告封面标题策划。
-你的任务是基于广告主题种子和创意 pattern,为 60-75 岁中老年用户生成更想看的封面主标题。
-目标不是一味悬疑,而是让目标用户觉得“和我有关、想继续看”。
+目标是为 45-75 岁用户生成更想点开的封面主标题。
 只输出 JSON,不要输出解释性正文。
 
 【user】
-请为下面的广告封面生成主标题。
+请根据广告主题种子和创意 pattern 生成封面主标题。
 
 【广告主题种子】
 {{video_description}}
@@ -13,57 +12,42 @@
 【创意Pattern】
 {{pattern_json}}
 
-【核心目标】
-- 核心目标:让 60-75 岁中老年用户愿意点开/继续看。
-- 标题长度保持 12-22 个汉字。
+【标题目标】
+- 14-20 个汉字,通俗、直接、有信息差或想看感;超过 22 个汉字视为无效。
 - 不要副标题。
-- 不要写成说明文、科普标题或平铺直叙的总结。
-
-【先做钩子判断】
-请先判断这个主题最适合哪一种 hook_angle,再写标题:
-- 反差型:表面和真相不一样,例如“晚年过得好不是靠钱多”
-- 未说破型:只说到关键处,留下后续原因,例如“退休后这件事很多人才看懂”
-- 过来人型:像同龄人经验,例如“人到六十才懂这几句话”
-- 清单型:具体几件事,例如“退休后这几件事要早点看透”
-- 情绪共鸣型:唱到心里、说到心坎,例如“这首歌越听越懂晚年的滋味”
-- 奇闻反差型:普通人做出不普通的事,例如“破棚里造出这辆怪车”
-- 实用提醒型:和日常生活直接相关,例如“路边太热心的人先别急着信”
-
-【候选标题要求】
-- 生成 5 个候选标题,每个候选必须是不同钩子角度或不同表达方式。
-- 每个标题都要通俗、直接、像老年人会点开的信息流大字。
-- 每个标题都要有反差、未说破、过来人经验、清单感、情绪共鸣或奇闻感中的至少一种。
-- 标题要与主题相关,但不要复刻视频脚本、镜头结构或高风险事件细节。
-- 尽量不用标点;不要使用逗号、句号、感叹号、问号、引号。
-- 不要错别字、乱码、异体字、不自然词组。
-
-【避免平淡表达】
-尽量不要使用这些容易变平的词:
-建议您看看、智慧锦囊、处世准则、幸福真相、详细拆解、综合指南、生活质量、深度解析、值得品读、开启优雅晚年。
-
-优先使用这些更通俗、有点击感的表达:
-早点看透、心里有数、别太较真、这几件事、原来是这样、很多人没想明白、看完心里踏实、过来人的话、老了才懂、越听越有味。
-
-【评分规则】
-请给每个候选打分:
-- hook_score: 老年人想看的钩子强度,0-100
-- plain_score: 通俗易懂程度,0-100
-- relevance_score: 与主题相关程度,0-100
-最终选择 selected_title 时优先看 hook_score,再看 plain_score,最后看 relevance_score。
-
-【底线限制】
-- 生成阶段优先保证点击吸引力和相关性,不要因为过度保守把标题写成平淡说明。
-- 不要写假官方通知、假系统提示、假按钮、二维码、医疗疗效、健康恐吓、血腥暴力、迷信预测、低俗擦边。
-- 避免明显夸张和强迫式表达,例如“100%”“唯一”“不看后悔”“最后一天”“紧急通知”。
+- 不写成说明文、科普总结或官方通知。
+- 不要为了迎合目标用户而滥用“老人、中老年、退休、晚年、六十、长辈”;只有主题本身强相关时才使用。
+- 标题要和主题相关,但不复刻视频脚本、镜头结构或高风险事件细节。
+
+【可选钩子角度】
+- 未说破型:说到关键处,留下原因。
+- 反差型:表面和真相不一样。
+- 清单型:几件事、几类人、几个原因。
+- 过来人型:像生活经验,但不要句句写年龄。
+- 情绪共鸣型:唱到心里、说到心坎、想起过去。
+- 奇闻反差型:普通事物背后有稀奇原因。
+- 实用提醒型:日常生活里容易忽略的提醒。
+
+【高亮词规则】
+- 输出 1-2 个 highlight_terms,必须完整出现在 selected_title 中。
+- 高亮词应是点击钩子的核心,例如对象、结果反差、数字、原因、稀奇点。
+- 不要高亮媒介词或虚词,例如“视频里、看完、原来、很多人、这几件事、才明白”。
+- 不要高亮风险词,例如“国家、补贴、领取、记性变差、儿子、女儿、病床、诈骗、犯罪”。
+- 如果没有合适高亮词,输出空数组。
+
+【底线】
+- 不要标点;不要错别字、乱码、异体字或不自然词组。
+- 不要假官方、假系统、二维码、医疗疗效、健康恐吓、血腥暴力、迷信预测、低俗擦边。
+- 避免“100%”“唯一”“不看后悔”“最后一天”“紧急通知”“惊掉下巴”等强诱导。
 
 【输出JSON格式】
 {
-  "hook_angle": "最终选择的钩子角度",
-  "hook_point": "一句话说明这个视频最能让老年人想看的点",
+  "hook_angle": "未说破型/反差型/清单型/过来人型/情绪共鸣型/奇闻反差型/实用提醒型",
+  "hook_point": "一句话说明最想让用户点开的点",
   "candidates": [
     {
       "title": "候选主标题",
-      "hook_angle": "反差型/未说破型/过来人型/清单型/情绪共鸣型/奇闻反差型/实用提醒型",
+      "hook_angle": "钩子角度",
       "hook_score": 90,
       "plain_score": 90,
       "relevance_score": 90,
@@ -71,5 +55,6 @@
     }
   ],
   "selected_title": "最终主标题",
+  "highlight_terms": ["高亮词1", "高亮词2"],
   "reason": "一句话说明为什么选择它"
 }

+ 30 - 35
examples/auto_put_ad_mini/prompts/ai_generated_material.md

@@ -4,46 +4,41 @@
 【输入】
 {{video_description}}
 
-【执行优先级】
-1. 合规安全。
-2. 服从输入中的“选中的创意Pattern”。
-3. 保持与视频主题相关。
-4. 参考高消耗/高点击率素材的表达方式,提升 60-75 岁中老年用户的停留和点击兴趣。
-5. 保证画面清晰、标题醒目、比例正确。
-
-【主题相关】
-- 输入内容是广告主题种子,不是视频脚本或画面脚本。
-- 必须保留主题背后的用户痛点、情绪冲突、信息差、生活提醒价值或核心主张。
-- 可以把具体事件转译成更普适、更安全、更有点击钩子的广告封面,但不能改成无关故事。
-- 相关性要求是“同主题/同情绪/同问题场景”,不是逐字逐事一致。
-- 不要求复刻视频原画面,优先生成更适合点击的同主题广告封面。
-
-【视觉策略】
-- 画面要像真实摄影广告封面,不要卡通、二次元、科技海报、欧美商业海报。
-- 视觉方向由 pattern 决定,基础 Prompt 不指定固定场景、人物关系、道具或色调。
-- 画面要服务点击钩子和主题相关性,不要套用固定视觉公式。
+【优先级】
+1. 使用指定主标题和指定高亮词。
+2. 服从选中的创意 Pattern。
+3. 保持与广告主题相关,并提升点击兴趣。
+4. 真实、清晰、手机小屏可读。
+5. 合规安全。
+
+【主题与画面】
+- 输入是广告主题种子,不是视频脚本或画面脚本。
+- 目标用户是 45-75 岁用户,但目标用户不等于画面主体。
+- 画面主体可以是人物、物品、资料清单、机器、自然景观、事件现场、舞台、桌面道具、建筑、老照片或场景对比。
+- 不强制出现人物;如果出现人物,也不强制是老人正脸。
+- 不复刻视频镜头,可以做同主题、同情绪、同问题场景的广告化转译。
+- 不要所有图都做成客厅、饭桌、会议室、病床、老人讨论的固定模板。
+
+【视觉风格】
+- 像真实中文信息流广告封面,不要卡通、二次元、科技海报、欧美商业海报。
+- 画面要有明确点击点:信息差、反差、清单感、奇闻感、情绪共鸣或生活提醒。
+- 由 Pattern 决定主要视觉方向;不要因为目标用户是中老年就默认画老人。
 - 人物必须是中国本土语境;不要外国人物;不要真实名人肖像或类似真实名人的脸。
 
-【标题策略
+【标题排版
 - 画面中必须有一个超大中文主标题。
-- 如果输入中提供了【封面标题】,必须使用其中的“主标题”,不要自行改写主标题。
-- 不要生成副标题、小字说明、角标或引导文案。
-- 主标题 12-22 个汉字,最多 2 行。
-- 主标题以大白字、黑色粗描边、高对比度为主。
-- 主标题中必须选择 1-2 个关键词使用黄色、橙色或红色做强调,但不要整句彩色化。
-- 主标题突出“我相关 + 信息差 + 未完全说破”,宁可短、有钩子,不要长句解释。
-- 主标题必须有广告素材钩子感,让用户产生“这和我有关、后面还有原因”的好奇。
-- 主标题不需要复述视频事实,应围绕同类用户会关心的问题、反差、提醒或悬念生成。
-- 主标题优先使用“提醒、反差、原因、清单、变化、很多人不知道、后来才明白”这类中老年高点击表达,但不要使用强恐吓和夸大承诺。
-- 主标题可使用对象、时间、数字、反差、情绪共鸣中的 1-2 个钩子点,不要堆叠。
-- 主标题必须是清晰可读的简体中文,不能有错别字、乱码、异体字或不自然词组。
-- 主标题不要使用逗号、冒号、顿号、句号、感叹号、问号;尤其不要使用感叹号或问号。
-- 除主标题外,不要口播字幕、互动提示、引导文案、伪按钮或界面文字。
+- 如果输入中提供了【封面标题】,必须逐字使用其中的“主标题”。
+- 不要生成副标题、小字说明、角标、引导文案或额外字幕。
+- 主标题最多 2 行,大白字、黑色粗描边、高对比度。
+- 只能高亮输入中提供的【高亮词】;没有高亮词时不要强行彩色高亮。
+- 高亮色可用黄色、橙色或红色,但不要整句彩色化。
+- 高亮只能改变文字颜色,不要给高亮词添加方括号、圆括号、引号、底色框、下划线或额外符号。
+- 主标题必须清晰可读,不能错字、乱码、异体字、断字、截断或不自然分词。
 
 【多样性】
-- 如果同一视频生成多张图,每张图应在 scene / subject / prop / composition / color_mood 中至少 2 项明显不同
-- 不同 pattern 必须产生不同视觉模板,不要只换标题不换画面
-- 可以偏广告封面化、讲解感、事件感、资料特写、舞台反应或人物情绪反应,不要全部使用同一种平和室内场景
+- 不同视频应有不同 scene / subject / prop / composition / color_mood。
+- 可以偏大字封面、资料特写、事件现场、物品近景、场景对比、舞台反应、自然奇观或人物情绪反应。
+- 如果人物不是主题必要元素,优先考虑非人物主体或人物弱化构图。
 
 【合规禁止】
 - 禁止假官方通知、假新闻播报、假系统提示、假微信界面、假聊天记录、假按钮、假红包、假弹窗、二维码、下载按钮、播放按钮伪装。

+ 14 - 18
examples/auto_put_ad_mini/prompts/ai_sanitize_video_description.md

@@ -1,26 +1,22 @@
 【system】
-你是广告素材生成前的广告主题提炼器。
-你的任务是把视频解构选题提炼成适合生成广告封面图的“广告主题种子”。
-只输出一句中文广告主题种子,不要解释。
-输出中绝对不要出现:领取、能领、已办成、赶紧、通知、不错过、转发、关注、公众号、加群、优惠券、卡券、下单、购买。
+你是广告封面生成前的主题提炼器。
+把视频解构选题提炼成一句广告主题种子。只输出一句中文,不要解释。
 
 【user】
 请把下面的原始视频选题提炼成广告封面图使用的主题种子。
-主题种子的目标用户是 60-75 岁中老年用户。
-主题种子要包含:中老年用户 + 核心问题/信息差 + 点击钩子方向。
-保留目标用户会关心的痛点、情绪冲突、信息差、生活提醒价值、可视化方向等关键维度,不局限于这些维度。
-不要复述视频镜头、口播包装、主持人、分屏结构、字幕设计或完整事件脚本。
 
-要求:
-1. 保留原始选题背后的核心主题,例如退休补贴信息差、AI诈骗防范、拒绝内耗、晚年生活选择、家庭关系反差等。
-2. 可以把具体事件转译成更普适、更安全、更有点击钩子的广告主题,但不能改成无关故事。
-3. 高风险细节要转成中老年人相关的生活提醒、防范意识或信息差,不要复述拐骗骗局、犯罪过程、恐吓画面、儿童危险、诈骗操作步骤。
-4. 去掉转发、关注、评论、公众号回复、私信、扫码、加群、领取、点击、下载、底部提示、推广话术等互动/营销引导。
-5. 不要写能领取、已办成、保证可领、赶紧去看、不错过福利等结果承诺或催促行动。
-6. 不要写假官方通知、系统提示、按钮、二维码,不要把画面设定为手机通知界面。
-7. 输出 50-110 个中文字符,一句话,要有“为什么想点开”的广告主题感。
-8. 如果原始选题包含拐骗、诈骗、犯罪等高风险词,主题种子应优先写成“日常搭话、陌生求助、熟人提醒、热心帮忙、生活防范、容易忽略的小细节”等低风险表达。
-9. 不要在主题种子中出现“孩子、儿童、未成年人、拐骗、犯罪、修车求助、诈骗步骤、作案手段”等具体高风险对象或动作;如原始选题包含这些内容,请转译成中老年人更相关的生活防范提醒。
+目标用户:45-75 岁中文信息流用户。
+
+提炼原则:
+1. 保留原始选题的核心问题、信息差、反差、情绪价值或生活提醒价值。
+2. 目标用户不等于画面主体;不要为了说明目标用户而强行写“老人、中老年、退休、晚年、长辈”。
+3. 只有原始选题本身强相关时,才可以保留年龄、退休、养老等表达。
+4. 去掉镜头结构、口播包装、字幕设计、转发关注、公众号回复、加群、领取、点击、下载等投放话术。
+5. 高风险事件只提炼成普适提醒或信息差,不要复述犯罪过程、恐吓细节、儿童危险或操作步骤。
+6. 输出 35-80 个中文字符,一句话,要让人知道“为什么想点开”,但不要像假通知或强诱导广告。
+
+禁止词:
+领取、能领、已办成、赶紧、通知、不错过、转发、关注、公众号、加群、优惠券、卡券、下单、购买、扫码、下载。
 
 原始视频选题:
 {{raw_description}}

+ 47 - 5
examples/auto_put_ad_mini/tools/ai_generated_material.py

@@ -58,6 +58,7 @@ AI_COVER_COPY_REQUIRED = os.getenv("AI_COVER_COPY_REQUIRED", "1").strip().lower(
     "0", "false", "no", "否",
 )
 AI_COVER_COPY_MAX_TOKENS = int(os.getenv("AI_COVER_COPY_MAX_TOKENS", "900"))
+AI_COVER_COPY_TITLE_MAX_LEN = int(os.getenv("AI_COVER_COPY_TITLE_MAX_LEN", "22"))
 AI_IMAGE_OSS_PREFIX = os.getenv("AI_IMAGE_OSS_PREFIX", "auto_put_tencent/image").strip("/")
 AI_IMAGE_ASPECT_RATIO = os.getenv("AI_IMAGE_ASPECT_RATIO", "16:9")
 AI_IMAGE_RESOLUTION = os.getenv("AI_IMAGE_RESOLUTION", "1K")
@@ -144,6 +145,7 @@ class CoverCopy:
     main_title: str
     hook_angle: str = ""
     hook_point: str = ""
+    highlight_terms: list[str] | None = None
     reason: str = ""
     candidates: list[dict[str, Any]] | None = None
 
@@ -152,6 +154,7 @@ class CoverCopy:
             "main_title": self.main_title,
             "hook_angle": self.hook_angle,
             "hook_point": self.hook_point,
+            "highlight_terms": self.highlight_terms or [],
             "reason": self.reason,
             "candidates": self.candidates or [],
         }
@@ -395,9 +398,11 @@ def _normalize_cover_copy_candidates(value: Any) -> list[dict[str, Any]]:
     for item in value:
         if not isinstance(item, dict):
             continue
-        title = _clean_cover_copy_text(item.get("title"), max_len=28)
+        title = _clean_cover_copy_text(item.get("title"), max_len=64)
         if not title:
             continue
+        if len(title) > AI_COVER_COPY_TITLE_MAX_LEN:
+            continue
         normalized = dict(item)
         normalized["title"] = title
         for key in ("hook_score", "plain_score", "relevance_score"):
@@ -423,6 +428,29 @@ def _best_cover_copy_candidate(candidates: list[dict[str, Any]]) -> dict[str, An
     )[0]
 
 
+def _normalize_highlight_terms(value: Any, *, title: str) -> list[str]:
+    if not isinstance(value, list):
+        return []
+    blocked = {
+        "视频里", "看完", "原来", "很多人", "这几件事", "才明白",
+        "国家", "补贴", "领取", "记性变差", "儿子", "女儿",
+        "病床", "诈骗", "犯罪",
+    }
+    out: list[str] = []
+    for item in value:
+        term = _clean_cover_copy_text(item, max_len=8)
+        if not term or term in blocked:
+            continue
+        if term not in title:
+            continue
+        if term in out:
+            continue
+        out.append(term)
+        if len(out) >= 2:
+            break
+    return out
+
+
 def generate_cover_copy(video_description: str, selection, model: str = OPENROUTER_TEXT_MODEL) -> CoverCopy:
     """Generate the cover title before image generation.
 
@@ -449,15 +477,27 @@ def generate_cover_copy(video_description: str, selection, model: str = OPENROUT
     parsed = _extract_json_object(_extract_chat_completion_text(resp.json()))
     candidates = _normalize_cover_copy_candidates(parsed.get("candidates"))
     best_candidate = _best_cover_copy_candidate(candidates)
-    main_title = _clean_cover_copy_text(parsed.get("selected_title") or parsed.get("main_title"), max_len=28)
+    main_title = _clean_cover_copy_text(
+        parsed.get("selected_title") or parsed.get("main_title"),
+        max_len=64,
+    )
+    if len(main_title) > AI_COVER_COPY_TITLE_MAX_LEN:
+        main_title = ""
     if not main_title and best_candidate:
         main_title = str(best_candidate.get("title") or "")
     if not main_title:
-        raise RuntimeError("OpenRouter 标题节点未返回 main_title")
+        raise RuntimeError(
+            f"OpenRouter 标题节点未返回 {AI_COVER_COPY_TITLE_MAX_LEN} 字以内的 main_title"
+        )
+    highlight_terms = _normalize_highlight_terms(
+        parsed.get("highlight_terms"),
+        title=main_title,
+    )
     return CoverCopy(
         main_title=main_title,
         hook_angle=str(parsed.get("hook_angle") or (best_candidate or {}).get("hook_angle") or "").strip()[:50],
         hook_point=str(parsed.get("hook_point") or "").strip()[:160],
+        highlight_terms=highlight_terms,
         reason=str(parsed.get("reason") or "").strip()[:200],
         candidates=candidates,
     )
@@ -473,8 +513,9 @@ def _render_pattern_prompt(video_description: str, selection, cover_copy: CoverC
 【封面标题】
 - 主标题:{cover_copy.main_title}
 - 标题钩子:{cover_copy.hook_angle or "未标注"}
+- 高亮词:{",".join(cover_copy.highlight_terms or []) or "无"}
 
-图片中必须使用上面的主标题。不要生成副标题,也不要自行新增其他标题、角标或引导文字。
+图片中必须使用上面的主标题。只允许高亮【高亮词】中列出的词;如果高亮词为“无”,不要强行彩色高亮。不要生成副标题,也不要自行新增其他标题、角标或引导文字。
 """
     enriched_description = f"""【广告主题种子】
 {video_description}
@@ -486,12 +527,13 @@ def _render_pattern_prompt(video_description: str, selection, cover_copy: CoverC
 - hook_category:{pattern.hook_category}
 - visual_template:{pattern.visual_template}
 - title_hook_rule:{pattern.title_hook_rule}
-- visual_rule:{pattern.visual_rule}
+- visual_direction:{pattern.visual_rule}
 - relevance_rule:{pattern.relevance_rule}
 - compliance_rule:{pattern.compliance_rule}
 - positive_examples:{positive_examples}
 - negative_examples:{negative_examples}
 
+visual_direction 只是可选视觉方向参考,不是硬约束;如果它和画面主体多样性、非默认人物、非默认老人正脸冲突,以全局视觉策略为准。
 请基于【广告主题种子】和【选中的创意Pattern】生成广告封面。视频内容只提供主题方向,画面可以做适合信息流点击的合理转译。"""
     return _render_prompt(enriched_description)