最近,WorkBuddy 的模型列表里把 Hy3 标成了限时免费。

活动通知写得很直接,0 积分替你把活干完。

image.png

看到活动以后,第一反应不是拿它测一道题,也不是让它多写几段文案。我在想,能不能趁这个机会多跑几次,把 WorkBuddy 和 CodeBuddy 接进自己的工作流,替掉一部分原来反复放在 Codex 里做的工作。

前段时间,我刷到一条用 Codex 制作拼贴动画的视频。

那种效果并不复杂。背景像一本摊开的绘本,人物、动物和道具是单独的纸片,配合旁白在画面里移动。但真要自己做,后面是一长串零碎工作。

写故事,拆分镜,列素材,逐张生图,抠图,统一尺寸,配音,切字幕,写 Remotion 动画,渲染后再抽帧检查。

每一步单独拿出来都不难。

难的是每次换一个故事,这些事又要从头做一遍。

所以这次我没有急着生成某一条具体视频。我先想做一个 WorkBuddy 版的通用 Skill。

以后只需要给它一个故事、主题或文章,WorkBuddy 就在同一个任务里自己完成脚本、分镜、素材生成、rembg 抠图、VoxCPM 配音、Remotion 动画和成片 QA。

Skill 不是一条更长的提示词

我以前也容易把 Skill 看成一份很长的 Prompt。

真正开始写这套 Skill 后,我更愿意把它叫作一份可以执行的制作规范。提示词只是其中一层,真正让结果稳定下来的,是规则、数据协议、脚本和工程模板一起工作。

这套纸质拼贴动画 Skill 的主体结构大致如下。

    paper-collage-video/
    ├── SKILL.md
    ├── references/
    │   ├── video-script-template.md
    │   ├── prompt-library.md
    │   ├── storyboard-schema.md
    │   ├── background-aware-motion.md
    │   ├── subtitles.md
    │   └── voiceover.md
    ├── scripts/
    │   ├── check_environment.py
    │   ├── remove_background.py
    │   ├── normalize_asset.py
    │   ├── scaffold_remotion.py
    │   ├── validate_background_motion.py
    │   └── validate_subtitles.py
    └── assets/remotion-template/
        ├── src/
        ├── storyboard.json
        ├── path-editor.mjs
        ├── path-editor.html
        └── path-editor.js

WorkBuddy Skill 结构与视频生产线

SKILL.md 负责规定执行顺序,也写清楚哪些事情不能省。

例如,图片必须由 WorkBuddy 在同一个任务里逐张生成,不能只输出一份素材提示词就停下。背景和可移动主体必须分开生成。每一个需要独立运动的角色,都要有自己的一张绿幕素材。

references 里放的是会被重复读取的专业规则。脚本模板告诉 WorkBuddy 怎么拆镜头,提示词库负责约束写实微缩绘本、剪纸拼贴、水彩绘本等不同画风,分镜协议规定 Remotion 到底要读取什么数据。

scripts 解决的是靠提醒很难稳定完成的动作。

检查 rembg 是否存在,确认模型有没有缓存,裁掉透明图片四周不一致的空白,验证字幕里有没有标点,检查角色路线是否穿过石头或树干,这些都不适合只写一句请仔细检查。

Remotion 模板负责把前面的规则落到画面上。故事换了,素材换了,分镜数据换了,底层的逐帧运动、图层合成和渲染方式不需要重写。

这样拆开以后,Skill 才不再是一段只能祈祷模型听懂的文字。

它开始有了可以验证的输入和输出。

把一次生成拆成可以恢复的生产线

我给 WorkBuddy 规定的完整流程是这样的。

    用户主题
      ↓
    video-script.md
      ↓
    assets-manifest.md
      ↓
    WorkBuddy 逐张生成素材
      ↓
    rembg 抠图和尺寸统一
      ↓
    VoxCPM2 生成旁白
      ↓
    storyboard.json
      ↓
    Remotion 渲染
      ↓
    ffprobe 和抽帧检查

WorkBuddy Skill 完整视频生产流程

这九步其实可以分成四段。

