本地AI短剧生成工作流拆解:从剧本到成片,数据不出本机

发布时间:2026/10/10 4:40:34
本地AI短剧生成工作流拆解:从剧本到成片,数据不出本机
简介面向短剧与漫剧创作者的开源本地化AI生成工具将故事构思、脚本编写、情节分析、动画/真人剧生成以及工作流管理整合在同一平台实现从故事到成片的一站式处理。所有数据均在本地完成无需上传云端兼顾隐私与安全适合个人创作者和小型团队协同使用。工具特别支持AI真人剧风格生成并提供小说创作辅助能力可快速搭建故事框架、推进角色与情节发展。压缩包共227个文件约41.38MB主要包含Vue/JavaScript前端逻辑、SQL数据库脚本、Markdown说明文档、JSON/YAML配置、演示视频等并附带ffmpeg.exe、启动脚本等本地处理组件。已有830人学习下载。资源内提供run_dev.bat等快捷启动方式通过源码与目录结构读者能够理解短剧生成管线的模块划分、工作流设计及本地化部署要点还可基于开源代码扩展个性化创作流程大幅缩短从创意到成片的制作周期。1. 本地 AI 短剧生成工作流为什么我不再依赖云端短剧工具做短视频内容的人应该都有过这种体验用在线短剧生成工具时故事剧本、角色设定、分镜出图、配音成片全都要在别人服务器上过一遍模型怎么处理、素材存哪里完全是黑匣子。我拆完这套开源本地 AI 短剧生成工具后把原本散落在云端 API 上的整条短剧生产链路彻底搬回本机。它本质上是一个短剧工作流管理平台覆盖从故事到成片的一站式流程数据不出本机灵活性非常高特别适合短剧工作室、个人创作者和 AI 应用开发者。不需要为一次预览就把未发布剧本和角色素材交给第三方这是它最打动我的点。资源包里的核心不是某一个模型而是一套编排好的管线本地 LLM 负责把故事拆成结构化分镜Stable Diffusion 负责生成角色一致的真人风格画面本地 TTS 负责配音Whisper 负责字幕校准最后用 FFmpeg 按分镜时间线合成成片。整条链路可以用命令行驱动也可以按自己的项目改工作流。下面我把拆包过程中实际跑通的路径和踩过的坑按顺序写出来照着做就能把工具用起来。2. 从故事到结构化分镜先让 LLM 把剧情拆成机器能执行的时间轴2.1 为什么用本地 LLM 而不是在线 API数据不出本机是硬需求短剧项目的版权敏感性比一般图文内容高得多角色设定、未发布剧本、导演思路都是核心竞争力。如果生成分镜时把原文发送到在线大模型剧本就等于先给第三方过了一遍。这套工具默认接的是 Ollama 本地推理服务模型文件放在自己机器上所有请求都走 127.0.0.1不碰外网。这一点对工作室来说特别重要尤其是 AI 真人剧这种需要维护角色数字资产的项目角色描述和定妆照一旦外泄后续内容优势就没了。安装 Ollama 的步骤很简单Linux 和 macOS 可以用官方脚本Windows 用安装包然后把模型拉到本地。短剧分镜任务我用 qwen2.5 系列模型7B 足够做基础分镜如果机器内存能到 32GB 以上建议直接上 14B长故事的角色关系和镜头分配会更稳定。zip 里 config.yaml 有 model_name 配置项换模型时不要只改名字还要留意上下文长度参数否则长剧本会被截断成半截。# Linux/macOS 安装 Ollama curl -fsSL https://ollama.com/install.sh | sh # 拉取 qwen2.5 7B 模型足够处理半小时短剧剧本 ollama pull qwen2.5:7b第 4 行是模型拉取需要等几分钟Ollama 跑起来后再执行后续 Python 脚本才不会报连接错误。我在实际使用中会把默认参数保持原样只调整 temperature 和 num_predict这两个参数直接影响分镜结构稳定性。2.2 分镜 JSON 生成让模型输出稳定结构而不是自由文本直接让大模型“写分镜”得到的基本都是散文没法被后续出图、配音脚本直接消费。这套工具的处理方式是把分镜输出格式限制成 JSON 数组每个分镜包含镜头号、场景、动作、角色、景别、时长和台词。模型输出要做到可解析光在 prompt 里写“不要解释”还不够Ollama 提供了 format 参数强制返回合法 JSON配合 few-shot 示例效果更稳定。下面的 Python 函数是我从资源包里的 storyboard_generator.py 整理出来的可以直接保存成脚本使用。注意 prompt 里的字段枚举和角色名要与你自己的角色注册表一致否则生成到一半会出现镜头角色对不上的情况。import requests import json OLLAMA_URL http://127.0.0.1:11434/api/generate def generate_storyboard(script: str) - dict: schema { type: array, items: { type: object, properties: { shot_id: {type: integer}, scene: {type: string}, action: {type: string}, character: {type: string}, camera: {type: string, enum: [近景, 中景, 远景, 特写]}, duration: {type: number}, voiceover: {type: string} }, required: [shot_id, scene, action, character, camera, duration, voiceover] } } prompt f你是短剧导演。把下面的剧情拆成可分镜拍摄的镜头列表。 只输出 JSON 数组不要输出任何解释。 JSON Schema: {json.dumps(schema, ensure_asciiFalse)} 剧情: {script} resp requests.post(OLLAMA_URL, json{ model: qwen2.5:7b, prompt: prompt, format: json, options: { temperature: 0.2, num_predict: 2048 }, stream: False }) return json.loads(resp.json()[response])第 15 行到第 22 行定义了一个严格的输出 schema模型会按这个结构返回。temperature 设为 0.2 是为了让分镜内容少一些随机发挥同一段剧本多次运行结果基本一致。num_predict 限制 2048如果剧本超过 2000 字需要分批传入再合并否则会截断。返回的 JSON 数组可以直接存成 storyboard.json后续所有阶段都从这个文件读取不要再从自然语言剧本二次解析。2.3 分镜表与角色注册表AI 出图前的制片手册分镜 JSON 只是时间轴骨架出图阶段还需要角色长相和服装的稳定描述。资源包里单独维护了一份角色注册表每个角色固定一个 keyvalue 里包含外貌、年龄、服装和负面描述。生成画面时这段内容会被拼到正向提示词的最前面保证同一角色在不同镜头里描述词完全一致。{ ai-characters: { 林远: { gender: male, age: 28, appearance: 短碎发, 深邃双眼, 下颚线清晰, clothing: 黑色皮夹克, 内搭白色T恤, negative: }, 苏晚: { gender: female, age: 24, appearance: 长发, 柳叶眉, 右眼角泪痣, clothing: 浅灰色针织开衫, 高腰牛仔裤 } } }这份 JSON 的价值在于把角色描述从提示词中剥离出来变成可复用的配置。我建议你拿到资源包后先把角色注册表改成自己项目的人设不要直接用例子里的名字和服装。分镜表中每一条记录还需要转换成出图用的拍摄计划转换逻辑在 zip 里的 planner.py 中可以找到核心就是把镜头 JSON 里的 action、camera 与角色描述拼接成一个完整提示词同时生成镜头 ID 和帧时长。镜头号景别运镜时长台词画面提示词来源1远景固定镜头3.0s没台词环境角色全身2中景缓慢推近3.5s“你还是来了”角色上半身动作3特写固定镜头2.5s“我知道你查过”面部表情按这个表去核对 storyboard.json能发现很多模型生成的问题比如镜头时长忽长忽短、角色名称与注册表不一致。我一般会在进入出图前做一次校验把不合法记录过滤掉绝不带着脏数据跑完整条流水线。3. 角色一致性与镜头生成Stable Diffusion 参数与 LoRA 落地的关键设置3.1 用 ComfyUI API 替代手动点按钮工作流即代码分镜和角色描述确定后下一步是生成每个镜头的画面。资源包默认推荐 ComfyUI而不是 Stable Diffusion WebUI原因很简单ComfyUI 的工作流可以保存为 JSON通过 API 动态修改节点参数天然适合批量出图和自动化管线。WebUI 在单张调试时方便但要把几十个镜头的提示词、种子、LoRA 权重逐个塞进去还是脚本化最靠谱。装完 ComfyUI 后先跑一次 main.py然后把 zip 里 workflows 目录下的 short_drama_txt2img.json 放进 ComfyUI 的 user/default/workflows 目录。接着用 Python 的 requests 直接调用 ComfyUI 的 /prompt 接口提交任务。下面这个脚本是从资源包的 render_frames.py 里精简出来的关键是对照工作流 JSON 里的节点 ID 修改输入。import json import requests import uuid COMFYUI_URL http://127.0.0.1:8188 with open(workflows/short_drama_txt2img.json, r) as f: workflow json.load(f) # ComfyUI 的 workflow 结构是 {节点ID: {class_type, inputs}} # 这里假设 5 号节点是正向提示词输入3 号节点是种子输入 final_prompt 1girl, 长发, 柳叶眉, 右眼角泪痣, 浅灰色针织开衫, 中景, 街道背景, cinematic lighting, 8k workflow[5][inputs][text] final_prompt workflow[3][inputs][seed] 12345 req { prompt: workflow, client_id: str(uuid.uuid4()) } resp requests.post(f{COMFYUI_URL}/prompt, jsonreq) print(resp.json())第 13 行的 final_prompt 是正向提示词这里没有包含负面提示词实际工作流里通常有一个单独的 CLIPTerminalTokenize 节点负责负面词。第 14 行 seed 固定为 12345目的是让同一镜头可以复现如果换一台机器seed 不变出图结果也不会变。ComfyUI 的 API 提交后是异步执行的脚本里一般还会轮询 /history/{prompt_id} 接口获取输出图片正式运行时长按每张图约 20 秒估算。启动 ComfyUI 时建议限制监听地址和端口避免开发模式下被局域网内其他设备误访问。python main.py --listen 127.0.0.1 --port 8188如果你的显卡显存只有 6GB 到 8GB启动时加上 --lowvram 参数出图速度会慢一些但稳定性好很多。显存不够时最容易出现“CUDA out of memory”那不是一个特定节点的问题而是整条 prompt 管线在同时加载 checkpoint、CLIP 和 VAE 时超了限低显存模式能强制分步加载。3.2 真人剧不“换脸”的关键LoRA 与参考图机制AI 真人剧最容易翻车的地方是角色一致性同一个角色上一帧还是瓜子脸下一帧脸型就变了。资源包采用两条腿走路全局用角色 LoRA 固定面容局部用 IPAdapter 参考图锁服装和色调。LoRA 需要提前为每个角色准备 8 到 15 张多角度定妆照然后训练一个轻量模型一般 rank 设置在 16 到 32 之间训练量不大但效果明显。在 ComfyUI API 中切换 LoRA 节点时需要修改 lora_name、strength_model 和 strength_clip 三个字段。不要把 LoRA 权重拉到 1.0那样会出现严重的塑料感表情也会僵硬。通常 0.8 到 0.9 之间表现比较自然具体数值要看训练集质量。# 动态切换角色 LoRA lora_node workflow[45][inputs] lora_node[lora_name] characters/lny.safetensors lora_node[strength_model] 0.85 lora_node[strength_clip] 0.85strength_model 控制模型特征注入强度strength_clip 控制文本条件影响强度。如果两个值差距过大会出现画面细节受控但风格失控的情况。我一般保持两者数值一致然后整体微调。角色出现轻度漂移时先降 strength_model 到 0.75如果还是不像就回到训练集补充那个角度的定妆照。如果不想训练 LoRA也可以只用 IPAdapter 的 reference_only 模式把角色注册表里的一张定妆照作为输入图片生成时让图像特征约束每一帧角色。这个方案省训练时间但角色在不同光线和角度下的一致性比 LoRA 弱一些。资源包的工作流里两种方案都预留了接口二选一即可。3.3 不同景别与运镜的参数模板抄作业的一页纸出图阶段最影响观感的是提示词和采样参数。竖屏短剧的分镜建议使用 720x1280 基础分辨率后面交给 FFmpeg 放大到 1080x1920不要一开始就生成超高分图否则显存和耗时都会成倍增长。下面这组参数模板是从资源包的 prompt 模板库中整理出来的按镜头类型选择。镜头类型正向提示词片段负面提示词stepsCFG近景/对话face focus, subtle skin texture, soft rim lightdeformed face, blurry eyes, bad hands284.5中景/动作upper body, dynamic pose, motion blurextra limbs, cropped head265.0远景/交代wide angle, environmental view, atmosphericlow detail, oversaturated265.5特写/情绪extreme close-up, eye detail, tear glintdistorted iris, harsh shadow304.0CFG 这个参数很玄学不是越大越清晰超过 6 后真人皮肤质感会像被磨过一样。这套工具里把 CFG 默认设置在 4 到 5.5 之间配合写实类 checkpoint 效果最自然。steps 控制在 26 到 30 之间足够再高只会增加出图时间画面细节提升非常有限。每个镜头的动态感不是靠 SD 生成的而是后期用 zoompan 模拟所以生成时只需要静态高细节帧就够了。多角色同框时需要在提示词里明确主次关系否则模型容易把两个角色的特征融合成一个奇怪的人。我通常会在提示词里写成“1girl and 1boy”并加上位置关系例如“boy on the left, girl on the right”再辅以 LoRA 分别约束。这一步没有统一参数需要多试几次但角色注册表里的描述词保持恒定是最基本的前提。4. 配音、字幕与成片合成Whisper / TTS / FFmpeg 的快速装配4.1 本地 TTS 与 STT 选型不是越大越好稳定是最优先分镜画面齐了之后配音质量直接决定成片观感。资源包里支持三种本地方案但真正符合“数据不出本机”要求的只有完全离线的那两个。我拆包后第一件事就是对比了它们的稳定性和中文效果。引擎显存占用中文自然度音色克隆是否满足数据不出本机ChatTTS2-4GB自然但偶有吞字不支持满足Coqui XTTS v24-6GB自然支持克隆支持满足Edge-TTS0优秀不支持不满足需要联网这套工具默认配置的是 Coqui XTTS v2因为短剧角色通常需要同一音色贯穿全片。XTTS 只要 3 到 5 秒干净人声采样就能复现音色中文效果在直播和短剧场景里够用。ChatTTS 优点是显存占用小但长句容易吞字适合旁白批量生成不适合角色对白。Edge-TTS 中文音色最舒服但它依赖微软在线服务对“数据不出本机”硬性要求直接不合格。启动 XTTS 前要先确认参考音频的路径zip 里 refs 目录放了几个示例音色建议换成你自己录的演员音样。采样音频里如果有音乐或混响克隆出来的音色会带上杂讯这种现象很难通过参数消除只能重新录。4.2 分镜 JSON 自动生成配音轨脚本化批量处理配音阶段我直接跑资源包里的 generate_audio.py它会读取 storyboard.json按 voiceover 字段逐条生成 wav 文件同时记录时间轴。下面是核心调用注意 split_sentences 这个参数对中文吞字影响非常大。from TTS.api import TTS import json import os tts TTS(model_nametts_models/multilingual/multi-dataset/xtts_v2, gpuTrue) audio_dir outputs/audio os.makedirs(audio_dir, exist_okTrue) with open(storyboard.json, r, encodingutf-8) as f: shots json.load(f) timeline [] for i, shot in enumerate(shots): wav_path f{audio_dir}/shot_{i:04d}.wav tts.tts_to_file( textshot[voiceover], file_pathwav_path, speaker_wavrefs/person1.wav, languagezh, split_sentencesTrue ) timeline.append({shot_id: shot[shot_id], audio: wav_path, duration: shot[duration]})第 14 行 text 直接取分镜里的 voiceover 字段如果台词是空字符串建议跳过生成否则 TTS 会输出一段静音。第 15 行 speaker_wav 是指定角色音色参考不同角色要传不同文件。第 17 行 split_sentences 会把长句按标点切分成短句再合成能明显减少吞字但如果剧本里没有标点这个参数也救不回来。timeline 列表后面的 FFmpeg 拼接要用所以每段音频的路径和时长必须正确记录。批量生成几十段音频时不要反复加载模型TTS 对象初始化一次就够了循环里只调用 tts_to_file。生成过程中如果某一段失败先检查参考音频路径是否存在再检查台词里是否有特殊字符。中文数字最好转成中文拼写比如“1998”转成“一九九八”XTTS 对阿拉伯数字的处理并不稳定。4.3 FFmpeg 按镜头时间线合成视频从静态图到“会动”的镜头素材都齐了之后合成成片的核心是把静态图加运镜效果再和对应音频拼成短视频片段。FFmpeg 的 zoompan 滤镜可以模拟缓慢推近或拉远这一步把静态图变成动态画面。下面命令是资源包里 compose_clip.sh 的简化版本给单张 PNG 和单个 WAV 生成一个 mp4。ffmpeg -y \ -framerate 25 -t 4 -i shots/shot_0001.png \ -i audio/shot_0001.wav \ -filter_complex \ [0:v]scale720:1280,zoompanzmin(zoom0.0005,1.2):xiw/2-(iw/zoom/2):yih/2-(ih/zoom/2):d100:s720x1280,formatyuv420p[v] \ -map [v] -map 1:a \ -c:v libx264 -c:a aac -shortest clips/shot_0001.mp4第 6 行 zoompan 的 d100 表示每帧生成 100 帧输出配合 framerate 25 正好是 4 秒。z 表达式里的 zoom0.0005 是每帧放大的步长数值越大推进越快0.0005 是缓慢推进的典型值。x 和 y 表达式把画面始终居中避免放大时出现黑边。formatyuv420p 是兼容播放器的关键不加这个滤镜某些播放器会花屏。音频通过 -shortest 截断到和画面一样长避免音频比画面长导致镜头切换错位。所有镜头片段生成后最后用 concat 拼接。这里有一个容易忽略的坑所有片段必须使用相同的编码参数、分辨率、像素格式否则 concat 会因为参数不统一而失败。我把所有片段统一用上面的命令生成然后写一个 concat 列表。for f in clips/*.mp4; do echo file $f concat_list.txt; done ffmpeg -f concat -safe 0 -i concat_list.txt -c copy outputs/final_silent.mp4第 2 行 concat 列表里的相对路径要正确如果文件和列表不在同一目录需要写相对路径或者绝对路径。用 -c copy 可以直接拼接不重新编码速度很快前提是所有片段编码参数一致。如果中途出现了黑屏或者音画不同步大概率是某一镜头的参数被单独改动过重新生成那个片段就好。4.4 字幕两路线直接来自剧本 vs Whisper 校准字幕生成有两种做法。第一种最简单直接从 storyboard.json 的 voiceover 字段生成 SRT 文件字幕顺序和台词内容都是可控的因为没有语音识别误差。第二种是用 Whisper 对合成后的成片做一次语音识别再导出 SRT这样能保证字幕和实际发音严格同步但会在 TTS 发音不准时引入不必要的错误。我一般先用第一种生成初版如果某个镜头台词有明显吞字再单独跑 Whisper 校准那一段。Whisper 命令如下medium 模型在中文短剧场景下足够small 模型会漏掉一些轻声词汇。whisper outputs/final_silent.mp4 --model medium --language zh --output_format srt --output_dir outputs生成好的 SRT 再通过 FFmpeg 烧进视频。注意中文字体路径要正确Windows 上通常是 SimHei 或 Microsoft YaHei烧字幕时如果字体找不到命令行不会有明显报错但字幕会不显示。这是很隐蔽的坑我习惯先输出一行带字幕的测试片段确认样式再跑完整成片。5. 避坑本地短剧生成最容易翻车的五个常见问题5.1 工作流加载、模型路径与版本相关的坑现象导入资源包里提供的 short_drama_txt2img.json 时ComfyUI 提示 Missing node type。原因作者机器上装了额外的自定义节点比如 ComfyUI-IPAdapter_plus 和 ComfyUI ControlNet 节点而新环境没有安装这些插件。工作流 JSON 里记录了节点类名ComfyUI 找不到对应类就会拒绝加载整个工作流。解决先看报错信息里缺少的是哪个节点类再到 ComfyUI Manager 里搜索安装。如果不想装额外插件可以回退到内置的基础节点版本但 IPAdapter 方案就不能用了。资源包的 workflows 目录下还有一个 txt2img_basic.json就是不依赖自定义节点的版本首次运行建议先用它。现象出图全黑或者日志里出现 Checkpoint file not found / Lora file not found。原因工作流 JSON 里记录的是作者本机的绝对路径例如 “F:/models/checkpoints/realistic.safetensors”换到你的机器后路径完全失效模型加载失败后输出黑图。解决在 config.yaml 里统一配置模型根目录启动脚本会扫描整个 workflow JSON把所有 ckpt_name、lora_name 的前缀替换成你本机的实际路径。不要手工改每一个节点几十个镜头改起来容易漏。改完之后先跑一个单镜头任务验证模型能正常加载再批量生成。5.2 渲染质量、角色一致性与时间轴相关的坑现象同一角色在不同镜头里脸型明显漂移尤其是侧脸和低头角度。原因角色提示词描述不一致或者每个镜头的 seed 不同LoRA 权重没有被固定。SD 生成时只要角色描述词顺序变化最终人脸特征就可能偏移。解决把所有角色的描述词统一放在提示词最前面的固定片段中不要每镜头重写。seed 用“镜头编号 全局种子”计算保证同一个镜头可以复现但不同镜头又不会完全一样。LoRA 权重固定后不要再单独微调。如果漂移还是明显就把第一镜生成的角色正面图作为 IPAdapter 参考图喂给后续镜头角色一致性会立刻改善。现象成片像幻灯片画面静止没有动感。原因只是把静态图按时长拼接没有加任何镜头运动或者转场效果。观众对静态图连续播放的容忍度很低超过 2 秒就会有明显停滞感。解决每个镜头用 FFmpeg zoompan 模拟推近或拉远镜头之间用 xfade 转场代替硬切。我常用的参数是推进速度 0.0005、转场时长 0.3 秒、每镜头 2 到 3 秒。短剧节奏要快一个镜头超过 4 秒就容易让观众划走。现象TTS 生成的中文台词吞字、跳字或者混入英文/数字。原因XTTS 在文本规范化方面并不擅长标点缺失和阿拉伯数字会打乱发音切分。尤其台词里出现“1998”时它可能会按英文方式读也可能跳过去。解决生成前把数字转换为中文读音例如“1998”转成“一九九八”日期“2023年”转成“二零二三年”。台词文本必须带标点句号或逗号至少保留一个这样 split_sentencesTrue 才有切分依据。如果某句台词反复出问题可以直接人工录制这段替代。现象显存不足生成到一半报 CUDA out of memory。原因直接生成 1080x1920 高清图或者在 ComfyUI 里把 batch size 调成多张。显存被一次性加载的 checkpoint、CLIP、VAE 和中间特征塞满。解决第一步把生成分辨率降到 720x1280最后再由 FFmpeg 放大。第二步在启动命令里加 --lowvram。第三步保持 Batch size 为 1不要为了凑速度批量出图。如果还超限往资源包里的低显存版工作流切它会将图分块处理。6. 进阶用工作流管理平台串联全流程让整套管线一次跑完这套 zip 里的工作流管理平台本质上不是新发明而是把前面所有阶段用一个编排器串起来。它通过命令行接收剧本、角色注册表和输出目录按阶段推进每完成一个阶段就在 run 目录下写一个 manifest.json。这样中途出错可以断点续跑不用每次都从剧本重新生成。# workflow_manager.py 的核心简化 def run(script_path, char_path, out_dir): shots generate_storyboard(open(script_path).read()) for shot in shots: frame generate_frame(shot, render_modeloraipadapter) audio generate_voice(shot) compose_clip(frame, audio, out_dir) concat_all(out_dir) verify_with_manifest(out_dir)第 5 行 generate_frame 会读取 config.yaml 里的模型路径和镜头参数第 7 行 compose_clip 内部走的是第 4 章的 FFmpeg 命令。verify_with_manifest 会检查最终视频时长与所有分镜时长之和是否一致镜头数量和分镜数数量是否匹配。这个自动化编排对做短剧矩阵特别有用只要把多个故事文本按相同格式放进 scripts 目录就能连续产出多条片子。我现在跑新项目不再手动执行每个脚本而是先跑一个最小样例一个两分钟剧本、一个角色、五个镜头强制过一遍全流程。确认配置没有任何路径问题后再铺量生成。这一遍最小验证能拦住大多数翻车情况省掉半夜等片时才发现角色漂移的后悔药。希望帮到你。本文还有配套的精品资源点击获取