写这个项目之前我的状态基本就是“剪辑半小时、选 BGM 半小时、导出审片再花半小时”。尤其是口播类中长视频素材动不动就是 20 分钟一条有效金句可能只有 5 分钟粗剪、去语气词、加字幕、找空镜每一步都极其消耗时间。后来我干脆自己写了一个 AI 剪辑客户端把“素材整理 → 语音转写 → 剪辑决策 → 字幕生成 → 成片合成”串成一条本地流水线。经过几次迭代后现在从一条原始口播素材到最终成片智能处理部分只需要约 54 分钟再加上人工审校与微调整体在两小时左右就能产出一条质量还不错的 6 到 10 分钟中长视频。这篇文章会把整个客户端的架构、核心模块、关键代码和完整工作流整理出来。内容偏向工程实战适合三类读者想知道“AI 视频自动剪辑到底怎么实现”的开发者经常做口播、课程、知识类中长视频的内容创作者打算自己搭一套“素材 → 成片”AI 工作流但还没找到切入点的技术人员。下面我们就从项目背景开始拆解。1. 项目背景与核心思路1.1 中长视频剪辑的痛点中长视频通常指时长在 5 到 15 分钟之间的视频典型的像知识科普、课程讲解、数码评测、项目复盘等。这种视频和短视频最大的区别是信息密度更高、剪辑点更多、对段落结构的要求更严格。我做这类视频时经常遇到几个问题素材冗余太多。一条 20 分钟的口播真正能用的内容可能只有 40%剩下都是语气词、重复表述、停顿、离题内容。字幕工作量大。中长视频基本离不开字幕手动听写、断句、校对非常费时。金句和空镜很难匹配。凭记忆找素材里的关键句子效率低或者空镜素材一大堆真到用的时候不知道选哪段。导出渲染周期长。如果剪辑软件工程很复杂预览卡顿导出又得反复调整。这些痛点如果全部靠人工处理一条中长视频花 3 到 4 个小时很正常。我想要的不是用 AI 完全替代剪辑师而是把流水线里最机械、最耗时的环节自动化让人只做最终的审美判断和风格微调。1.2 AI 剪辑客户端的定位在那套方案里我把“AI 剪辑客户端”定义成一个本地优先的任务引擎而不是一个新的剪映或 Premiere。也就是说它不负责复杂的多轨时间线编辑而是负责完成下面这条流水线原始素材入库 ↓ 语音转写ASR ↓ 文本结构化段落、金句、废句识别 ↓ AI 生成剪辑决策保留内容、剪切范围、字幕文本 ↓ 自动执行剪切与拼接 ↓ 字幕生成与烧录 ↓ 输出成片 剪辑报告开发者视角下它就是一个由多个模块组成的 Python 客户端用户视角下它还预留了一个本地操作面板用来配置任务、查看进度和审阅结果。1.3 这套方案解决什么问题这套方案解决的问题是“从素材到粗剪成片”的全自动流程。最终产出的不是复杂特效而是一个符合逻辑、结构干净、有字幕、有基础 BGM 的“半成品”。创作者拿到这个半成品之后只需要人工审阅一遍调整节奏和细节就能达到可用状态。所以核心思路不是让 AI 替你创作而是让 AI 帮你把“脏活累活”全干了让你把时间花在真正需要审美的环节。2. 整体架构与核心流程2.1 客户端总体架构这个客户端整体分三层┌─────────────────────────────────────────┐ │ 操作层本地面板 / CLI 命令入口 │ │ 任务配置、进度查看、结果审阅 │ └─────────────────────────────────────────┘ ┌─────────────────────────────────────────┐ │ 核心引擎层Python 模块 │ │ material / transcriber / strategist / │ │ assembler / subtitle │ └─────────────────────────────────────────┘ ┌─────────────────────────────────────────┐ │ 外部能力层ffmpeg / 本地模型 / LLM API │ └─────────────────────────────────────────┘这样分层的目的是让每个模块可以独立替换。比如今天用 Whisper 做转写明天换成更强的商用 ASR只需要替换transcriber模块今天用某家 LLM 做剪辑决策明天换另一家也不会影响其他模块。2.2 全自动流水线拆解我习惯把流水线拆成 5 个阶段阶段负责模块核心产出素材入库material标准化素材清单、去重、预检测语音转写transcriber带时间轴的文本、说话人分段剪辑决策strategist保留段落、剪切区间、字幕文本成片合成assembler视频剪切、拼接、粗剪成片字幕增强subtitleSRT / ASS 字幕、烧录字幕这 5 个阶段不是完全线性执行的。比如字幕增强可以和成片合成并行准备素材入库的时候也可以提前启动预检测。2.3 54 分钟到底花在哪里很多人看到“54 分钟全自动剪辑”会产生一个疑问这 54 分钟究竟是在剪视频还是在等待 AI 转写以一条 10 分钟成片、原始口播素材 20 分钟为例耗时分布大概是这样的环节耗时说明素材入库与预检测3 分钟检查音视频格式、时长、可用性语音转写8 分钟20 分钟音频的本地转写文本结构化与剪辑决策5 分钟LLM 分析转写文本生成剪辑点位自动剪切与拼接18 分钟ffmpeg 无损剪切后再统一导出字幕生成与烧录15 分钟字幕断句、对齐、压制日志归集与报告5 分钟生成剪辑报告供人工审阅这里的耗时取决于机器性能和素材时长。如果你用的是 GPU 机器转写会快很多如果只是 CPU时间会明显上升。后面我会给出更具体的性能建议。3. 环境准备与项目初始化3.1 运行环境这个项目以 Python 为核心语言外部依赖 ffmpeg。我在开发时用的是操作系统macOS但 Windows / Linux 也兼容Python3.10 以上ffmpeg6.x 以上语音识别本地 Whisper 模型LLM API采用 OpenAI 兼容接口通过配置文件切换服务商。版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。3.2 依赖清单核心依赖如下# requirements.txt fastapi0.115.0 uvicorn0.30.0 faster-whisper1.0.2 openai1.40.0 python-dotenv1.0.1 pydantic2.8.0 moviepy1.0.3 yt-dlp2024.8.6几个依赖的用途faster-whisper本地语音转写支持 int8 量化CPU 上也能跑openai调用 LLM 接口完成剪辑决策不是绑定具体厂商只是使用它提供的兼容 SDKmoviepy负责视频剪切、拼接、音频合成yt-dlp用于某些素材下载场景本项目不是必须。安装命令pip install -r requirements.txt sudo apt install ffmpeg # Debian/Ubuntu brew install ffmpeg # macOS3.3 项目目录设计一个好的目录结构能让你后续扩展模块时不用大改代码。我推荐这样组织ai_editor/ ├── main.py # CLI 入口 ├── config.yaml # 全局配置 ├── core/ │ ├── __init__.py │ ├── material.py # 素材管理 │ ├── transcriber.py # 语音转写 │ ├── strategist.py # AI 剪辑决策 │ ├── assembler.py # 视频剪切拼接 │ └── subtitle.py # 字幕生成 ├── jobs/ │ └── demo_job.json # 单个任务配置 ├── outputs/ # 成片输出 └── logs/ # 日志与剪辑报告我还是比较建议把每个模块写成独立文件而不是把所有逻辑堆在main.py里。因为后续你会不断调试单个模块比如单独跑一次转写、单独看一次剪辑决策独立模块能省很多重复计算的时间。4. 核心模块实现4.1 素材管理模块素材管理模块解决的是“素材太乱”的问题。它的职责有 4 个扫描指定目录下的所有音视频文件读取视频的时长、分辨率、编码信息过滤掉明显不可用的文件输出标准化的素材清单。下面是一个简化版示例# 文件路径core/material.py import json from pathlib import Path import ffmpeg class Material: def __init__(self, path: str): self.path Path(path) self.duration 0.0 self.width 0 self.height 0 self.codec def probe(self): # 使用 ffmpeg probe 读取元数据 data ffmpeg.probe(str(self.path)) stream next( (s for s in data[streams] if s[codec_type] video), None, ) if stream: self.codec stream.get(codec_name, ) self.width int(stream.get(width, 0)) self.height int(stream.get(height, 0)) self.duration float(data.get(format, {}).get(duration, 0)) return self def to_dict(self): return { path: str(self.path), duration: round(self.duration, 2), width: self.width, height: self.height, codec: self.codec, } def scan_materials(source_dir: str): source Path(source_dir) supported [.mp4, .mov, .mkv, .ts, .m4a, .wav] materials [] for f in sorted(source.rglob(*)): if f.suffix.lower() in supported: m Material(str(f)).probe() if m.duration 1: materials.append(m.to_dict()) return materials if __name__ __main__: result scan_materials(./raw) print(json.dumps(result, ensure_asciiFalse, indent2))这段代码的关键点是使用ffmpeg.probe读取视频元数据不依赖 OpenCV轻量且准确过滤掉时长小于 1 秒的文件避免把截图、空文件混入素材库输出 JSON 格式的素材清单方便后续模块读取。4.2 语音转写模块语音转写是整个流水线最核心的一步。剪辑决策是否准确很大程度上取决于转写文本是否带正确的时间轴。我这里选择faster-whisper因为它在 CPU 上也能跑并且支持 VAD语音活动检测可以跳过静音区域减少幻觉。# 文件路径core/transcriber.py from faster_whisper import WhisperModel def transcribe_audio(audio_path: str, model_size: str small): # 可根据机器性能选择模型tiny / base / small / medium / large-v3 model WhisperModel(model_size, devicecpu, compute_typeint8) segments, info model.transcribe( audio_path, vad_filterTrue, languagezh, beam_size5, ) result [] for seg in segments: result.append({ start: round(seg.start, 3), end: round(seg.end, 3), text: seg.text.strip(), }) return { language: info.language, duration: round(info.duration, 3), segments: result, }如果你有 NVIDIA GPU可以把device改成cudacompute_type改成float16转写速度会提升 5 到 10 倍。转写结果最终会写成 JSON 文件{ language: zh, duration: 1200.5, segments: [ { start: 0.0, end: 3.2, text: 大家好今天我们来聊一聊AI视频剪辑 }, { start: 3.2, end: 8.1, text: 很多朋友问中长视频到底应该怎么剪 } ] }这里的“段”是一个个句子级时间片段后面 AI 剪辑决策就是基于这些片段做判断的。4.3 AI 剪辑决策模块这是项目里最有“AI 感”的模块。它的任务是把转写文本切分成“保留”和“删除”两类并给出重新组织后的视频脚本。设计思路将转写片段拼接成带编号的文本调用 LLM让它输出 JSON 结构的剪辑决策解析 JSON生成剪辑指令。这里的提示词非常关键。我一开始的提示词写得太宽泛AI 经常把重要内容删掉。后来我把提示词改成“面向口播视频的结构化剪辑”效果才稳定下来。# 文件路径core/strategist.py import json import openai SYSTEM_PROMPT 你是一名资深视频剪辑师负责口播类中长视频的粗剪。 你会收到带编号的转写片段请完成以下任务 1. 删除语气词、重复表述、离题内容 2. 保留核心观点、案例、数据、总结 3. 对保留下来的片段重新排序如果原文顺序合理则保持原顺序 4. 为每个保留片段生成适合作为字幕的句子删除括号、标记等杂质。 输出必须是 JSON 格式 { kept_segments: [ {segment_ids: [1, 2, 3], subtitle: 字幕文本}, ], summary: 剪辑思路说明 } 不要输出 JSON 之外的任何内容。 def make_clip_decision(segments: list[dict], api_key: str, base_url: str, model: str): client openai.OpenAI(api_keyapi_key, base_urlbase_url) # 把带编号的片段拼成输入文本 lines [] for idx, seg in enumerate(segments): lines.append(f[{idx}] {seg[text]}) user_text \n.join(lines) response client.chat.completions.create( modelmodel, response_format{type: json_object}, messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_text}, ], temperature0.2, ) content response.choices[0].message.content decision json.loads(content) return decision把segment_ids保留下来是为了让剪辑指令能够直接对应到时间轴。AI 返回的不是“第几秒到第几秒”而是“哪几个片段要保留”这样就能避免 AI 编造不精确的时间点。4.4 成片合成模块拿到剪辑决策后下一步就是真正执行剪切和拼接。这里有两种常见做法基于 moviepy灵活但速度慢适合片段不多、需要复杂合成的场景基于 ffmpeg 命令速度快支持无损剪切适合大量片段裁剪。我的方案是混合使用先用 ffmpeg 对原始文件进行无损剪切再把剪切后的片段用 moviepy 拼接、加转场、混音。下面演示 ffmpeg 无损剪切的核心命令ffmpeg -ss 00:00:03.200 -i raw/demo.mp4 -t 4.900 -c copy output/part_000.mp4命令参数说明-ss指定起始时间-t指定片段时长-c copy表示不重新编码速度快保留原始画质。但如果片段之间需要转场、需要统一分辨率那么-c copy就不够用了必须重新编码。下面是一个更完整的 Python 合成示例# 文件路径core/assembler.py import subprocess def cut_segment(input_path, output_path, start, end): duration round(end - start, 3) cmd [ ffmpeg, -ss, str(round(start, 3)), -i, input_path, -t, str(duration), -c:v, libx264, -c:a, aac, -preset, veryfast, -pix_fmt, yuv420p, output_path, -y, ] subprocess.run(cmd, checkTrue, capture_outputTrue) def concat_segments(segment_files, output_path): # 先生成 concat 列表文件 list_file concat_list.txt with open(list_file, w) as f: for item in segment_files: f.write(ffile {item}\n) cmd [ ffmpeg, -f, concat, -safe, 0, -i, list_file, -c:v, libx264, -c:a, aac, -preset, veryfast, output_path, -y, ] subprocess.run(cmd, checkTrue, capture_outputTrue)这个模块的性能优化空间很大。如果你有几十个剪切片段串行执行ffmpeg会慢一些可以用 Python 的concurrent.futures.ThreadPoolExecutor并行执行前期的剪切任务最后再统一拼接。4.5 字幕生成模块字幕模块的输入是 AI 决策返回的“保留片段 字幕文本”。我们需要把字幕文本重新映射到最终成片的时间轴上。这一步容易踩坑因为剪切之后原素材的时间轴和成片的时间轴已经不是一回事了。我的做法是按照保留片段在成片中的累计时长计算每一条字幕的起始时间生成 SRT 或 ASS 字幕文件使用 ffmpeg 把字幕烧录到视频中。生成 SRT 的示例# 文件路径core/subtitle.py def seconds_to_srt_time(seconds): ms int((seconds - int(seconds)) * 1000) h int(seconds // 3600) m int((seconds % 3600) // 60) s int(seconds % 60) return f{h:02d}:{m:02d}:{s:02d},{ms:03d} def build_srt(segments): # segments: [{start: 1.0, end: 4.0, text: ...}] lines [] for idx, seg in enumerate(segments, start1): lines.append(str(idx)) lines.append( f{seconds_to_srt_time(seg[start])} -- f{seconds_to_srt_time(seg[end])} ) lines.append(seg[text]) lines.append() return \n.join(lines)生成好 SRT 之后用 ffmpeg 烧录ffmpeg -i output/final.mp4 -vf subtitlessubtitle.srt:force_styleFontSize18,PrimaryColourHFFFFFF -c:a copy output/final_sub.mp4 -y如果你希望字幕更美观可以考虑生成 ASS 字幕支持更多的样式和位置控制。这里不再展开。5. 全流程实录从原始素材到成片5.1 输入素材与预期结果假设我们有一个raw目录里面是两条视频素材demo_talk.mp4时长 20 分 30 秒口播画面demo_broll.mp4时长 15 分钟空镜素材。目标输出是一条 8 分钟左右的知识类口播成片包含字幕和基础 BGM。5.2 执行命令与输出日志整个流水线通过一个main.py入口启动python main.py --job jobs/demo_job.jsonjobs/demo_job.json示例{ name: demo_job, source_dir: ./raw, output_dir: ./outputs, target_duration: 8, model_size: small, llm_model: your-llm-model, enable_subtitle: true, subtitle_style: default }执行过程中控制台会输出阶段日志[1/5] 扫描素材目录发现 2 个有效素材 [2/5] 语音转写demo_talk.mp4预计耗时 8 分钟 [2/5] 转写完成共 240 个片段总时长 1230.5 秒 [3/5] AI 剪辑决策正在分析文本结构... [3/5] 决策完成保留 82 个片段删除 158 个片段 [4/5] 执行剪切并行处理 82 个片段 [5/5] 生成字幕并烧录 [完成] 成片输出outputs/final_sub.mp4 [完成] 剪辑报告outputs/report.json这里的“删除 158 个片段”听起来很多但口播素材里确实存在大量重复、停顿、口头语片段自动删除是正确的。5.3 两小时的工作时间分配很多人听说“两小时剪出一条中长视频”时会觉得不现实。这里我要说明54 分钟是机器处理时间不是全流程时间。完整的两小时工作流其实是时间段人工 / 机器工作内容0:00 - 0:20人工整理素材、命名、放入 raw 目录0:20 - 1:14机器流水线自动处理 54 分钟1:14 - 1:40人工审阅粗剪成片提出修改意见1:40 - 1:55机器根据修改意见重新渲染1:55 - 2:00人工最终检查、导出发布机器处理是主体但人的判断依然很重要。某些内容 AI 觉得应该删掉但作为创作者你觉得必须保留直接改决策配置就行。6. 常见问题与排查在开发和使用的过程中我遇到了不少问题。下面把高频问题整理成一个排查表问题现象常见原因解决思路语音转写结果包含大量重复文本没有开启 VAD 过滤Whisper 产生幻觉在transcribe中开启vad_filterTrue适当调高beam_sizeAI 剪辑决策把重要内容删掉Prompt 中没有说明哪些内容必须保留优化 SYSTEM_PROMPT增加“核心观点、案例、数据必须保留”的规则生成的字幕和画面不同步重新剪切后没有更新字幕时间轴字幕时间轴应基于成片时间线重新计算不能直接沿用原始转写时间视频中有黑屏或卡顿剪切片段边界不够精准或片段之间缺少转场对每个片段首尾预留 0.2 秒过渡区间并在拼接时加淡入淡出ffmpeg 执行时报“Invalid data found”素材编码异常或格式不支持先用ffmpeg -v error -i file检查原始文件必要时统一转码为 MP4/H.264转写速度太慢CPU 跑大模型未量化改用small或base模型compute_typeint8有 GPU 时改用float16LLM 接口返回非 JSON部分模型对response_format支持不稳定在解析 JSON 前做异常捕获必要时提取文本块中的{...}内容再解析排查工具推荐# 检查视频完整性和编码信息 ffmpeg -v error -i demo_talk.mp4 -f null - # 查看视频时长、分辨率 ffprobe -v error -show_entries formatduration -of defaultnoprint_wrappers1 demo_talk.mp4 # 快速截取 3 秒测试片段验证编码参数 ffmpeg -ss 00:01:00 -i demo_talk.mp4 -t 3 -c:v libx264 -preset veryfast test.mp4 -y遇到转写不准的问题我的建议是先不要盲目换更大的模型而是先检查原始音频质量。如果录音有回声、底噪很大再大的模型也救不回来。先做音频降噪再转写效果会好很多。7. 最佳实践与工程建议7.1 素材规范化AI 流程最怕的是“输入不可控”。如果素材一会儿横屏、一会儿竖屏一会儿 30fps、一会儿 60fps最终拼接时非常容易出现黑边或卡顿。建议在素材入库时做一次规范化统一横屏或竖屏统一分辨率比如 1920x1080 或 1080x1920统一帧率比如 30fps音频采样率统一为 44100Hz 或 48000Hz。你可以在material.py中增加一个normalize方法在进入流水线之前先把素材转成统一规格。7.2 保持任务可重跑剪辑是一个反复迭代的过程。第一次自动生成的结果大概率不是最终版本你需要能够“改一个参数重新跑一遍”而不是每次从零开始。我的做法是每个任务一个独立目录保存中间产物转写结果缓存避免重复转写记录每次 AI 决策的 Prompt 版本和模型版本输出剪辑报告方便回溯是哪一步出了问题。很多人在第一次跑通流水线后就不再优化结果一换素材就翻车。根本原因是中间产物没有缓存每次都要重新转写和决策调试成本极高。7.3 合规与版权注意使用 AI 辅助剪辑时有两类合规风险要特别注意。第一类是语音克隆和肖像权。如果你的方案里涉及 AI 配音、数字人、声音克隆必须确保获得了相关授权并在视频中明确告知观众使用了 AI 合成技术。第二类是素材版权。自动从网络抓取视频、音乐素材时要确认素材的授权范围。很多 BGM 和视频素材虽然可以下载但未必允许商用。我建议在流水线里加入“素材来源与授权状态”字段作为剪辑报告的固定内容。7.4 保留人工审阅环节即使 AI 已经能完成粗剪也不要跳过人工审阅。当前主流模型在处理“语言逻辑”方面已经不错但对于“画面节奏”“情绪表达”“品牌一致性”这些层级判断力还是有限的。我的做法是把成片压缩成低分辨率版本供快速预览比如 720p人工审阅时只关注结构、语气、字幕错误三类问题修改意见以 JSON 形式反馈到系统支持局部重剪。7.5 API Key 与模型配置管理不要把 API Key 写死在代码里。使用.env文件或环境变量管理敏感信息# .env LLM_API_KEYyour-api-key LLM_BASE_URLhttps://your-endpoint.example LLM_MODELyour-model-name并在config.yaml中只保留非敏感配置llm: model: ${LLM_MODEL} base_url: ${LLM_BASE_URL} temperature: 0.2 cut: preset: veryfast pix_fmt: yuv420p subtitle: font_size: 18 font_color: HFFFFFF outline: 2 margin_v: 50这样你可以在不修改核心代码的情况下切换不同的模型服务商也方便多台机器部署。8. 总结与后续学习路线这个 AI 剪辑客户端的核心价值不是“一键生成视频”这种魔术式体验而是把中长视频制作里最耗时、最机械的音频转写、文本理解、片段切分、字幕对轴这些环节自动化把内容创作者从重复劳动中解放出来。如果你也想尝试我的建议是从一个小目标开始不要一开始就追求完整桌面应用。先写一个针对单一口播素材的 Python 脚本跑通“转写 → 决策 → 剪切 → 字幕”这条最小链路再逐步扩展为可配置、可复现的客户端。接下来的学习路线可以参考先熟悉ffmpeg的基本命令尤其是-ss、-t、-c copy、concat的用法学习faster-whisper的转写接口和 VAD 参数调优设计一套适合自己内容类型的 Prompt跑通 AI 剪辑决策加入视频拼接和字幕压制最后再考虑做本地操作面板、任务队列、批量处理等高级功能。实际上当你把这条链路跑通之后你会发现它不仅能剪口播视频换一套“目标结构”它也能用于课程切片、直播切片、会议纪要视频化等场景。关键是你已经拥有了一个可以不断往上面叠加能力的工程框架。动手做一次比看十篇教程都管用。