用户主题、video-script.md 和 assets-manifest.md 负责把故事想清楚。前者确定横竖版、时长和画风,中间那份脚本把内容拆成镜头,素材清单再把每个镜头需要什么图片写成明确合同。

WorkBuddy 生图、rembg 和 VoxCPM2 负责准备可以进入时间线的素材。图片需要经过生成、抠图和尺寸统一,旁白则要先得到真实音频时长,镜头帧数才有可靠依据。

storyboard.json 是交接点。前面的故事、图片、声音和路径,到这里全部变成帧、图层和关键帧数据。Remotion 不再临场猜故事,只读取这份数据逐帧渲染。

ffprobe 和抽帧检查负责决定任务是否真的结束。分辨率、音轨、字幕、绿边和路径接触点有任何一项不对,就返回对应阶段修改,不推倒整条生产线。

步骤多并不可怕。每一步都会留下可以检查的文件,这才让它成为一条能恢复的生产线。

如果 WorkBuddy 已经生成了十张合格素材,第十一张出了问题,下次运行时应该继续补第十一张,而不是把前面十张全部重做。

如果只是角色路线不对,也不该重新生图、重新抠图、重新配音。修改 storyboard.json 后,从路线校验和 Remotion 渲染继续就够了。

这也是我后来越来越在意的一点。

一键生成不等于把所有动作塞进一个黑盒里。真正好用的一键流程,应该知道自己执行到了哪里,也允许人从中间接手。

生产线开始前还有一道环境闸门。

check_environment.py 只检查,不会擅自安装。它会确认 Node.js、npm、FFmpeg 和 rembg 是否可用,也会检查 rembg 模型是否已经缓存。环境闸门还会按照 voiceover.md 的规则检查本地 VoxCPM2。

如果 rembg 不存在,Skill 必须停下来询问是否安装。模型需要首次下载时,也要先取得同意。Remotion 依赖优先复用本机已有目录,找不到时才询问是否执行 npm install。

这看起来比直接自动安装多了一步,却避免了工作流为了生成一条视频,悄悄改掉整台电脑的环境。

素材尺寸不是小问题

前景素材统一生成绿幕版本,再交给 rembg 抠成透明 PNG。

抠完以后还要做一次 normalize。

脚本先裁掉透明空边,再把主体按指定占比放回 1024×1024 的透明画布。人物和陆行动物默认底部对齐,主体占画布大约 72% 到 82%。

我一开始也觉得这是后期细节。

真正做动画时才发现,同样是两张 1024×1024 的角色图片,如果一张周围留白很多,另一张贴近边缘,在 Remotion 里给它们相同的 width,画面里的实际大小仍然不同。

角色忽大忽小,有时与缩放曲线无关,问题出在素材边界不一致。

所以在这套 Skill 里,生成尺寸、主体占比和 Remotion 显示宽度被当成三个不同的数据。不能靠临时改一个 width,把前面的素材问题盖过去。

前景素材从绿幕到 Remotion 图层的处理过程

配音和字幕也分开处理。

旁白由本机 VoxCPM2 生成,沿用已经准备好的参考音频。配音原文保留正常标点,因为停顿和语气需要它。

屏幕字幕则按语义切成 2 到 14 个汉字的短句,一次只出现一条,不带标点,太长会显得比较拥挤,占据更多的屏幕空间。

渲染前,validate_subtitles.py 会检查长度、标点、换行、时间重叠和停留时长。这样一句字幕不要有标点,才从偏好变成了可以执行的规则。

storyboard.json 是整个 Skill 的数据协议

素材准备好以后,画面怎么动,不再由 WorkBuddy 临场发挥。

所有镜头都写入 storyboard.json。视频尺寸、帧率、场景时长、背景、角色图层、字幕、相机运动和配音时间,都以这份文件为准。

我没有让 Remotion 去理解故事。

Remotion 只负责按照数据一帧一帧地画。

