内容流水线

同一个角色,跨 60 个镜头不能换脸

漫剧生成比单纯文生图复杂得多,需要在剧本→素材→分镜→成片四阶段维持一致性并优雅处理失败。

漫剧(漫画形态的短剧)从剧本到成片的生产链路远长于单纯的文生图。剧本 → 分镜 → 角色与场景素材 → 逐镜生成 → 合成渲染,每一环的产出都是下一环的输入,且需要保持跨镜的一致性。如果某一镜失败,必须能在不影响已完成部分的前提下重试。

为什么要分阶段

剧本从创意到成片经过多个中间态,每个中间态都是可编辑、可回退、可重复使用的产物。分阶段的关键:

输入输出契约的明确性。每个阶段接收前一阶段的输出,产出下一阶段的输入。比如剧本阶段输出结构化的Markdown(约定好场景标题、角色台词、舞台提示的格式),分镜阶段才能用LLM稳定地解析。如果剧本是自由文本,分镜的LLM解析就会出现"有时识别到5镜,有时识别到7镜"的不确定性。

失败隔离。成片阶段某一镜的视频生成失败(比如Ark服务的内容审核拒绝),应该只影响那一镜的重试,不能拖垮整个成片流程。这需要每一阶段的产出都能被独立操作——镜头列表可以编辑某一镜而不重新生成全部;资产库里的某个角色形象可以独立重生成。

人工介入点。素材库、分镜表都是可编辑的中间产物,创作者可以在这些点做审核和调整,而不是端到端黑盒生成。

阶段一:剧本与分镜结构化

剧本本身是Markdown自由文本,但为了让后续阶段能稳定解析,需要约定轻量的标记:

  • 场景标题:## 场景 N:标题
  • 角色台词:角色名:台词
  • 舞台提示:(斜体提示)*斜体*

这些约定不强制校验(v1保留灵活性),但供分镜阶段的LLM解析时参考。字数统计时去除标记后计数。

版本快照是这一阶段的核心设计。Script Doctor AI产生修改建议时,系统先存当前版本快照(reason=pre_ai),再让AI整稿覆盖,覆盖后再建一条快照(reason=ai_write)。这样创作者可以随时「撤销」回某个时刻的版本。

阶段二:素材库与一致性

从剧本和故事圣经抽取角色和场景,生成可复用的图片素材。这一阶段的难点不在单张图的生成质量,而在跨镜的一致性

一个角色在第1镜和第5镜出现,两张图片必须是同一个人——脸型、发色、服饰都要对应。如果靠Prompt描述来维持一致("一个穿蓝色衣服的女性,20岁左右,棕色头发…"),生成出来的两张图几乎不可能完全一致。

解决方案:把角色固化成可复用资产。从剧本首次提到某个角色时,生成一次角色立绘,存入项目级资产库。后续分镜中再提到这个角色,不再重新生成,而是直接引用库里的立绘。场景同理——虽然不同镜头的场景可能视角不同(客厅的远景vs近景),但起点的关键元素(颜色、家具、灯光风格)要保持一致,也要存入场景库。

资产存储采用相对路径,锁定机制防止意外覆盖。生成时使用runImageGeneration加2小时退避重试和size回退。状态机跟踪每个资产的生成进度(pending→generating→ready或failed)。上传路由校验文件类型、大小和MIME,存盘时用assetId派生路径防目录穿越。

阶段三:逐镜分镜表

从剧本和资产库,用LLM拆分镜。输出结构化的镜头列表,每镜包含:

  • sceneHeading:场景标题
  • description:画面描述(用于生成首帧)
  • motionDesc:运镜和动作(后续SP4图生视频用)
  • dialogue:台词
  • visibleAssetIds:该镜涉及的角色/场景资产ID列表
  • durationSec:时长

并行度与失败隔离。分镜表生成后,逐镜生成首帧是一个可高并发的任务。BullMQ的il_COMIC队列处理这些job,并发度由worker数量控制。某一镜的首帧生成失败(比如参考资产图太多超过上限),置该镜的frameStatus为failed,其他镜头继续进行,不互相阻塞。失败的镜头可以单独点「重生成」。

