返回博客

构建实录 / 2026-06-12

一套 IP、三条管线:儿童视频 AI 工厂的完整工程拆解

儿童视频 AI 生产管线成片画面

这个项目不是"一条管线",是围绕固定 IP「六六一家」的三条视频管线

  • 科普/古诗主管线:单文件 533 行 Python,一条命令从分镜 JSON 出到成片
  • 66 大冒险线:核心工具 2953 行,manifest 状态机驱动,带多角色对白
  • 黏土重制线:七步脚本的人机分工流水线,给已有视频做定格动画质感

三条线共用一套角色资产和合成能力,分别对应三种输入源——选题、真实生活、已有视频。科普片单条边际成本几块钱;到目前为止全项目产出 55 条成片、已发布 18 条,古诗线 70 首剧本分镜全部就绪。这篇按管线逐条拆实现,所有参数和代码都来自仓库现状。

起点:一个反常的数据

动手前拆了对标账号「秒懂少儿百科」(百万级少儿科普号),Top 内容里有个反常信号:爆款的收藏数经常超过点赞数。娱乐内容不长这样——收藏比点赞高,说明家长在把它当教育素材存档、转家庭群反复用。这类内容是工具,不是消遣。

打法随之确定:盯收藏率和完播,不追点赞;选题库 88 条、8 个系列、80% 原创角度。而三条内容线的形态差异,决定了三条管线不同的工程形态:

科普/古诗主管线66 大冒险线黏土重制线
输入源选题(从零自产)真实育儿日常已有原视频
自动化全自动全自动 + 对白编排半自动(人机分工)
内容形态单人旁白讲解,50–58s叙事动画、多角色对白黏土定格质感重制
核心代码build_sample.py 533 行3699 行(主工具 2953)01–07 七步脚本
画面来源codex 生图 + seedanceseedance(火山 ark 直连)即梦网页手动生成

一、主管线:533 行走完「分镜 → 成片」六个阶段

全部合成逻辑在一个 build_sample.py 里:18 个函数、六个子命令(all/base/clips/concat/bgm/check),外部依赖只有 ffmpeg 和 Pillow。all {tid} 一条命令按顺序跑完六个阶段,出片自动校验。

阶段 0:分镜即协议

新选题不改一行代码。给 output/{tid}/ 备一份 scenes.json

{"title": "为什么会下雨?", "scenes": [
  {"id": "sc1", "text": "旁白/字幕文本", "zoom": "in", "dur": 6, "tint": [210,185,140]}
]}

text 必填,其余可省。素材按镜丢进目录,管线按文件存在性自动选路:dream/dream_scN.mp4(AI 视频)优先,其次 images/scN.png(静图运镜),都没有则走兜底图——兜底图不是摆设,它让"素材还没生成"的选题也能先跑通全流程看节奏。

阶段 1:base——每镜底图

有真图时做 cover 裁剪scale = max(W/bw, H/bh) 先放大到填满 1080×1440 画布再居中裁切,任何比例的生图都不留黑边。没图时用 numpy 现算一张径向渐变兜底:以画布中心为原点算归一化半径 r,中心色 tint*1.18+28、边缘色 tint*0.62 按 r 插值——分镜里配的 tint 三元组决定这一镜的底色气质,再用 Pillow 把关键词大字画上去。

阶段 2:clips——单镜合成,五件事一条 ffmpeg 命令

这是最重的函数。每一镜要同时解决画面、时长、字幕、旁白、音效五件事,全部编译成一条 filter_complex

时长跟旁白走。 dur = 旁白时长 + 0.5s。AI 视频不够长就 setpts={slow}*PTS 慢放铺满(慢放倍率 = 目标时长/视频时长),静图直接按配音时长渲染。旁白永不截断——科普片"讲清楚"优先于"画面节奏"。慢放超过 1.6× 由 check 报警,解法是缩旁白或加长视频。

静图微动零成本。 Ken Burns 不是匀速推拉,zoompan 表达式叠了三路不同周期的正弦——zoom 上加"呼吸",x/y 上加"漂浮":

z = min(1.0 + 0.05*on/frames + 0.016*sin(on/22), 1.12)
x = iw/2 - (iw/zoom/2) + 14*sin(on/40)
y = ih/2 - (ih/zoom/2) + 11*sin(on/55)