下面是一段经过简化的角色路线数据。

    {
      "durationInFrames": 120,
      "background": "assets/river-bank.jpg",
      "backgroundMap": {
        "horizonY": 0.34,
        "blockedZones": [
          {"id": "deep-water", "x": 0.42, "y": 0.56, "width": 0.22, "height": 0.18}
        ],
        "paths": [{
          "id": "main-route",
          "points": [
            {"x": 0.12, "y": 0.82, "scale": 1.06},
            {"x": 0.34, "y": 0.72, "scale": 0.98},
            {"x": 0.58, "y": 0.61, "scale": 0.88},
            {"x": 0.82, "y": 0.48, "scale": 0.76}
          ]
        }]
      },
      "layers": [{
        "source": "assets/character.png",
        "motion": {
          "preset": "follow-path",
          "pathRef": "main-route",
          "pathAnchor": {"x": 0.5, "y": 0.94},
          "depthScale": true,
          "pathProgress": [
            {"frame": 0, "progress": 0},
            {"frame": 48, "progress": 0.46},
            {"frame": 66, "progress": 0.46},
            {"frame": 119, "progress": 1}
          ]
        }
      }]
    }

backgroundMap 是对背景图的结构化描述。

horizonY 记录地平线。blockedZones 标出深水、树干、石头等不能直接穿过的区域。paths 保存道路、河流或轨道的真实走向。坐标都在 0 到 1 之间,所以预览窗口变大变小,路径仍能贴在同一个位置。

角色图层用 pathRef 引用路线。

pathAnchor 决定路线应该贴在素材的哪个部位。人物和动物取脚底,汽车取轮胎接地点,船取水线。只有飞行物才适合用图片中心。

pathProgress 负责节奏。

它描述某一帧走到了路线的哪个位置。两个关键帧的 progress 相同,就表示角色在这里停了一会儿。再配合起步、回弹、轻微步态和镜头推拉,一条路线里就能做出加速、减速、停顿和再次出发。

这些字段没有多高级。

但它们把脚要踩在地上这句话,翻译成了 Remotion 可以执行的数据。

背景感知路线运动原理

路线不能按控制点数量平均

路线运动里还有一个很容易忽略的问题。

假设直线路段只有两个点,转弯处为了贴合背景放了六个点。如果把进度平均分给每一段,角色到了弯道就会突然变慢,因为它要花同样的时间走过许多很短的线段。

Skill 模板里的 samplePath 不按点数分配进度,而是先把归一化坐标还原成屏幕像素,计算每一段的实际长度。

    length[i] = hypot(
      deltaX * canvasWidth,
      deltaY * canvasHeight
    )

    target = progress * totalLength

    找到 target 所在的线段
    在线段起点和终点之间插值

这样一来,控制点只负责描述形状,不会偷偷改变速度。

角色的缩放也跟路线一起走。

路径点可以显式记录 scale。如果没有写,Remotion 就根据路径纵坐标和地平线估算景深。角色靠近画面下方时变大,走向地平线时缩小。

路线切线还可以提供一个受限的旋转角度。走上坡或转弯时,角色会轻微倾斜,但不会整张图片跟着道路大幅翻转。

做到这里我才意识到,角色看起来像贴纸一样滑,通常不是少写了几个 translateX。

真正缺的是背景、路线、角色接触点和时间进度之间的关系。

自动规划不够时,让人直接画路线

Skill 会先分析处理后的背景图,再尝试自动提取道路边界、弯道、消失点和禁入区。

普通道路可以自动规划。遇到河岸、踏石、山路和遮挡,模型仍然可能判断错。继续在提示词里描述先走近一点再向右绕过去,成本很高,结果也不稳定。

所以这套 Skill 里还装了一个本地路线控制台。

Remotion 主体路线编辑台

控制台本身很简单。

服务端只用了 Node.js 自带的 http、fs 和 child_process,没有再引入 Web 框架。前端是一页原生 HTML、CSS 和 Canvas。打开以后选择场景,再选择场景里的某个主体,就能在背景图上点击绘制路线。

每个主体有自己的独立路线。

控制点可以拖动、删除或清空,时间滑杆会让角色沿路线预览。编辑器保存的是归一化坐标,所以浏览器里的显示尺寸不会污染正式视频数据。

