完成极速成片

This commit is contained in:
Azmat@qq.com
2026-08-25 18:46:20 +08:00
parent df6784b90c
commit 2f70d3e8a0
15 changed files with 495 additions and 146 deletions
+33 -7
View File
@@ -81,7 +81,7 @@ PERSONA_BRIEFS: dict[str, str] = {
"urban": "25–32岁都市白领,工位或下班回家,说话像跟同事吐槽,不要主播腔",
"bestie": "闺蜜分享口吻,带点兴奋,爱用「你懂的」「我跟你讲」",
"ceo": "利落、判断句、少形容词,像拍板不是带货",
"reviewer": "先讲怎么试的再给结论,允许提一个小缺点才可信",
"reviewer": "先讲怎么试的再给结论;可信度来自过程、适用边界或意外发现,不编造小缺点",
"mom": "带娃/家务间隙,讲省事,孩子或家人能沾边",
"genz": "宿舍或通勤,短句,像给朋友发语音条",
}
@@ -103,10 +103,18 @@ _SHOT_SIZE_MARKERS = (
"手持", "跟拍", "俯拍", "仰拍", "推近", "拉远", "",
)
_MINOR_CHARACTER_RE = re.compile(
r"(?:婴儿|宝宝|宝贝|幼儿|儿童|小孩|小朋友|未成年|男童|女童|baby|toddler|infant)"
r"|(?:[0-9零一二三四五六七八九十]{1,3}\s*岁)",
r"(?:婴儿|宝宝|宝贝|幼儿|儿童|小孩|小朋友|未成年|男童|女童|baby|toddler|infant)",
re.IGNORECASE,
)
_NUMERIC_AGE_RE = re.compile(r"(?<!\d)(\d{1,2})\s*岁")
_CHINESE_MINOR_AGE_RE = re.compile(r"(?:[零一二三四五六七八九]岁|十岁|十[一二三四五六七]岁)")
_CANNED_DEFECT_RE = re.compile(r"唯一(?:的)?(?:小)?(?:缺点|不足|遗憾)")
_NATURAL_TURN_PHRASES = (
"我会额外留意的使用细节",
"换个场景更能看出差别的地方",
"下单前可以先确认的一点",
"实测时我更在意的细节",
)
_FORMAT_KEY_BY_LABEL = {label: key for key, label in PRESENTATION_FORMATS.items()}
@@ -231,7 +239,10 @@ def format_visual_beats(beats: list[tuple[int, int, str]]) -> str:
def _is_minor_character(name: str, visual_prompt: str) -> bool:
"""角色基础资产不得是未成年人,避免真人生图审核拦截与儿童肖像风险。"""
return bool(_MINOR_CHARACTER_RE.search(f"{name or ''} {visual_prompt or ''}"))
text = f"{name or ''} {visual_prompt or ''}"
if _MINOR_CHARACTER_RE.search(text) or _CHINESE_MINOR_AGE_RE.search(text):
return True
return any(int(age) < 18 for age in _NUMERIC_AGE_RE.findall(text))
def _product_only_visual(duration: int) -> str:
@@ -253,6 +264,14 @@ def _product_only_visual(duration: int) -> str:
return "【本镜任务】用商品本身传达关键信息,不出现人物。\n【声音】旁白继续,画面不出现未成年人。\n【画面内容】\n" + format_visual_beats(beats)
def _replace_canned_defect_phrase(text: str) -> str:
"""不改模型给出的事实,只替换会让测评显得千篇一律的“唯一缺点”话术。"""
if not text or not _CANNED_DEFECT_RE.search(text):
return text
variant = _NATURAL_TURN_PHRASES[sum(map(ord, text)) % len(_NATURAL_TURN_PHRASES)]
return _CANNED_DEFECT_RE.sub(variant, text)
# --------------------------------------------------------------------------- #
# skill 加载(缓存)
# --------------------------------------------------------------------------- #
@@ -613,6 +632,11 @@ def build_agent_messages(
f"严格按已加载的「{PRESENTATION_FORMATS[fmt]} × {VIDEO_STRUCTURES[structure]}」套路写,"
f"不要串成别的结构的套话。\n"
)
authenticity_line = (
"【真实感转折】禁止使用「唯一缺点/唯一不足/唯一的小遗憾」这类模板句,也不要为了显得真实而编造缺点。"
"每版从以下角度自然选一个推进:测试过程里的意外发现、适用人群的边界、不同使用场景的反差、"
"一个可观察的细节、或使用习惯建议;必须由商品资料或画面可观察事实支持,不能每镜重复同一种。\n"
)
head = (
f"【画幅】{aspect_ratio}\n"
f"【表现形式】{PRESENTATION_FORMATS[fmt]}(套路见 playbooks/format-{fmt}.md,已加载)\n"
@@ -623,6 +647,7 @@ def build_agent_messages(
f"{beats_line}"
f"{structure_line}"
f"{combo_line}"
f"{authenticity_line}"
f"【商品信息】\n{_product_context(project, selling_point_ids, persona)}"
)
if mode == "revise" and base_draft and target_index is not None:
@@ -1196,7 +1221,7 @@ def normalize_draft(
if not line:
continue
sp = d.get("speaker")
dialogue.append({"speaker": sp if sp in valid_ids else None, "line": line})
dialogue.append({"speaker": sp if sp in valid_ids else None, "line": _replace_canned_defect_phrase(line)})
# 旁白:结构化对白/lines 优先,其次整句字符串 dialogue,再退到通用字段解析(narration/voiceover/caption/字幕…)
narration = ""
if isinstance(raw_dialogue, str) and raw_dialogue.strip():
@@ -1205,8 +1230,9 @@ def normalize_draft(
narration = " ".join(d["line"] for d in dialogue) # 扁平拼接,兼容下游字幕/配音
if not narration:
narration = _pick_field(seg, _NARRATION_EXACT, _NARRATION_FUZZY)
narration = _replace_canned_defect_phrase(narration)
# 画面:优先收成秒级分镜;beats 数组会折进 visual,下游故事板/视频直接读这一段。
visual = compose_segment_visual(seg)
visual = _replace_canned_defect_phrase(compose_segment_visual(seg))
norm_segments.append(
{
"index": i,
@@ -1255,7 +1281,7 @@ def normalize_draft(
elif index < len(segments) and isinstance(segments[index], dict):
composed = compose_segment_visual(segments[index], seconds)
if composed:
norm["visual"] = composed
norm["visual"] = _replace_canned_defect_phrase(composed)
draft["segments"] = norm_segments
draft["segment_count"] = len(norm_segments)