三个周期互质,肉眼看不出循环,静图像手持实拍。实现细节:先把底图放大到 2160×2880(两倍画布)再做 zoompan,避免运镜采样糊掉。这让"只给关键镜头买 AI 视频"的混合档成为默认——大多数镜头根本不需要 AI 视频。

字幕按字数比例分段。 split_segs 先按句末标点切句、每两句一段,然后按各段字数占比分配镜头时长——近似"说到哪句显示哪句",不需要任何语音对齐模型。每段字幕渲成独立 PNG,overlay=enable='between(t,s,e)' 按时间窗启用。字幕是独立图层,不随运镜漂移;text 含换行则整段显示自动变双语模式(中上英下)。

音轨 apad + amix 兜底。 旁白 apad 补齐时长防提前收声;音效存在时压低音量后 amix normalize=0 混入——关键镜头有环境音(下雨选题真的有雨声),缺音效也不会让该镜合成失败。

阶段 3:concat——xfade 偏移链

镜头拼接不是简单 concat,是 xfade(视频交叉淡化 0.4s)+ acrossfade(音频交叉)的链式滚动:每一步的 offset 是前面所有镜头时长累加再减去已消耗的转场时长(offset += durs[i] - td),N 镜生成 N−1 级串联 filter。写错 offset 是这类拼接最常见的坑——转场点会漂移。

阶段 4:assemble——片头尾与封面同源

片头是固定原片 intro_raw.mp4 叠一张运行时生成的标题卡make_title.py 画大字 PNG)——同一张标题卡就是封面的来源,片头画面和封面天然一致。片尾同理叠互动钩子卡。片头有固定旁白,片尾没有——prep_segmentanullsrc 生成静音轨兜底,保证每段都有 44100Hz 立体声,否则 concat demuxer 拼接时音轨会错位。目录下放个 no_intro/no_outro 空文件即可跳过对应段——开关用文件表达,和"素材存在性选路"一个哲学。

阶段 5:bgm——循环铺满与尾部淡出

BGM 按系列固化:古诗(G 前缀)配古风曲、科普(S/H)配轻快曲,同系列所有视频统一一首——账号听感一致性靠这个保证。实现:-stream_loop -1 无限循环输入,volume=0.26 垫底,afade=t=out:st={总长-3}:d=3 尾部 3 秒淡出,amix duration=first 以正片时长为准截断。

阶段 6:logo 与规范化导出——防"只显示第一帧"

最后一步除了叠 logo,更重要的是规范化fps 滤镜统一帧率 + setpts=PTS-STARTPTS 重置时间戳 + -vsync cfr 恒定帧率 + -movflags +faststart。seedance 视频经过慢放和多次混流后 pts 会乱,直接发小红书/微信的症状是播放器只显示第一帧——这个坑踩过一次之后,规范化被写死成导出的必经步骤。

阶段 7:check——三层自动校验

  • 逐镜层:画面素材来源(AI 视频/静图/兜底)、配音齐全(缺配音会导致拼接失败,提前报)、慢放倍率是否超 1.6×
  • 成片层:断言分辨率 1080×1440、音轨存在、时长落在 30–60s、封面存在
  • 资产层:片头尾原片/BGM/logo 四件套齐全

批量生产里,校验和出片同等重要——问题在列表里,不用肉眼看片排查。


二、冒险线:manifest 状态机 + 多角色对白引擎

《66 的大冒险》把儿子 66 的真实成长小事改成系列动画。和科普线的本质区别:它有故事和对白。这让工具复杂了一个量级——episode_tool.py 有 2953 行、15 个子命令,再加 seedance 直连脚本 290 行和合成脚本 413 行。

叙事被固化成规则

每集 8 段结构:任务开始 → 发现 → 探索 → 受挫 → 父母介入 → 再尝试 → 完成 → 成长奖励(勇气 +1)。固定开头"今天,是 66 来到地球的第 X 天"——X 不是随手写的,是日期减生日按真实日历算的;固定结尾是那句"勇敢不是从来不会害怕,而是害怕以后依然愿意再试一次"。结构和仪式感稳定,AI 才能稳定地往里填内容。

episode 包与状态机:next-tasks 是给人和 AI 的工作队列