真正需要小心的是保存。

如果网页直接覆盖 storyboard.json,只要写入一份坐标越界或字段缺失的数据,原来的可渲染工程就可能被破坏。

现在的保存过程分成四步。

    浏览器 PUT 新 storyboard
      ↓
    服务端写入随机命名的临时 JSON
      ↓
    Python 校验器检查临时文件
      ↓
    通过后原子替换正式 storyboard.json

validate_background_motion.py 会检查路径是否至少有四个点,坐标是否处于 0 到 1,路径 id 是否重复,pathRef 是否真实存在,pathProgress 是否按帧递增,以及路线有没有穿过未获允许的禁入区。

校验失败时,临时文件会被删除,原 storyboard.json 保持不动。

这部分代码并不多,却是我认为整个路线编辑器里最值得保留的设计。

可以让人改,但不能让一次误操作毁掉已经能跑的工程。

我是怎么一轮轮把它改出来的

这套 Skill 并不是先画好一张完整架构图,再一次写完。

第一轮只解决一件事,让 WorkBuddy 真正一键跑到底。它不能写完素材提示词就停下,也不能把逐张生图重新交给我。已经生成的文件还要能够续用。

第二轮处理的是素材边界。绿幕和 rembg 解决了透明背景,normalize 再把角色放进统一画布,Remotion 才能得到稳定的视觉尺寸。

第三轮补上声音、字幕和画风控制。我把配音切到本地 VoxCPM2,显示字幕变成 2 到 14 字的无标点短语,默认画风也从单一剪纸改成可以选择。

第四轮开始处理主体和背景的关系。backgroundMap、pathRef、pathAnchor、pathProgress 和 depthScale 被写进分镜协议,路线校验器负责挡住坐标越界和穿过禁入区的数据。

第五轮没有继续追求全自动。我加了路线控制台、保存前校验、原子替换和成片后的路线确认,让人可以只改判断错误的那一小段。

WorkBuddy Skill 的五轮迭代路线

Skill 做完以后,我让它生成了《小马过河》

前面的脚本、数据协议、Remotion 模板和路线控制台都装进 WorkBuddy 以后,我才开始跑具体故事。

我给它的主题是《小马过河》。

这次我没有再逐步告诉它先写什么、下一张图片画什么。WorkBuddy 按照 Skill 自己拆成六个场景,生成小马、母马、老牛、松鼠和河岸素材,完成抠图、尺寸统一、配音、字幕和 Remotion 渲染。

第一次成片已经能完整播放。小马走到河边、听完老牛和松鼠的意见,再试着过河,故事和镜头都跑通了。

但其中一段路线没有贴住河岸。

这时我没有修改素材,也没有让 WorkBuddy 从头再做一遍。我打开 Skill 自带的路线控制台,把走歪的那一段重新画好。

保存后,WorkBuddy 继续运行路线校验、Remotion 渲染和 ffprobe 检查。

得到的成片是 1080×1920、30 帧、32.04 秒,带完整音轨的 MP4。

WorkBuddy Skill 生成的小马过河成片抽帧

这条实测也验证了 Skill 末尾的一条规则。

初版视频完成渲染和抽帧检查后,WorkBuddy 要主动问一句。

是否有需要更新的路线。

image.png

如果我说没有,它就交付视频和 Remotion 工程。如果我说有,它就打开路线控制台,等我保存,再从校验、渲染和 QA 接着执行。


字幕的样式还是差点意思,大家有喜欢的字幕样式可以通过控制 remotion 来进行优化。

Workbuddy 生成的图片会有水印,rembg 还是会把水印给抠出来,大家可以在 skill 中加一层去水印的功能或者使用自定义的生图 api。

目前暂时还是做不出来特别惊艳的效果,可能是没有 bgm 以及 remotion 控制等等的问题,但感觉逗一逗小孩子还是可以的。


最后附上 rembg 和 voxcpm 的链接:

  • rembg:https://github.com/danielgatis/rembg
  • VoxCPM / VoxCPM2:https://github.com/OpenBMB/VoxCPM
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