首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >我用 WorkBuddy 把 AI 长视频做成了流水线:从分镜构思到无缝合片

我用 WorkBuddy 把 AI 长视频做成了流水线:从分镜构思到无缝合片

原创
作者头像
用户6882326
发布于 2026-10-08 15:10:32
发布于 2026-10-08 15:10:32
1200
举报

出图是"抽一张卡",做视频是"让 100 张卡连成一口气"。这篇不讲模型参数,讲真正卡住人的那一层——叙事单元的切分和帧与帧之间的账。


一、先说结论:视频难的不是画质

刚开始做 AI 视频的人,通常会把力气全花在 prompt 上:堆"电影感""8K""大师级运镜"。做完你会发现两件事很扎心:

  • 画质根本不是 prompt 决定的——它是"参考图的像素量 × 输出分辨率"决定的,写得再花也只是心理安慰;
  • 真正毁片的是三样东西:动作超载导致的中途跳变、段与段之间的接缝、以及角色在第三段以后慢慢变成另一个人。

所以我后来把整件事重构成了流水线:WorkBuddy 当编排层(拆镜 → 下发任务 → 校验 → 合片 → 归档),本地 ComfyUI 当渲染机。这篇就是这条流水线的完整拆解。


二、整体架构:编排层与渲染层分开

把"想清楚"和"跑得动"分成两层,是这套东西能跑起来的前提。混在一起写,你会发现每改一个镜头都要重跑一遍全片。

层

谁来做

干什么

产物

构思层

WorkBuddy

读剧本 → 逐镜展开 → 标注叙事目的 → 定时长 → 划段

镜头清单 + 段级任务表

编排层

WorkBuddy

生成首帧、拼 API 工作流、批量提交、轮询、抽帧校验

每段一个 webm + 校验记录

渲染层

本地 ComfyUI

实际出图 / 出视频

原始帧序列

收尾层

WorkBuddy

PyAV 拼接、叠化接缝、响度归一、归档

成片 mp4

为什么必须分两层:渲染层是"哑"的,它不知道你要讲什么故事,只认 prompt 和帧数;构思层是"哑"的,它不知道显存多大。把判断力放在编排层,渲染层只负责执行——这样换模型、换分辨率、换显卡,上面的东西一行不用改。

首帧构图·影院光束下的中景
首帧构图·影院光束下的中景

三、地基:动笔之前必须填满的一张表

手部动作镜·抚琴特写
手部动作镜·抚琴特写

这是全套流程里唯一一条没有商量余地的规矩:写任何一条 prompt 之前,先把这张表逐镜填满。表没填满,不许动笔。

格

必答内容

缺了这一格的真实症状

① 手的所有权

这一镜哪只手做动作;另一只手在哪、手里有什么(写 "free hand" =没写)

多画一条手臂 / 凭空多一只手

② 道具接触关系

道具被谁、用哪只手、以什么方式持有;本镜内位置如何变化(含落点)

道具先消失再凭空出现;道具浮空

③ 衣物物理路径

凡"移物 / 移衣"动作,必写:物件坐标 → 手与物件接触点 → 逐节中间站点 → 脱离点

裙子直接缩上去 / 消失,而不是被撩起、被手指捏住、堆成褶

④ 本镜结束状态

这一镜结束时:人、手、道具、布料各在哪(=下一镜的起点)

镜间 / 段间跳变,承接链断裂

⑤ 物件动作数

本镜与整段的物件动作数(不是子镜数);>3 立刻拆段

超载 → 模型在起拍点直接跳到结果,中间过程不存在

第 ③ 格是最容易被忽略、也最值钱的一格。举个例子:让角色边走边把手里的布料垂到地上,如果你只写"她把布料放下",模型给你的大概率是布料凭空消失。写成"布料从腰侧滑落 → 指尖勾住布角 → 沿大腿外侧逐节下落 → 触地堆成一褶"之后,动作才会真的被"演"出来。

动作镜·国风剑舞(动作预算示例)
动作镜·国风剑舞(动作预算示例)

每段写完必过两条硬自检:

  1. 数物件动作数(不是子镜数),>3 立刻拆段;
  2. 每个"从身上移走某物"的动作,逐条查有没有四段式路径链,缺的当场补。

四、第二步:给每一镜标两个"为什么"

每镜必答两个问题,这叫叙事目的双层标注:

  • 结构层(删掉它全片缺什么):交代信息 / 建立·强化角色 / 推进情节 / 埋设·回扣伏笔 / 情绪转场;
  • 执行层(要在观众身上制造什么效果):制造氛围 / 引导注意力 / 制造时间感 / 建立·打破预期。