一集是一个 episode 包(JSON 模板起草 → 校验 → 导出)。包里的 manifest 记录每一镜三种资产的就绪状态:image_ready / audio_ready / video_ready。最实用的子命令是 next-tasks:扫描 manifest,按依赖顺序生成待办清单——角色参考图缺失最优先,然后逐镜"先图、图好了才轮到音频和视频",每条任务都带产出路径和对应的提示词文件位置。

这个设计的价值在于:生产状态不在人脑子里,在 manifest 里。 无论中断多久、换谁(或换哪个 AI)接手,refresh + next-tasks 两条命令就能知道"现在缺什么、下一步做什么、提示词在哪"。配套的还有提示词工厂(每镜自动导出生图/生视频提示词文件)、状态报告渲染、分镜表 CSV 导出。

多角色对白引擎

科普线合成的是"画面+旁白",冒险线编排的是"一场戏":

  • 对白直接写在剧本文本里,正则按「角色:台词」解析——66/六六/爸爸/妈妈/旁白/X音效 都是合法说话人,不需要单独的结构化录入;
  • 每个角色有独立声线参数:六六音量 1.05、语速 +0.04(孩子说话快而响亮),爸爸 +0.02,妈妈 −0.02(温柔要慢),旁白音量 0.95;
  • 情绪预设按镜头标题和情绪关键词匹配,一次给出语气/语速/色调三元组,色调还会回流到画面 tint;
  • TTS 念白前把"66"自动替换成"六六"——不处理会念成"六十六"。

字幕体系也比科普线厚:SRT 和 ASS 双格式渲染(对白字幕、旁白字幕分开)、按 ffprobe 实测音频时长对齐时间轴,甚至还有弹幕轨——danmaku 子命令生成 ASS 滚动弹幕烧进成片,模拟"视频网站感"。TTS 主力是云端引擎,但工具里内置了 Windows SAPI 本地合成兜底——断网或额度用尽时,生产不停。

seedance 火山 ark 直连:提交-轮询-下载

科普线的图生视频走中转,冒险线直连火山 ark(ark.cn-beijing.volces.com/api/v3):create 把首帧图转 base64 data URL 随 prompt 提交建任务(watermark: false),任务 id 落到本地 tasks.json 登记;poll 按 scene_id 找回任务轮询状态,succeeded 就下载视频归位,failed/超时都有明确出口。中断恢复友好:任务登记在文件里,脚本挂了重跑 poll 就行。


三、黏土线:约束决定架构

第三条线给已有视频做黏土动画(claymation)重制。它是半自动——不是技术做不到全自动,是约束决定的:黏土生成效果最好的是即梦网页端,没有合规接口,而这个项目坚持不绕登录、不碰验证码、不用非官方接口

于是流水线被切成"机器 → 人 → 机器"三段,机器把人工环节压到最薄:

01 拆镜头 → 02 抽关键帧 → 03 生成提示词 → 04 任务表
   →(人工:按任务表在即梦网页生成,下载按序号命名)
→ 05 自动重命名归位 → 06 定格质感后期 → 07 拼接成片

01 拆镜头:按配置的目标秒数均匀切段(-crf 18 保质量、去音轨),末段过短自动并入上一段防碎片;每段落 CSV 日志。03 提示词工厂prompt_templates.yaml(黏土风格模板)×character_liuliu.yaml(角色卡的 description + must_keep 约束)填充,每镜产出图片/视频/首尾帧三种提示词 txt,再汇总一份 all_prompts.md 供人一页复制。04 任务表告诉你每个镜头上传什么、生成什么、下载后命名几号——人在网页端只做"复制提示词、点生成、按号保存"。

06 是这条线的技术核心:AI 生成的视频太"顺滑",没有黏土定格的手感,就用 ffmpeg 假装出来——四板斧:

  1. 降帧率到 12fps(定格动画的标志性帧率)
  2. noise=alls=8:allf=t 加胶片颗粒
  3. 暖色调
  4. 每 2 帧做 2px 微位移,模拟手工定格的抖动

四板斧下去,Seedance 出的顺滑视频就有了 stop-motion 味。成本纪律写进文档:主力 Seedance 2.0 Fast 720P 约 0.27 元/秒、4–6 秒一段;先低成本跑通整体风格,再重跑关键镜头;单镜头抽卡设上限,不用高质量模式试错。


