make-ai-short — 生成式短视频,端到端
像素来源于视频模型;Remotion 仅负责合成片段 + 字幕。已在 blue-man 第 1 集“The Door”上验证。
与 /make-short(纯 TSX)为兄弟技能——遵循相同纪律,外加 TSX 从未需要的三道关卡:生成前表明成本、从 character.json 锁定角色、通过结束帧条件固定循环。
所有操作在仓库根目录执行。需要 .env 中的 FAL_KEY(片段 + 图像)和 ELEVENLABS_API_KEY(语音)。
三条铁律
- 切勿从文本重复生成角色。
ai-shorts/<char>/character.json+character.png是锁定的参考。每个需要该角色的静态图像都使用gen_image.py --ref character.png;每个片段都是从已包含该角色的帧进行图像到视频的转换。如果角色表尚不存在,则在其他任何步骤之前,制作一个(并获得用户确认)是其自身的独立步骤。 - 在花费之前说明成本。 推导得出,非引用:Seedance 1080p 无音频 =
h×w×24×dur/1024tokens × $1.2/1M ≈ $0.0583/秒(fal 定价 API 已验证至 2026-07-14——完整表格及 Wan/Veo-Lite 陷阱请参见 ai-shorts/IDEAS.md)。始终使用generate_audio:false——语音由 ElevenLabs 负责,音效使用我们的 SFX 管线。 - 通过约束而非运气实现循环。 镜头 1 的真正帧 0(从片段中提取,而非提示图像)是循环目标。最后一个镜头将其作为
end_image_url传入(Seedance/Kling),合成在最后约 10 帧上稳定到该确切静态帧(remotion/src/shots/ai-1/Ai1Door.tsx中的LoopSettle模式)。
产物约定
ai-shorts/<name>/
script.md · beats.json — 与 /make-short 相同约定(vo[] 包含真实单词时间)
character.json — 锁定参考(+ video_model 块 = 所选模型)
shots/NN-slug.png — 每个镜头的起始帧(gen_image.py --ref character.png [--ref scene])
shots/NN-slug.json — gen_clip 侧车文件(model, payload, request id)
shots/upload-urls.json — fal 存储 URL(上传在同一组重新生成中可重复使用)
voice/ · sfx-plan.json · output/ [voice/output 在 gitignore 中]
remotion/src/shots/ai-N/ — composition + vo.gen.ts
media/projects/<name>/ — 用于 staticFile() 的片段副本(已提交——像素不可重现;shots/*.mp4 工作副本在 gitignore 中)
操作顺序(语音优先于像素)
- 脚本 — script.md + beats.json。钩子在帧 0 处合成;主题应需要循环(blue-man 第 1 集:“到达是一个神话”→ 结尾 = 走向同一扇门)。约 80-100 单词,窗口 2.5-2.7 wps。绝无吸引互动的片尾,永远没有——在回报处结束,循环就是结局。
- 语音优先 —
python tools/gen_voice.py --beats ... --emit-ts remotion/src/shots/ai-N/vo.gen.ts(语音和模型来自.env,或当系列需要解说员(如 GeorgeJBFqnCBsd6RMkjVDRZzb,带 eleven_v3 标签)时来自 beats.json 的"voice"块;在生成任何片段之前,像 make-short 阶段 4 那样处理 LINE CHECK 标志)。真实单词时间决定镜头窗口;Seedance 接受整数 4-12 秒时长,因此从窗口向上取整镜头长度。 对话(两个或更多角色): beats.json 的"cast"将每个说话者映射到其character.json(使用其"voice"块),每个旁白行在其"who"中指定说话者,--pack 0.3根据真实长度将各行首尾相连,而不是将它们挤入估算的窗口。每个角色一个语音,存放在其 character.json 中,别无他处。 对话中的口型: 视频模型不知道谁在说话;它会让听众说话,冻结说话者,并在下一句上延长笑声。生成片段后,运行python tools/mouth_sync.py ai-shorts/<name>(beats.json 的"shots"为每个片段提供全局起始和长度;每个 character.json 的"mouth"块提供侧面、半径、区域、样式和keep_above)。它会擦除模型的口型,并根据语音轨道的响度绘制每个角色自己的口型。以相同方式进行 QA:每半秒检查两个面部与说话者的对应关系。一行内的动作变化(大笑,然后说话)需要两个片段,第二个片段从第一个片段在剪切时刻的帧开始。然后逐帧扫描每个口型同步片段:超过片段中位数四倍的尖峰是剪切点、快速手势或带有鬼影特征的两帧变形;在该镜头的"hold"中列出变形帧并重新运行,以便跳跃表现为干净的剪切。 - 起始帧 — 每个镜头一个,
python tools/gen_image.py --model pro --aspect 9:16 --ref character.png(+ 当场景/道具需跨镜头匹配时,镜头 1 的场景静态帧作为第二个参考)。在任何视频调用之前对所有 PNG 进行 QA——在这里修复漂移成本最低。- 注意手部、衣物细节、比例——在重新生成时在提示中明确命名它们。
- 几何形状必须可步行(镜头 6 的教训,花费 2 次重新生成):使用结束帧固定时,起始帧必须允许动作通过向前步行到达结束状态——如果结束帧的角色比起始帧的离镜头更近,模型会生成一个克隆角色而非移动。从起始角色比结束状态更/近开始,以便向前运动使其缩小到结束状态。
- 片段 — 说明总派生成本,然后并行运行
python tools/gen_clip.py每个镜头(--set image_url=<fal storage url> --set 'duration="N"' --set resolution=1080p --set generate_audio=false,最后一个镜头添加--set end_image_url=<frame0 url>)。通过bakeoff_clip.upload_ref一次性上传参考;将 URL 保存在 shots/upload-urls.json 中。 以上 fal 形式是此签出的默认配置(CLIP_PROVIDER=fal,.env中的FAL_VIDEO_MODEL)。 使用 kie.ai 代替(--provider kie): 字段名称遵循 kie 模型,而非 fal。对于bytedance/seedance-2:--aspect none --set first_frame_url=@ai-shorts/<name>/shots/NN-slug.png --set duration=N --set resolution=1080p --set generate_audio=false,最终镜头--set last_frame_url=@<frame0.png>。@path上传本地文件(kie 保留 24 小时,因此 upload-urls.json 的重复使用仅在一天内有效)。成本:kie.ai/pricing,而非 fal 表;运行时会打印使用的积分。bakeoff_clip.py仍仅限 fal。 为每个片段编写提示:精确的角色短语 + “整个镜头中场景内恰好一个角色”(克隆是头号失败原因)+ 镜头移动 + “无变形” + 风格行。 - 片段 QA,逐帧进行 — 每个片段起始/中间/结束帧条 + 最终片段最后一帧与循环目标的并排对比。重新生成失败片段(说明重新生成成本);5 秒 1080p 重新生成约 $0.29——相比交付一个克隆的代价要低。
- 合成 — 单时间轴上
OffthreadVideo序列,约 8 帧交叉淡入淡出重叠(片段比其窗口长约 1 秒——这部分是余量),根目录的Captions+ProgressBar,尾部LoopSettle溶解。将最终片段复制到media/projects/<name>/。运行npm run gen,在边界和关键帧处使用 frames.mjs 读取每个 PNG,然后 render-all --scale=1 并混入 voice.wav。 - 音效 — 遵循 /suggest-sfx 流程。空隙旁白校准(blue-man 第 1 集): 旁白空隙中的提示音效不进行掩蔽和闪避——它们需要比密集旁白表低约 7-8 dB(空隙嗖嗖声 -9..-13,而非 -3..-5)。使用 volumedetect RMS 对照语音均值参考进行验证;主层音量可能达到语音级别。试听混音等待用户耳朵(硬门限)。
- 保存回传 — 记录 used_in,beats.json 真实时间(gen_voice 写入),提交侧车文件,成本账目记录在最终报告中。
“完成”的定义 =
每个片段的每一帧中角色一致(已逐帧 QA)· 循环已验证(最后一帧与帧 0 并排对比)· 字幕在真实单词时间上 · 每次花费前已说明成本且总成本已汇总 · 音效试音已渲染,等待用户耳朵 · 无吸引互动片尾。
· tools/check_text.py 对口语和屏幕上的文本检查通过 · 使用 tools/export_phone.py 制作手机副本 · LESSONS.md 中有日期记录(如果经验教训改变了工作方式,技能以通用术语更新)。