一句话覆盖两层,比如:

镜 3:交代师姐在樱树下抚琴的空间关系(结构层);用中景 + 花瓣缓落让观众先安静下来,为后镜情绪转折蓄力(执行层)。

标完就有四条自检线:找不到目的的镜删掉;连续三镜高强度叙事后必须插一个呼吸镜;伏笔必须成对出现;台词不能成为叙事目的的主体——大量依赖"角色说出 XX"就是视听化不足。

时长则由叙事目的决定(这是 551 个镜头统计出来的基准):

叙事目的

基准时长

推进 / 升级

2.5–3.5s(事件加速,镜头也加速)

交代 / 建立 / 展现 / 情绪 / 呼应

4–5s

转折 / 翻转

5–7s(重量要落地)

升华 / 主旨

6–10s(终极意象需要沉淀)

对白镜有个反直觉的规矩:先排声音事件,再定镜长,禁止先挑个短镜长再往里塞台词。否则就会出现"台词说到一半被切"的经典翻车。


五、段级切分:为什么 4 秒一镜的脚本喂进去会碎

这是整篇文章最重要的一节。

很多人拿着镜级脚本(4–5 秒一镜、几十镜)直接喂给视频模型,结果成片的突变间隔是 1.7~7.3 秒、而且和你的段边界毫无关系——因为模型在每一段内部会自己切 3~4 次。你排的节奏,它根本没在执行。

正确做法是按"生成单元"重新切,规则就这么几条:

  1. 段长只有 10s / 15s 两档。帧数必须落在 17k+5 网格上(243 帧 ≈ 10.125s / 362 帧 ≈ 15.083s)。4–9 秒的内容向上并进 10s 段——短段反而更容易被跳。超 15s 在叙事闭合点拆段,不许硬压。
  2. 每段动作 ≤3 个,一个动作单独占一个子镜,段尾停在"动作落定后的稳定状态"。
  3. 每段一个首帧图,接 I2V 节点的 first_frame。段 N 的首帧姿态 = 段 N−1 的末状态。
  4. 段内就是一镜到底,没有剪辑点。构思层里写的"空镜 / 插入镜"不能带进 prompt——写进去它也不会切,只会把画面拉远或把人变形成怪物。正解是把空镜改写成运镜:想要"空教室全景",就写"镜头缓缓拉开露出整间教室,再推回收人"。
  5. 段间接缝必须叠化。段 N 的末帧是喂进去的锚定图,段 N+1 模型重绘的首帧与它天然有差——实测接缝处跳变 63.2,而段内最大帧差只有 5~7。肉眼一眼就觉得"接得不顺"。

叠化的做法(一条命令):

代码语言:bash
复制
# 视频叠化 0.5s + 音频交叉淡化 0.5s,整片复测 0 处突变
ffmpeg -i seg1.mp4 -i seg2.mp4 \
  -filter_complex "[0:v][1:v]xfade=transition=fade:duration=0.5:offset=9.6[v]; \
                   [0:a][1:a]acrossfade=d=0.5[a]" \
  -map "[v]" -map "[a]" out.mp4

另外,每段开头那 0.3 秒是"首帧交棒"的跳变期,直接裁掉前 8 帧最省事。

布料物理路径·晾衣场景
布料物理路径·晾衣场景

试跑路径(别跳):正式推全片之前,先跑 3 段——

  1. 色板段(验通路与色调);
  2. 关键道具段(验路径链守不守得住);
  3. 动作最密的那一段(全章最容易被跳,用它验路径链到底有没有效)。

六、接线:一条链错一处就是满屏花屏

视频节点的工作流比出图敏感得多。正确接线只有一种:

代码语言:json
复制
{
  "1": { "class_type": "LoraLoaderModelOnly",
         "inputs": { "model": ["4", 0], "lora_name": "ref2v_turbo.safetensors", "strength_model": 1.0 } },
  "2": { "class_type": "MiniMaxH3ImageToVideo",
         "inputs": { "model": ["1", 0], "positive": ["5", 0], "first_frame": ["9", 0], "length": 362, "duration": 15 } },
  "3": { "class_type": "BasicGuider", "inputs": { "model": ["1", 0], "conditioning": ["2", 0] } },
  "6": { "class_type": "KSamplerSelect", "inputs": { "sampler_name": "euler" } },
  "7": { "class_type": "BasicScheduler", "inputs": { "model": ["1", 0], "scheduler": "simple", "steps": 8 } },
  "8": { "class_type": "CreateVideo", "inputs": { "fps": 24 } }
}

