热爱技术,追求卓越
不断求索,精益求精

我给自己做了个"文章变视频"的智能体

先划一条线:画面不用 AI 生成

做这个智能体之前,我先给自己划了一条线:画面一个像素都不许由文生图、文生视频模型生成。

原因不复杂。文生视频模型出的画面,文字基本是糊的、数字经常是错的、风格还会飘。而我做的这类”文章讲解视频”,画面里全是数据、标题、关键句——这些恰恰是模型最不擅长、又最不能错的东西。一条视频里把”47.45%”渲染成”4.745%”,这条就废了。

所以路线反过来:画面用代码画。HTML 加 CSS,交给浏览器排版,再截成图。文字是文本、数字是数字,谁来屏幕上都一模一样,一个字都不会糊。

这样一来,”文章变视频”这件事就被拆成了一条很朴素的流水线:把文章读懂 → 写成文案 → 配音 → 按文案渲染画面 → 拼起来 → 贴人像、烧字幕、对口型 → 出片。每一段都是确定性操作,不是”抽卡”。

整条链子长什么样:九个阶段

我把它固化成九个阶段,每个阶段的输入输出都固定。整条链子跑下来,出一个 output.mp4——带音轨、字幕已烧录,1920×1080、25 帧、H.264 加 AAC。

阶段 干什么 产出
0 拿到素材(链接 / 文案 / 文档) source/original.md
0.5 选模板套件(风格 + 头像 + 音色) 记录模板名
1 写文案脚本:把文章提炼成分镜 script.json
2 配音,逐幕生成音频 audio/scene_0N.mp3
3 把各幕音频拼成整条 video/whole.wav
4 按文案渲染画面帧 frames/scene_0N.png
5 帧连成段、加转场、拼静默片 video/silent.mp4
6 贴人像圆窗(静态脸,先占位) video/withwin.mp4
7 烧字幕 + 混音 video/stage.mp4
8 整条对口型(Wav2Lip) video/whole_lip.mp4
9 输出成片 output.mp4

底下用的东西其实都不贵:HTML/CSS + Playwright 渲染画面,ffmpeg 做拼接、转场、烧字幕、混流,Edge-TTS(免费)或 CosyVoice 克隆音配音,本地跑的 Wav2Lip-ONNX 做口型。没有一个环节是”生成式”的,全是确定性工具。

阶段 1:把文章”翻译”成分镜

第一步不是渲染,是理解。我用文本模型把文章读一遍,提炼成一串分镜,写进 script.json。结构固定成三段:开头一镜(切入主题、抛问题)→ 正文若干镜(文章主体)→ 结尾一镜(呼应收尾)。

每一镜有一个类型,决定长得什么样:标题页、大数字页、三卡页、高亮页、分层页……正文几页不写死,内容讲全就行,三页五行都行,不为凑页数硬拆。

这里有个反复踩出来的经验:给视频配的文字,和屏幕上显示的文字要分开写。配音稿(旁白)要”说人话”——短句、不用书面词、别堆术语,想象是跟人当面讲;而画面上的字保持精炼书面风,别口语化。两套文本,一套给人耳朵,一套给人眼睛。

阶段 2~3:配音,然后拿它当”时间基准”

配音我用的是自己声音的克隆音(CosyVoice v3.5 克隆),出来的音色是我自己的。逐幕生成 mp3,再拼成一条完整的 whole.wav。

这里最值钱的一件事,不是”有声音”,而是配音里带着字级时间戳——每个字在音频里的起止毫秒都被记下来了。为什么重要?因为整条视频的节奏基准就是配音时间轴。后面每一幕多长、在哪转场、字幕什么时候冒出来,全部对齐这套时间戳。

顺序上必须”先配音、后画面”,因为配音是”因”,画面是”果”。先把声音定下来,画面跟着它走。

踩过一个坑:每段音频的头尾都有静音,尾巴上的静音有时候能有半秒到一秒。如果按”音频文件总时长”去切幕边界,画面就会被这截静音拖后,变成视觉比听觉慢半拍。正确做法是用”这一段最后一个字的结束时间”当边界,不能看文件时长。

阶段 4:用代码把画面画出来

这一步是整套方案的骨气所在。每一幕对应一个 HTML 模板,把 script.json 里的文字填进去,用 Playwright 截图成 PNG。

好处很直接:字号、行距、配色、对齐全部可控,长标题不会溢出、数字不会糊。画面是”渲染”出来的,不是”生成”出来的,所以稳定、可复现,改了文字重跑一遍就更新。

我把视觉风格做成了可换的模板套件。一套模板就是一个目录,里面装着它的版式、配色、头像、音色。横屏一套、竖屏一套,切换只改一个环境变量,引擎一个字不用动。换风格不碰代码,这是后面才想明白的架构。

阶段 5:转场,以及被它坑得最惨的一次

画面帧有了,用 ffmpeg 把每帧拉成一段,段与段之间用 xfade 加转场,拼成一条安静的片子。

这里我吃过一个很隐蔽的 bug,值得单独说:镜头一处偏移,后面整条都跟着错位。