生成首帧时用runImageEdit(图生图),把visibleAssetIds指向的资产图作为参考(最多5张),确保新生成的画面里的人物和环境与资产库对应。如果某镜没有关联资产(visibleAssetIds为空),退化到纯文生图。

分镜表本身可编辑。创作者可以调整每镜的描述、调整时长、增删镜头、改变资产关联。这些编辑操作走PATCH,不需要重新生成分镜。

阶段四:渲染合成与续跑

逐镜图生视频,再用ffmpeg按顺序拼接成成片。v1版本的合成只处理视频序列的顺序拼接,不包括字幕、转场、配乐等后期元素——这些在后续版本中补齐。

续跑的关键:每个镜头的videoPath一旦写入就不再改写。处理job时,先检查这一镜是否已有videoPath;如果有,跳过生成,直接进入下一镜。这样即使整集渲染中途中断(比如火山Ark服务宕机、ffmpeg进程被杀),重新触发时只需补齐缺失的镜头,不会重复扣费或重复调用Ark。

进度回写。每生成完一镜的视频,更新Job的progress字段反映完成镜数。前端轮询获取进度,展示"第3/12镜已完成"的UI。

Ark API。调用POST https://ark.cn-beijing.volces.com/api/v3/contents/generations/tasks,入参包含首帧公网URL和描述文本,轮询task直到succeeded状态才能下载video_url。首帧URL必须是公开可达的,因为Ark无法访问鉴权路由。系统新增路由GET /api/comic/public/frame/[shotId](无鉴权,shotId是cuid不可猜),供Ark拉取。

超时与退避。Ark请求设30分钟超时,失败时429状态码进行指数退避重试。内容审核拒绝(违反社区政策)时,Ark返回特定错误码,映射到友好中文提示"建议尝试非写实风格",提示用户调整prompt后重试。

ffmpeg拼接采用concat demuxer。生成的最终视频存入videos/final-<episodeId>.mp4,路径由episodeId派生防穿越。

跨阶段的一致性保证

整个流水线的稳定性关键:

  1. 资产库的冻结。一旦素材阶段完成,后续分镜和渲染阶段引用的资产ID必须指向已锁定的资产。资产库提供lock机制,锁定的资产不能被重生成或删除,防止后续镜头生成时资产被意外覆盖。
  2. 版本快照链。剧本的每次修改都记录快照,分镜由该时刻的剧本生成。如果剧本后来改了,已生成的分镜保持不变(不会自动更新),创作者需要明确点「重新生成分镜」才会用最新剧本重新拆。
  3. 归属校验。每个操作都绑定到userId和projectId。asset→project→userId的链路确保用户A生成的资产对用户B完全不可见,也不会因为user B的分镜生成而被修改。

失败处理设计

LLM解析失败。分镜拆解时LLM返回的JSON格式错误或关键字段缺失,parseNovelJson失败→不建脏数据到数据库,给前端返回友好错误提示"无法理解剧本结构,请检查格式"。

图片生成失败。某镜首帧出图失败,该镜frameStatus=failed,msg字段记录错误("参考图过多"、"描述文本违反政策"等)。同集的其他镜继续生成。用户可以修改这一镜的描述,单独重试。

视频生成中断。若Ark不可达或超时,该镜videoStatus=failed,整集renderStatus=failed。前端展示"第7镜生成失败",用户点「重试」时,系统检查已有videoPath的镜头(第1-6镜)都跳过,只重新生成第7镜及后续。

并发冲突。剧本在自动保存中,同时AI写入修改,系统比对updatedAt时间戳,冲突时给前端返回"冲突,请确认是保留用户编辑还是接受AI修改",不静默覆盖。

漫剧流水线的设计核心是把每个中间产物结构化并可编辑,这样一致性问题转化为资产库的管理问题,失败也能精确定位到某个镜头而不必推倒重来。

星野的头像

星野 XINGYE

一个人维护 AI 平台的工程师。这里记录 63 篇复盘:18 份故障档案、OTA、架构演进与工作流。