三个必须记住的禁用项(出现即花屏):

  • ⛔ 标准 KSampler —— 换成 BasicGuider + SamplerCustomAdvanced;
  • ⛔ ConditioningZeroOut —— 没有负向通道,别想着"清空负向";
  • ⛔ sigma shift 节点 —— 头号元凶。shift_video=12.0 会把 sigma 曲线压平,采样压根不降噪,输出全屏噪声块。

花屏速查三步:

  1. 先怀疑接线,不要先怀疑模型或 prompt;
  2. 花屏段的文件体积是正常段的 3–5 倍(实测 1.6 MB vs 0.25 MB @512×288/124 帧)——扫一遍分段的体积就能定位是哪一段炸了;
  3. 修完必须抽 4–5 帧真读核验,不能只信脚本打印的"完成"。

七、画质天花板 = 参考图 × 输出分辨率

这句话值回票价:prompt 不产生细节。"画质细腻""8K""电影感"这类词的权重极低。要脸好,唯一的路是单独出一张"纯脸特写"资产图——脸占画面 70% 以上,不要头肩像。

然后是身份漂移。角色跑到第三段开始变脸,这是所有人的必经之路。三个认知:

  • 参考图必须用"点号展平键":"ref_images.ref_image_0": ["11", 0]。写成列表或嵌套字典会被引擎静默丢弃——提交零报错、正常出片、就是人不对。这种坑最耗人。
  • 身份漂移的正解是参考图驱动(R2V),不是训 LoRA。训 LoRA 成本高、且一次只能锁一个人;参考图通路一次可以喂 9 张图 + 3 段参考视频 + 3 段参考音频。
  • 动作幅度 ≈ 首尾两帧的姿势差。首帧和尾帧给同一张图,模型没有"距离"要跑,动作会自动缩水成原地抬手级别。要做大动作,就先用出图模型各出一张姿势差很大的定格图。
背影机位·纯背景构图
背影机位·纯背景构图

还有一个反直觉的实测:一段 10 秒里机位一定会自己动。哪怕把"固定机位"写死,它照样偷偷推近,推近幅度与动作幅度正相关——所以想保全景,只能让首帧取景比目标再远一档,别指望 prompt 里的定点句。


八、合片:拼接的六个坑

分段生成之后,合片这段最容易被小看。用 PyAV 拼接时踩过的坑,按杀伤力排序:

  1. 两段直接 mux 必报 EINVAL:两源 pts 各自从 0 起 → dts 回卷。
  2. 唯一可靠解法是 concat demuxer:open(lst, 'w').write("file 'a.webm'\nfile 'b.webm'\n") inv = av.open(lst, format='concat', options={'safe': '0'})清单里写绝对路径时必须带 {'safe': '0'},否则报 PermissionError。
  3. B 帧必须 flush,否则尾部几十帧直接丢:实测 486 帧不 flush 只出 445 帧(丢 41 帧 / 1.7 秒)。for pkt in vstream.encode(None): ov.mux(pkt)
  4. demux 流是顺序消费的:先迭代到视频 EOF,再去解音频,拿到的是空。必须一次全解、按帧类型分流。
  5. 视频/音频必须按 dts 交错写入:先把视频包全写完再写音频包,音频流会被整条丢弃——不报错、无音轨、时长也算错。
  6. 跨段 mux 要显式写全局单调递增的 pts,否则 mp4 muxer 拒收(Invalid argument ... returned 22)。

校验成片规格时,必须用便携包自带的解释器:

代码语言:python
复制
import av
c = av.open(path)
s = c.streams.video[0]
print(s.codec_context.width, s.codec_context.height, c.duration / 1e6, s.average_rate)
print([(a.codec_context.name, a.codec_context.sample_rate) for a in c.streams.audio])

九、8GB 显存 / 31GB 内存的生存线

