AI视频任务如何实现预览确认与中断恢复
RapVideo将生成流程拆为场景图片、用户确认和带音频的视频生成,并用D1保存任务状态、R2存储媒体。文章聚焦供应商已接单但请求超时、用户关闭页面等情况下的任务恢复。
AI 深度解读
把一两张肖像变成十秒说唱视频,难点远不止写提示词。RapVideo 作者把流程拆成场景图、人工确认、视频生成两个任务,再围绕扣费、超时、回调和存储建立恢复机制。核心判断是:用户要能提前检查人物,后台也要能在浏览器关闭后继续交付。
这篇值得程序员读的部分,是它把 AI 生成接到了真实产品的故障边界上:请求超时未必失败,模型成功也未必已经交付。前者决定能不能重试、退款,后者决定任务什么时候才算完成。
为什么先花 10 credits 看一张图
RapVideo 使用 React 和 TanStack Start,运行在 Cloudflare Workers 上,D1 保存任务状态,R2 保存生成媒体。用户先提交肖像和场景选项,生成静态场景,检查后再启动带音频的视频生成,最终拿到 MP4 和任务历史。
这个检查点解决的是人物和构图问题:两个人是否都出现了,脸有没有混在一起,麦克风是否挡脸。静态图更容易检查,也给后续视频提供明确的起始帧。
它同时是一次消费决策。场景生成花 10 credits,视频生成花 90 credits,重新生成场景还要另付一次费用,不能叫「免费预览」。不过,确认首帧并不保证整段视频的人脸稳定。
作者还主动缩小了输入范围:单人或双人,studio、street、stage 三种场景,friends、birthday、team 三种主题。单人需要一张参考图,双人需要两张,不开放任意歌词和上传音频。
浏览器提交 templateId: "rap"、templateVersion: 1、step: "scene",以及 settings 和 references。其中 prompt 固定为空字符串,不接受自由补充。服务端模板决定供应商路由、提示词、输入键和扣费额度,并验证素材归属,为供应商生成访问签名。参考图限 JPEG、PNG、WebP,大小小于 10 MB。
可选项越少,服务端越容易把每一次生成的规则讲清楚、查明白。
模板版本还会跟随任务保存。默认配置以后改了,也能知道旧结果由哪一版生成。禁用前端下拉框无法替代这些服务端校验。
场景管构图,视频管表演
作者写场景提示词时,优先描述能检查的条件:恰好两位成年表演者,第一张参考图在左、第二张在右,保留各自面容、发型和服装,不混合身份。构图要求面向镜头、双手可见、膝盖以上入画、头顶留出空间。
录音室场景再补上录音间、深色吸音板、悬挂麦克风和暖琥珀色灯光。相比堆审美形容词,这些条件更方便验收,也方便定位问题。如果场景图已经串脸,先修参考图或场景,比修改视频提示词更直接。
视频阶段使用 Replicate 上的 Kling 2.6,输入已确认的场景图,生成十秒视频并开启音频:
const input = {
start_image: signedSceneUrl,
duration: 10,
generate_audio: true,
prompt: compiledPerformancePrompt,
};
要看的是 start_image:第二步承接第一步的构图。generate_audio: true 则把声音也交给这次生成。
表演提示词要求镜头稳定、动作轻微、有原创 hip-hop 节拍。双人场景安排左侧人物在第 1 到第 4 秒说唱,右侧人物在第 5 到第 8 秒回应,没开口的人点头配合;同时要求保持人数、不切镜头。
这些时间只是意图,不是帧级音频时间线。台词、发音、轮流演唱和口型都可能偏离。流程也不克隆上传的声音,并要求避免模仿名人歌手。
因此,验收必须看完整段人脸、听实际唱词。首帧好看,只能证明首帧好看。
请求超时后,为什么不能马上重试或退款
最麻烦的边界是:应用已发出请求,供应商已经接单,但连接在返回响应前断开。此时再提交一次,可能产生第二笔付费任务;直接退款,又可能让仍在运行的任务失去对应扣费。
作者先创建本地任务,再调用供应商。明确拒绝可以按失败处理;是否接单无法确认,就保持待核实状态,交给后续对账与恢复。
同一次尝试的任务身份由用户、工具、流程、购买引用、步骤和尝试次数共同确定。重复请求返回已有任务;用户主动要求重做场景,才算新的尝试。
但这里有上限:如果应用始终拿不到供应商的 prediction ID,后续可能无法定位那笔外部任务。本地幂等键只能避免本地重复,不能保证外部 API 严格只执行一次。
扣费与任务创建则放在同一个数据库原子操作中:
begin atomic operation
debit credits
insert pending task
enforce concurrency limit
commit
submit the reserved task to the provider
关键在 commit 后才提交供应商。数据库操作同时检查用户生成并发限制,避免只扣费没任务,或任务没有资金保障;外部调用无法加入这笔事务,才需要保留 pending 和提交结果不确定的处理。
退款也必须可重复处理:恢复原消费对应的余额、记录审计事件,后续轮询再看到相同失败时,发现任务已经结算,不能再次发放 credits。
模型完成以后,还要把结果救回来
浏览器轮询、历史刷新、webhook 和定时恢复共用一个对账函数。Cloudflare 每五分钟运行恢复任务,检查已有输出、查询已知 prediction、保存媒体,再决定是否结算。
多个入口可能同时到达,因此同一任务用两分钟数据库租约限制并发,外部操作预算更短,为 90 秒。结算时检查租约令牌,防止旧 worker 超时后回来覆盖新决定。
webhook 会校验签名和时间戳,核对本地 prediction 映射,再查询供应商当前状态,不会拿到回调里的任意 URL 就下载。一小时恢复期限到达时,取消也可能与完成同时发生,必须检查最终供应商状态后再结算。
供应商返回 succeeded,还不等于用户已经拿到视频。
输出必须保存进 R2,并关联到本地任务。对象键由任务 ID、prediction ID 和输出序号稳定生成,对象元数据也记录这些标识。这样即使 R2 上传成功、数据库更新失败,下次仍能找到对象并补上关联,避免重复下载。
存储查询失败也不能当成「对象不存在」。R2 暂时不可用时应保留重试机会。下载端还限制供应商主机、拒绝重定向,并检查媒体类型和大小。
作者的恢复测试覆盖重复回调、存储失败、完成与取消竞争,也包含浏览器关闭后十六分钟才完成的视频,以及供应商输出失效后找回已上传对象的情况。这些是受控测试,没有给出生产可靠性百分比。
我的判断
我认同把人工检查放在更贵的视频步骤前面,也认同把交付责任留在服务端。相比一次生成到底,场景确认提供了明确的排错边界;相比依赖页面轮询,后台恢复更符合长任务的实际生命周期。
但「先确认场景能省多少钱、提高多少满意度」,原文没有对照数据。它可能减少无效视频,也增加等待和额外场景消费,目前只能视为作者的产品取舍。
收窄输入同样有前提。固定主题、固定双人台词适合这个产品,却不能直接证明开放式创作也该这样设计。原文也没有交代数据库原子预留和租约的完整实现,不能仅凭伪代码确认所有并发边界都已解决。
提示词中的人数、站位和时间约束有助于检查,但没有变成模型能力保证。状态机测试和视觉、听觉验收必须分开做。
能拿来做什么
- 做图片转视频产品时,可以把构图确认设计成独立步骤,明确展示两阶段价格,并在动画等待期间保留场景图。
- 用 Node 或 Workers 对接付费生成 API 时,先持久化任务和消费记录,区分明确失败与提交结果未知,避免让页面上的「重试」直接创建新任务。
- 把轮询、webhook 和定时恢复接到同一套结算逻辑,并用稳定对象键处理「文件已上传、数据库未更新」的中断。
- 验证 AI 视频流程时,同时检查重复退款、迟到完成等故障路径,以及完整视频的人脸、台词和口型,不用首帧代替整段验收。