我做了一个 AI 视频生成画布

我做了一个 AI 视频生成画布

最近做了一个本地 AI 视频生成项目,名字叫「帧流 Frameflow」。

它可以把图片、提示词和视频生成任务放在一张画布上。图片可以连到视频节点,也可以在输入框里通过 @ 引用。生成完成后,结果会自动保存到素材库。

项目的起因很简单。

平时用 AI 生成视频,提示词、参考图和生成结果经常散落在不同地方。视频生成时间又比较长,页面一刷新,任务状态还可能丢失。

所以我想做一个工具,把这些东西集中到一张画布里。

项目现在能做什么

目前已经完成的功能:

  1. 创建和保存多个视频项目。
  2. 上传本地图片,在画布中作为参考图使用。
  3. 调用 Agnes Video V2.0 生成视频。
  4. 支持文生视频、图生视频、多图参考和关键帧模式。
  5. 支持图片生成和图片编辑。
  6. 可以通过连线或 @ 引用其他节点。
  7. 保存每次生成的请求、响应和结果。
  8. 页面刷新后,继续查询没有完成的视频任务。
  9. 在工作台查看项目、素材和生成记录。
  10. 使用账号密码登录,API Key 只保存在服务端。

项目是本地运行的,还不是一个对外提供服务的 SaaS。

第一版先把接口跑通

刚开始没有做复杂功能,目标只有一个:在本地创建视频任务,并且能拿到最后的视频。

技术栈如下:

前端:React、Vite、React Flow、Zustand
后端:Node.js、Express
数据库:SQLite
视频接口:Agnes Video V2.0

React Flow 用来处理节点和连线,Zustand 保存画布状态。后端负责调用 Agnes、上传素材和保存任务记录。

数据库使用 Node.js 22 内置的 node:sqlite,不需要再安装单独的数据库服务。对本地项目来说比较省事,复制数据库文件也能直接备份。

第一版的页面很像一个开发工具:

左边是节点库
中间是画布
右边是参数
底部是任务日志

界面不算好看,但文生视频、任务轮询和结果保存这条流程先跑通了。

中间加了很多节点,后来又删了

项目中间有一版做了 16 种以上的节点,包括文本、脚本、分镜、镜头卡、Prompt、反向词、图片、视频、音频、素材库和输出。

当时觉得节点越完整,越接近一套 AI 视频工作流。

真正用起来后发现,有些节点只是界面做好了,后面的生成流程并没有完全接上。节点数量多了以后,用户还要先弄懂每个节点应该怎么连。

所以后来又做了一次精简。

当前主要保留:

  1. 图片素材。
  2. 图片生成。
  3. 图片编辑。
  4. Agnes 视频生成。
  5. 素材库。
  6. 图片和视频结果。

角色库、模板、视频合成这些功能还没有做完,就直接标记为规划中,不再放模拟数据。

这次调整比较实际。项目功能少一点没关系,先保证页面上能看到的功能是真的能用。

后来把首页改成了工作台

第一版打开就是画布,对开发者来说没问题,但项目多了以后不方便管理。

后来增加了一个工作台,里面可以看:

  • 最近的项目;
  • 已经上传或生成的素材;
  • 视频和图片生成任务;
  • Agnes 服务是否配置成功。

进入项目后才打开画布。

画布底部还增加了一个输入框,可以直接写提示词,也可以用 @ 引用画布里的图片。

比如:

让 @人物参考图 站在雨夜街头,
镜头从远景缓慢推进到半身特写。

选择图片后,系统会自动创建一条引用关系。如果引用一张图片,视频模式自动切换成图生视频;引用多张图片,则切换成多图模式。

这个操作比手动找端口、拉连线快一些。需要固定工作流时仍然可以手动连线。

开发时遇到的几个问题

1. 本地图片不能直接交给云端

浏览器能打开这样的地址:

http://localhost:8787/uploads/example.png

但 Agnes 运行在云端,它访问不到我电脑上的 localhost。