小显存做视频不是"慢一点",是"直接不出片"。几条自己撞出来的红线:

  • 启动必须带 --disable-smart-memory。摘掉这个参数,显存调度会把 20GB 的模型硬塞进 8GB,输出纯色块垃圾;
  • 提交前先探活:接口返回 200 且 内存空闲 > 10GB 再提交;空闲 < 5GB 一律不提交——那不是引擎故障,是整机蓝屏;
  • 两组模型别想同时驻留。图像栈和视频栈加起来约 51GB > 可用 31GB,切换时最稳的姿势是直接重启引擎,而不是逐个卸载模型(单飞卸载会 OOM);
  • 帧数和画幅有硬约束:帧数落 17k+5 网格、15 秒封顶;宽高必须都是 32 的整数倍。反例 768×432 会直接崩——432/32 = 13.5,RoPE reshape 失败,这不是显存问题,是维度对不上;
  • 首帧图的宽高比必须与目标分辨率完全一致。这个节点对图的行为是直接拉伸,不是 center-crop,比例不齐人物就变形。

十、档位速查表

以短边为基准,124 帧 ≈ 5.17 秒,8 步 turbo:

档位

像素

124 帧耗时

用途

draft 512×288

0.147 MP

~81–88 s

草稿:验动作与音轨,成本可忽略

wide 832×480

0.399 MP

~254 s

横构图 / 多人场景,不裁人

hd 512×896(竖)

0.459 MP

~326 s

定稿常用档

hd3 768×1344(竖)

1.032 MP

~985 s

上限档,先想清楚再跑

选档先算主角横向跨度:要求 档位宽/档位高 × 源高 ≥ 跨度,装不下就一定切人。而且分辨率抬档的时间开销比像素量更陡。

工作流的节奏建议是:先跑草稿档看动作与节奏,满意了再上定稿档。别一上来就冲高分辨率——一个动作超载的段落,跑得再清晰也是废片。


十一、WorkBuddy 在这套流程里到底干了什么

前面十节听着像"视频技术笔记",但真正让这套东西能一天跑完一整章的,是编排层:

  • 把构思层变成可执行的任务表:逐镜表、叙事目的、时长、段级切分,全部落成结构化清单,而不是散在脑子里;
  • 批量下发 + 轮询:一个章节十几段,逐段提交、逐段等待、逐段抽帧校验,失败单独重跑,不牵连前后;
  • 产物归档:分段文件按序号命名、成片与校验图分开落盘,第二天起来直接逐个审;
  • 把"人肉经验"固化成脚本:禁用节点黑名单、帧数网格校验、接缝叠化、响度归一,全部写成可重复执行的步骤。

一句话总结分工:模型负责"把这一帧画对",WorkBuddy 负责"保证这十几段连起来是一部片"。


十二、踩坑速查表

症状

根因

解法

满屏噪点色块

接了 sigma shift 或标准采样器

改 BasicGuider + euler + simple/8 步;体积 3–5 倍的段就是它

衣服"飞走"、动作瞬间完成

单段物件动作数 >3

拆段,每段 ≤3 个动作,逐条补四段式路径链

第三段开始换脸

参考图没进去 / 只靠 prompt 描述

用点号展平键传参考图;走 R2V 通路

段与段之间"跳一下"

首帧重绘与人眼锚定帧有差

xfade 0.5s + acrossfade 0.5s;裁掉每段前 8 帧

成片没声音

音频包写在视频包之后被丢弃

按 dts 归并排序后交错 mux

尾部几十帧丢失

B 帧没 flush

编码循环后 encode(None)

提交就崩 / 整机蓝屏

内存空闲 < 5GB

提交前探活,空闲 > 10GB 再跑

人物被拉伸变形

首帧图宽高比 ≠ 目标分辨率

喂图前先 center-crop 到目标比例


结语

做视频最贵的东西不是显卡,是返工。而返工几乎全部来自两个地方:想清楚之前就动手,以及接缝没处理。

把构思层做成一张必须填满的表、把段级切分当成硬约束、把接缝当成一等公民——这三条做到,你会发现"AI 视频"从碰运气变成了一件可交付的活。

至于工具链,我现在的原则很简单:能本地的绝不上云。渲染在自己机器上跑,编排交给 WorkBuddy,一章节的成本是零积分加一个晚上,比什么都划算。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、先说结论:视频难的不是画质
  • 二、整体架构:编排层与渲染层分开
  • 三、地基:动笔之前必须填满的一张表
  • 四、第二步:给每一镜标两个"为什么"
  • 五、段级切分:为什么 4 秒一镜的脚本喂进去会碎
  • 六、接线:一条链错一处就是满屏花屏
  • 七、画质天花板 = 参考图 × 输出分辨率
  • 八、合片:拼接的六个坑
  • 九、8GB 显存 / 31GB 内存的生存线
  • 十、档位速查表
  • 十一、WorkBuddy 在这套流程里到底干了什么
  • 十二、踩坑速查表
  • 结语
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档