四、三线共用什么:成本的真正来源

单条几块钱的边际成本,靠"只做一次"的资产被三条线共摊:

资产说明成本属性
角色设定图(六六一家)三线生图必挂参考一次性
片头/片尾原片科普线自动拼接一次性
系列 BGM科普曲/古风曲各一首固化一次性
logo/字体/封面模板站酷快乐体等一次性
codex 生图ChatGPT Pro 额度,stdin 传 prompt、挂参考图边际免费
seedance 图生视频主线 0.75–1.1 元/段;黏土 Fast 0.27 元/秒按次
火山 bigtts科普/古诗/多角色固定音色极低

每条新片真正花钱的只有少量 seedance 段。全项目仅有两道人工审核:科学性(给孩子讲错因果链是误人子弟,宁可少写)和角色一致性(挂了设定图 AI 偶尔还是画出陌生小孩,出片前扫一遍)。

五、各系列产出与发布进度

管线的产能最终要落到账本上。以仓库的发布清单为准(final.mp4 存在才算成片):

内容线系列成片已发
少儿科普(33 条)🛡️ 安全小卫士166
✨ 好习惯养成71
🩺 健康身体11
🧠 身体的秘密70
🌿 自然科学21
爱上古诗·黏土画风(20 条)李白77
杜甫42
王维 / 白居易 / 孟浩然90
66 大冒险ep01 挖掘机10

合计 55 条成片(含一条英文样片不计账本),已发 18 条。 几个说明:

  • 古诗片是黏土画风、主管线出片G 前缀自动配古风 BGM 和小书童开场);黏土子管线贡献的是风格资产体系——李白、杜甫、王维、白居易、孟浩然五位诗人的角色设定图都在它的角色卡目录下。剧本分镜 70 首全部就绪,已出片 20、待出片 50,出片速度完全由 seedance 预算决定。
  • 李白 7 条全部已发——同诗人攒合集是发布策略的一部分,合集是收藏率的锚点。
  • 待发库存 35 条(24 科普 + 11 古诗)。发布纪律也写在清单头部:亲子时段 19:00–21:00 发、上传勾选「包含 AI 生成内容」、首发由本人操作。

这张表也回答了"管线到底值不值":生产端 55 条的边际成本是可控的几百块,瓶颈已经完全转移到发布和数据回流侧。

六、踩坑清单

  • 变帧率视频在微信/小红书只显示第一帧:seedance 慢放+多次混流后 pts 异常。cfr + faststart + setpts=PTS-STARTPTS 规范化导出,写死在收尾函数里。
  • 解剖类镜头交给图生视频必然穿模(内脏乱飘):此类镜头强制静图卡通示意,写进分镜规范。
  • seedance 并发就 ReadTimeout:逐批串行提交、图先压小 JPEG;即梦 pre-TNS 审核不过就换图或改静图,不要硬试。
  • concat demuxer 对音轨格式敏感:所有段落统一 44100Hz 立体声,无声段用 anullsrc 生成静音轨,否则拼接错位。
  • xfade offset 手滚:偏移是"累计时长 − 已消耗转场"的滚动值,算错转场点就漂。

七、收尾:一个判断框架

三条管线是三种取舍:能全自动的全自动(科普),需要叙事的加编排层和状态机(冒险),受平台约束的人机分工、机器只做两头(黏土)。如果你也要搭内容管线,可以搬走的判断框架是——先看输入源和约束,再决定自动化档位;以及三条通用手法:

  1. 把内容结构做成协议:分镜 JSON、8 段叙事模板、任务表——数据驱动,新内容不改代码;
  2. 把生产状态放进 manifestnext-tasks 式的工作队列让任何人(或 AI)随时接手;
  3. 用 ffmpeg 的程序化手段兜质感:三路正弦微动、12fps 假定格、径向渐变兜底图——很多"看起来要花钱"的效果,表达式就够了。

生产端已经证明了自己:55 条成片、五大科普系列加五位诗人的古诗系列,边际成本压在几块钱一条。现在卡脖子的不是生产是发布回流——35 条库存排期发出去,拿真实的收藏和完播数据回来修正选题,这部分没有脚本可写。