一种解决方法是把所有图片先上传到对象存储。但这个项目目前主要在本地使用,为此再接一套存储服务有点重。

最后的处理方式是:

  1. 后端识别本站 /uploads/ 目录里的图片。
  2. 读取本地文件。
  3. 转成 base64 格式。
  4. 再把图片内容发送给 Agnes。

同时限制单张图片不能超过 10MB,并且只允许读取上传目录里的文件。

公网图片不需要转换,继续使用原地址。

2. 视频任务不能只靠一个定时器

视频生成不是立即返回结果。创建任务后,要拿着任务 ID 每隔一段时间查询状态。

刚开始就是普通的轮询,后来遇到了不少情况:

  • 创建任务后马上刷新页面;
  • 生成过程中切换到另一个项目;
  • 同一个项目开了多个浏览器标签页;
  • 查询接口暂时返回 404、429、502 或 503;
  • 状态已经完成,但视频地址还没有返回;
  • 上游已经创建任务,前端却没有保存任务 ID。

现在的处理方式:

  1. 拿到任务 ID 后先保存,再开始查询。
  2. 重新打开项目时恢复未完成任务。
  3. 用户切换项目后,结果仍然写回原来的项目。
  4. 多个标签页之间加锁,避免重复查询。
  5. 临时网络错误自动重试。
  6. 连续失败后暂停轮询,下次打开项目再继续。

视频生成接口真正麻烦的部分,不是发起请求,而是保证任务不会因为刷新或网络问题丢掉。

3. 多张参考图的顺序不能乱

在多图和关键帧模式里,图片顺序是有含义的。

比如两张图片分别代表首帧和尾帧,如果后台把顺序调换,生成结果就会发生变化。

现在会优先按照用户在提示词中 @ 图片的顺序组织请求。只通过连线加入、没有被引用的图片,再按原顺序放到后面。

这部分代码不复杂,但如果没有明确规则,同一张画布可能生成不同的请求。

4. 改节点时要考虑旧项目

项目数据保存在 SQLite 里。节点端口调整后,旧项目里保存的端口名称可能已经不存在。

后来增加了一份端口注册表,统一使用下面这类名称:

in:asset
out:image
out:video

旧端口也保留了迁移关系。打开历史项目时,先转换旧端口,再加载画布。

一些已经废弃的节点也会在加载时处理,避免旧项目直接打不开。

项目里是怎么使用 AI 的

这个项目的开发也用了不少 AI 工具。

大致流程是:

  1. 先写开发文档,列清楚功能范围、数据结构和验收条件。
  2. 用设计工具制作 HTML 交互原型。
  3. 根据原型实现 React 页面。
  4. 用编码助手修改具体功能和处理报错。
  5. 自己运行真实任务,再根据结果继续调整。

AI 写界面和基础代码比较快,但也容易一次加太多功能。

例如前面提到的 16 种节点,做出来不难,判断哪些节点应该保留反而更难。接口返回字段不一致、任务恢复、旧数据迁移这些问题,也都是实际运行后才逐步补上的。

我现在使用 AI 开发项目时,会尽量先把任务拆小,并且写清楚什么情况才算完成。

目前还没有做的功能

帧流现在可以完成图片到视频的基本流程,但还不是完整的视频制作工具。

下面这些功能还在计划中:

  1. 使用 LLM 自动拆解脚本和分镜。
  2. 根据分镜批量创建视频任务。
  3. 使用 ffmpeg 拼接多个视频片段。
  4. 接入对象存储,不再依赖 base64 传本地图片。
  5. 保存和复用常用工作流。
  6. 增加更完整的自动化测试。

最后的总结

这个项目最开始只是想把 Agnes 视频接口放进一张节点画布。

做完第一版后才发现,调用接口是最简单的部分。后面花时间最多的是任务保存、异常重试、本地素材、旧数据兼容和界面精简。

现在它已经可以作为一个本地工具使用,但还有不少功能没完成。

先把正在使用的流程做稳定,再继续增加新的节点。这是我做这个项目到现在最直接的感受。