Memo:我的 AI 视频生成工作流架构速记
起因
在 VPS 上折腾出来一套能自动生成带配音视频的工作流。跑通了,但细节太多容易忘。这篇笔记记下架构和各部分的关系,当作自己的”地图”。
一句话描述
n8n 是导演,One API 是中介,NVIDIA 是编剧,video_service 是后期制作,edge-tts 是配音员。
整体架构图
text
┌─────────────────────────────────────────────────────────┐
│ 你的 VPS │
│ │
│ ┌───────────────────┐ ┌──────────────────────┐ │
│ │ Docker 容器 │ │ 宿主机进程 │ │
│ │ │ │ │ │
│ │ ┌─────────────┐ │ │ ┌────────────────┐ │ │
│ │ │ n8n │ │ │ │ video_service │ │ │
│ │ │ (编排层) │ │ │ │ .py (Flask) │ │ │
│ │ └──────┬──────┘ │ │ └───────┬────────┘ │ │
│ │ │ │ │ │ │ │
│ │ ┌──────▼──────┐ │ │ │ │ │
│ │ │ one-api │ │ │ │ │ │
│ │ │ (LLM 网关) │ │ │ │ │ │
│ │ └─────────────┘ │ │ │ │ │
│ └───────────────────┘ └──────────┼───────────┘ │
│ │ │ │
│ │ 172.17.0.1:3000 │ 172.17.0.1:5001
└─────────┼───────────────────────────────┼──────────────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ NVIDIA API │ │ edge-tts │
│ (外部 LLM) │ │ (外部 TTS) │
└─────────────┘ └─────────────┘
各部分角色
| 组件 | 角色 | 干什么 | 跑在哪 |
|---|---|---|---|
| n8n | 导演 | 按顺序调用别人,不干具体活 | Docker 容器 |
| One API | 中介 | 转发请求,管理 Key 和用量 | Docker 容器 |
| NVIDIA API | 编剧 | 根据 prompt 生成脚本 | 外部服务 |
| video_service.py | 后期制作 | 合成视频、调用 TTS | 宿主机 Flask 服务 |
| edge-tts | 配音员 | 文字转语音 | 外部服务 |
| MoviePy + FFmpeg | 剪辑工具 | 图片合成、编码 | 本地库 |
数据流向
text
1. n8n (Manual Trigger) 启动
↓
2. n8n → One API: "帮我生成脚本"
↓
3. One API → NVIDIA: 转发请求
↓
4. NVIDIA → One API: 返回脚本
↓
5. One API → n8n: 返回脚本
↓
6. n8n → video_service: "用这个脚本做视频"
↓
7. video_service → edge-tts: "生成配音"
↓
8. edge-tts → video_service: 返回 MP3
↓
9. video_service (MoviePy): 合成视频 + 音频
↓
10. video_service → n8n: 返回 MP4 路径
↓
11. 视频文件: /opt/rag-nvidia/videos/video_xxx/output.mp4
关键连接
| 从 | 到 | 用什么 | 路径 |
|---|---|---|---|
| n8n | One API | HTTP POST | 172.17.0.1:3000 |
| One API | NVIDIA | HTTP POST | 公网 |
| n8n | video_service | HTTP POST | 172.17.0.1:5001 |
| video_service | edge-tts | Python 调用 | 公网 |
为什么这么拆
为什么 n8n 不直接调 NVIDIA?
One API 统一管理 Key、用量、渠道。换供应商时只改 One API,n8n 不动。
为什么视频服务不塞进 n8n?
n8n 的 Docker 镜像极简,没有 Python、没有 shell、装不了系统包。视频合成必须跑在外面。
为什么视频服务做成 HTTP 服务,不做成脚本?
n8n 容器里没有 Python 环境。做成 HTTP 服务后,n8n 只需发 HTTP 请求,跨容器无障碍。
核心代码位置
| 文件 | 位置 | 作用 |
|---|---|---|
video_service.py | /opt/rag-nvidia/ | Flask 服务,视频合成入口 |
static_images/ | /opt/rag-nvidia/ | 3 张静态占位图 |
videos/ | /opt/rag-nvidia/ | 输出目录 |
| n8n 工作流 | n8n 界面 | 编排逻辑 |
关键参数备忘
One API 地址(n8n 里用):
text
http://172.17.0.1:3000/v1/chat/completions
视频服务地址(n8n 里用):
text
http://172.17.0.1:5001/generate
LLM 模型:
text
nvidia/nemotron-3-ultra-550b-a55b
max_tokens: 4096(推理模型需要大预算)stream: false(关闭流式避免超时)- Timeout:
300000(5 分钟)
TTS 音色:
text
zh-CN-YunxiNeural(云希,男声)
踩过的坑(速查)
| 坑 | 原因 | 解决 |
|---|---|---|
| n8n 访问 172.17.0.1 被拦 | K3s/UFW 把 INPUT policy 设为 DROP | iptables -I INPUT 1 -s 172.17.0.0/16 -p tcp --dport <端口> -j ACCEPT |
| 脚本被截断成十几字 | 推理模型 token 预算不够 | max_tokens: 4096 |
| n8n 请求 18 秒超时 | 默认 timeout 是 18000ms | 改成 300000 |
| JSON Body 报控制字符错误 | 脚本含换行符 | 用”Using Fields Below”代替”Using JSON” |
| 视频无声音 | 音频被裁剪或未合并 | 让图片时长 = 音频时长 / 图片数 |
| venv 挂载进容器不可用 | 符号链接指向宿主机路径 | 视频服务跑宿主机,n8n 走 HTTP |
当前状态
| 环节 | 状态 |
|---|---|
| n8n 一键触发 | ✅ |
| LLM 生成 97 字脚本 | ✅ 约 37 秒 |
| edge-tts 配音 | ✅ 23 秒 |
| 视频合成 | ✅ 2.2MB MP4 |
| 音视频同步 | ✅ 时长自动匹配 |
