旁白演示
一个智能体,一个下午,一个 VP 能在手机上观看的 .mov 文件。经过五个视频片段(时长 69–188 秒)验证的配方:
- 访谈用户——五个问题,不超过(见下文)。根据你已有的上下文起草故事;用户进行编辑,而非写作。
- 编写场景规格(
clips/<name>.yaml):4-8 个场景,每个场景旁白约 25-45 字(≈ 10-18 秒演讲)。每个场景都有一个核心内容——事物出现的时刻——视频剪辑使得核心内容尽早显示,而非最后。 - 渲染:
scripts/render_clip.py clips/<name>.yaml。按场景处理:TTS → 捕获 → 将视频适配到音频 → 字幕 → 混流;然后合并。 - 审查渲染器生成的静态图像(
out/_<clip>_<scene>.png),使用read工具确认无误后再声明完成。使用--only s2重新渲染单个场景。 - 发布(可选):
scripts/slack_post.py <channel_id> out/<clip>.mov "<简介>"。
所有步骤均可重新运行且已缓存(TTS 通过文本哈希缓存,按场景缓存视频),因此迭代一句话仅需数秒。
1. 访谈(依次提问,然后起草)
一次性提出全部五个问题;接受部分答案,并从上下文中补全其余信息,同时标注你做出的假设。
- 视频给谁看,他们有多少时间?(同行工程师 3 分钟 · 高管 90 秒 · Slack 滚动 60 秒)→ 确定场景数量和节奏。
- 他们看完后应该相信什么? → 成为场景 1 的第一句话和最后一场景的最后一句话。
- 屏幕上必须显示什么?(用户界面、终端、文档、图表、文件)→ 决定捕获类型。询问 URL / 命令 / 路径。
- ‘实时’时刻是什么? 某个可见的、正在发生的事件(查询返回、构建完成、数字出现)。每个视频至少一个。
- 语音和语气? 默认:
onyx,“平静、清晰、自信的技术演示旁白,中等语速,数字稍加重”。如果他们想要更轻松的,可以提供alloy/nova。
然后以表格形式提出分镜故事板(场景 · 屏幕内容 · 一行旁白要点 · 核心内容)并获得确认后再渲染。渲染一个 5 场景的视频大约需要 3-6 分钟;在获得确认前不要渲染。
2. 场景规格
clip: 1-setup # 输出名称 → out/1-setup.mov
size: 1440x900 # 可选:所有场景的通用帧大小(适配并填充)——在混合屏幕和浏览器场景时设置
voice: onyx
instructions: "平静、清晰、自信的技术演示旁白。中等语速,在有句号处自然停顿。数字稍加重。"
scenes:
- id: s1 # 简短、稳定的 ID → 缓存键
caption: "主题 · 一个图表,四项证据" # 底部字幕;保持 ≤ 60 字符
narration: "…25-45词…" # 如果 TTS 处理数字有问题,请用文字写出数字(如“一万八千”)
# 每个场景仅一个捕获源:
url: "http://127.0.0.1:4300/#mode=purchase" # (a) 通过 Playwright 的浏览器捕获——外加可选操作:
actions: # 在加载后按顺序执行;`ready` 标记核心内容
- fill: ["#prompt", "Acne Studios"]
- press: ["#prompt", "Enter"]
- wait: ".layer-chip" # 选择器(45 秒超时)→ 设置 ready
- click: "button:has-text('fit')"
- select: ["#graph-layout", "brand"]
- scroll_to: "#join" # scrollIntoView(用于长文档)→ 设置 ready
- hover: ".card"
# video: "cache/cr_s3" # (b) 预录制目录(page.mov + marks.json)或文件——参见 record_screen.sh
# still: "docs/figure.png" # (c) 静态图像(图表、幻灯片)——无 Ken Burns 效果,按旁白时长保持显示
hold: 2 # 最后一次操作后额外等待的秒数
speed: 1 # 预加速源视频(8 = 慢速爬升以 8 倍速度显示)
scale: 2560 # 按场景缩放的宽度(屏幕录制);font: 28 = 此场景的字幕像素大小
使视频效果良好的规则:
- 核心内容优先。 渲染器在
ready之前约 2.5 秒开始每个场景,如果旁白更长则填充最后一帧,如果更短则裁剪。一个场景的“核心内容”在 45 秒录制的第 40 秒出现,它将变成一个从核心内容开始的 12 秒场景。 - 旁白与屏幕一致。 旁白中每个大写名称和每个数字必须在屏幕上可见或即将可见。删除那些没有的画面语句。
- 每个场景一个想法。 如果一个句子以“此外”开头,那么它应该是一个新场景,或者被删除。
- 字幕即大纲。 只阅读字幕就应该能了解整个故事。
- 绝不虚构旁白中的数字。 从你刚获取的结果文件或实时响应中获取;如果四舍五入,请说“大约”。
3. 捕获源
- 浏览器(
url+actions) — Playwright Chromium,1440×900,无头模式,录制 webm。需要包含@playwright/test和 Chromium 的 Node 项目;设置PW_REPO=/path/to/that/project(默认:技能自带的pw/目录——运行scripts/setup.sh一次来创建)。 - 屏幕 / 终端(
video) —scripts/record_screen.sh <scene> <seconds> [--rect x,y,w,h] -- <command…>:启动screencapture -V,运行你的命令,当命令通过打印MARK表示核心内容已就绪时,写入带有started(毫秒)的marks.json。 陷阱:screencapture -V仅在像素变化时前进;静态的终端窗格会冻结录制。保持某个内容在变化(如 1 Hz 时钟行),或使用show_file.sh为文件分页并附带时钟。使用DEMO_SCALE=2560缩小宽屏捕获。 隐私: 录制一个矩形区域(--rect,以点为单位;screencapture -R),以便仅捕获你想要的窗格——不包含窗口标题、标签栏、侧边栏、停靠栏、通知或背景窗口。在发布之前,阅读每张审查静态图像并询问:屏幕上是否有任何内容能识别雇主、主机、内部 URL、其他人?对你分页的文本进行脱敏处理(show_file.sh)——它只是演示内容,而不是你的真实文件。 - 静态图像(
still) — 一个 PNG/JPG,按旁白时长保持显示;用于图表、表格、幻灯片。 - 预录制(
video: file.mov) — 配合场景中的ready: <seconds>使用。
4. 渲染、审查、发布
export OPENAI_API_KEY=… # TTS(gpt-4o-mini-tts);或 TTS_URL=https://<proxy>/v1/audio/speech TTS_API_KEY=… 用于兼容代理
scripts/render_clip.py clips/1-setup.yaml # 完整视频 → out/1-setup.mov(+ out/_1-setup_<scene>.png 静态图像)
scripts/render_clip.py clips/1-setup.yaml --only s2 # 重新渲染单个场景,重新合并
DEMO_FONT=26 scripts/render_clip.py … # 字幕大小(1440 px 时 22,2560 px 源时 26-30)
审查:使用 read 查看两到三张 out/_*.png 静态图像。如果浏览器场景记录 wait failed: … Timeout,说明应用过慢或选择器已更改:该场景仍从 t=0 开始渲染(没有核心内容裁剪)——修复选择器,或当应用空闲时使用 --only <id> 重新运行该场景。检查字幕是否清晰、核心内容是否在屏幕上、没有敏感信息可见(令牌、买家级行、其他人的消息)。如果可以,观看 .mov 文件的前 5 秒和后 5 秒。
发布到 Slack,附带不超过三行的简介:内容 · 时长 · 一个数字。scripts/slack_post.py C0123… out/1-setup.mov "…" 使用 ~/.config/slack-mcp/credentials.json 中的 slack-mcp 浏览器会话凭据;如果不存在,请将文件连同简介一起交给用户。
5. 约定(保持项目整洁)
clips/ 规格文件 · cache/ TTS 和每场景视频(已加入 gitignore) · out/ 最终文件和静态图像 · blurbs/ Slack 文本。按播放顺序命名视频(0-…, 1-…, 2a-…)。保留一个 storyboard.md,包含访谈中的表格;这是视频制作后人们会要求查看的产物。
依赖项:ffmpeg/ffprobe,node ≥ 18,Playwright Chromium(用于浏览器场景),macOS screencapture(用于屏幕场景),一个 TTS 密钥。scripts/setup.sh 会检查并安装它所能安装的内容。