xfade 的 offset 参数是绝对时间,不是”从上一段结尾再走多少”。而每段画面为了给转场留尾巴,实际长度比音频长半个转场。如果按”音频时长前缀和”去算每一段的长度,每一幕就会错开半个转场,然后逐幕累积——跑到最后,画面整体滞后了一整幕。

修法是把每段的长度公式重推一遍:第一段长度 = 第一个转场偏移 + 转场时长;中间每段 = 两个转场偏移之差 + 转场时长;最后一段补上剩余。改完之后,拼出来的静默片总长死死等于音频总长,误差为零。验证的方法也定死了:不能只看总时长,要在每一幕的边界抽帧,肉眼核对”画面上的大字”和”那一刻的配音内容”对得上。

阶段 6 / 8:先贴脸,再对口型,别搞反

人像分两步,这是被坑过才理清的顺序。

阶段 6 只贴一张静态的脸,放进左上角的圆窗,从头到尾一动不动。这一步很快,因为它只需要渲染一帧带透明通道的圆窗图,然后循环铺到整条片上就行——秒级完成。它不依赖任何对口型工具,只是先确认”脸在不在、位置对不对”。

阶段 8 才是唯一做口型的地方,用本地跑的 Wav2Lip 整条一次性处理。这里的关键是:脸从阶段 7 的成片里直接抠出来用(那时圆环、分割线都已经画好了),不用外面再喂一张头像。抠出来 → 整条对口型 → 再贴回原来的位置。

贴回这步我也优化过一轮。早期的做法是逐帧重新画圆环、重新加模糊,整条要跑九分钟;后来发现成片里圆环本来就是现成的,我只需要换掉圆里的那张脸——于是改成”一张静态的圆形遮罩 + 叠加”,只替换圆内的画面,63 秒搞定,快了差不多八倍,还不会出现”两个圆环叠在一起”的鬼影。

不过这里有个必须记住的坑:用一张静态图当遮罩做叠加时,ffmpeg 会把它当成只有一帧,结果整条视频都不动了,全冻在第一帧。必须给它加 -loop 1,并且把帧率对齐,它才会正确地”一直铺下去”。

还有一条验收铁律,也是被上面这个坑逼出来的:判断成片”有没有在动”,必须拿最终产物在不同时间点各抽一串连续帧,算像素差。只看时长、只看中间文件,是发现不了”冻帧”的。顺便说一句,口型的运动幅度本来就小,抽帧算出来变化像素只有百分之一到一点五,这是正常的,别以为没生效。

把”一次只做一个阶段”写成了规矩

技术上跑通之后,我发现真正决定这个智能体好不好用的,不是某个算法,而是流程怎么卡。

我给它立了条死规矩:九个阶段,就是九次确认。每做完一个阶段,立刻停下,把能看能听的产物交出来,等人说”继续”再往下。不许一口气跑完再交,也不许”顺手把下一步也做了”。

听起来像是给自己找麻烦,但这条规矩救过很多次。文章→视频这条链子又长又贵,配音一改,后面全得重跑。如果闷头跑到底再给人看,一旦方向错了,浪费的是整条链子的时间和成本。分段确认,等于把”返工”的颗粒度压到了最小:每一步都能当场叫停、当场纠正。

还有一条相关的纪律:交付给别人的文件,尤其是成片和截图,文件名一律带时间戳。因为中间产物和最终成片经常同名,浏览器和聊天软件按文件名缓存——你明明改了,对方打开还是旧的,然后你以为没生效,来回折腾。带时间戳,一次到位。

跑出来什么样

拿一篇五千多字的商业分析文章实跑,出来的成片:1920×1080、25 帧、时长 156 秒、大小约 8.8MB,带配音、带烧录字幕、带口型人像。另一篇新闻稿跑出来是 200 秒、12MB。

配音不再是找现成音色,是我自己的声音;画面里每一个数字、每一句标题,都是代码画出来的,不会糊、不会错;口型在本地跑,一毛钱不花(这台机器是没有显卡的,纯 CPU)。竖屏那套也跑通了,1080×1920,适合发抖音和视频号。

这套东西真正的价值在哪

回头看,这个智能体最值钱的地方,不是”能出视频”,而是它把一件没法标准化的事情,拆成了九步能标准化的事情。

每一步都有确定的输入、确定的输出、确定的验收标准。画面的部分是代码渲染,所以稳定;声音的部分有字级时间戳,所以对齐;流程的部分有确认闸门,所以可纠错。整条链子不依赖任何一次”生成模型心情好”,跑一百遍,结果都一样。

对做内容的人来说,这解决的是产能和一致性:一篇文章进来,一条风格统一、数据准确的讲解视频出去,中间不用人盯着调每一帧。剩下要动脑的,只有”文案怎么写”——而那恰恰是最该由人来做的一步。

赞(0)
未经允许不得转载:LoveCTO-技术天天练,程序员的 AI 实战笔记 » 我给自己做了个"文章变视频"的智能体

热爱技术 追求卓越 精益求精

登录

找回密码

注册