6.8 KiB
6.8 KiB
AiVideo Architecture Guide
1. 项目目标与当前定位
该项目是一个 AIGC 视频 POC,核心能力是把用户 prompt 转成三分镜短视频,并通过 Node API 流式返回进度与结果。当前实现已经覆盖:
- 分镜生成(
script) - 单分镜润色(
refine) - 视频渲染与合成(
render) task_id级别隔离输出(outputs/{task_id}/)- Docker 内置 ComfyUI + Node + Python 联动
- 启动时自检(Comfy 可达性 + workflow/节点约束)
整体设计是「Node 作为编排/网关,Python 作为生成引擎」。
2. 目录与职责
server/:Node API + SSE 网关 + 启动自检入口engine/:Python 生成引擎(LLM 分镜、TTS、Comfy、MoviePy 合成)scripts/:Comfy 连通性和 workflow 约束检查configs/config.yaml:运行时配置(Comfy 地址、模型、workflow 映射等)docker-compose.yml:aivideo+comfyui双服务部署dev.sh:本地开发启动/日志/重建封装outputs/{task_id}/:任务级产物目录(分镜、润色结果、最终视频)
3. 运行架构(容器级)
aivideo服务- 运行 Node (
server/index.js) - Node 通过
spawn调用 Python(python -m engine.main) - 对外暴露
3000
- 运行 Node (
comfyui服务- 默认镜像:
jamesbrink/comfyui:latest - 对外暴露
8188 - 挂载
./ComfyUI/*到容器/comfyui/*
- 默认镜像:
- 服务连接
aivideo通过http://comfyui:8188访问 ComfyUI API(容器内 DNS)
4. 应用架构(进程级)
4.1 Node 层(server/index.js)
职责:
- 提供 HTTP/SSE 接口
- 统一生成
task_id并创建输出目录 - 把请求参数透传给 Python 引擎
- 把 Python stdout 协议行转成 SSE 事件
- 启动前执行自检(
check_comfy.py+inspect_comfy_node.py)
主要接口:
GET /api/healthGET /api/script(SSE)POST /api/refine(JSON)POST /api/render(SSE)GET /api/static/...(输出视频静态托管,禁缓存)
并发策略(当前):
- 渲染接口使用单全局锁
isBusy(同一时刻只允许一个渲染)
4.2 Python 引擎层(engine/main.py)
职责:
- 解析参数并分发
step:script/refine/render - 处理全局风格与角色注入
- 与 OpenAI、ComfyUI、TTS、MoviePy 协同
- 按协议输出进度与结构化结果(
SCENE_JSON、PROG、RENDER_DONE)
子模块职责:
engine/script_gen.py:LLM 分镜生成与润色engine/audio_gen.py:Edge TTS 合成旁白engine/comfy_client.py:提交 workflow、轮询 history、提取产物engine/video_editor.py:字幕叠加 + 转场 + 最终拼接engine/config.py:YAML 配置读取
5. 核心流程
5.1 Script 生成
- Node 收到
GET /api/script - 生成
task_id,创建outputs/{task_id} - Node spawn Python
--step script - Python 调 LLM 生成三分镜(无 key 时可 mock fallback)
- Python 输出多行
SCENE_JSON ... - Node 将其转发为 SSE
scene事件
5.2 Refine 润色
- Node 收到
POST /api/refine - 透传当前 scenes/scene 到 Python stdin
- Python 调 LLM 润色指定分镜
- 返回
SCENE_JSON,Node 组装 JSON 响应
5.3 Render 渲染
- Node 收到
POST /api/render(SSE) - 全局
isBusy判定是否可渲染 - Python 先做 TTS(并发),再逐分镜调 Comfy
- 收集视频 + 音频,MoviePy 合成
final.mp4 - Python 输出
PROG进度与RENDER_DONE - Node 转发 SSE 完成事件
6. 关键设计约束
task_id必须贯穿 API 与引擎,保证产物隔离- 启动自检失败时,服务不启动(fail fast)
- workflow 参数注入基于:
- 明确 node_id,或
- class_type fallback 自动定位
- 全局风格/角色必须双重注入:
- LLM prompt 约束
- 渲染前 image_prompt 装饰(character + style + scene)
7. 当前架构优势
- 职责拆分清晰:Node 编排、Python 算法,边界明确
- 可观测性较好:SSE 实时进度 + 结构化协议行
- 生产思路正确:自检机制避免“半可用”状态
- 兼容能力强:mock 路径可脱离 Comfy/LLM 快速调通
8. 主要架构风险与优化方向
P0(优先处理)
-
作业状态只在内存
- 问题:Node 重启后任务状态丢失,前端不可恢复
- 建议:引入任务元数据存储(SQLite/Redis),记录状态机(queued/running/succeeded/failed)
-
单点渲染锁
isBusy- 问题:无法扩展并发;请求高峰体验差
- 建议:升级为队列模型(本地队列或 Redis/BullMQ),支持排队和取消
-
SSE 协议基于字符串前缀
- 问题:协议演进脆弱,难版本化
- 建议:统一 JSON line 协议(字段:
type,task_id,ts,payload,version)
P1(中期)
-
配置与环境耦合较松散
- 建议:增加 config schema 校验(pydantic/JSON schema),启动即检查缺项与类型
-
Comfy 产物识别依赖 history + 文件存在
- 建议:扩展更稳定的完成判定(WebSocket event 或更严格 history 状态判断)
-
缺少全链路 trace id
- 建议:在 Node/Python/Comfy 请求中统一注入
task_id和request_id
- 建议:在 Node/Python/Comfy 请求中统一注入
P2(长期)
-
引擎内聚度可再提升
- 建议:把
script/refine/render拆成独立 use-case 模块,CLI 仅作参数适配
- 建议:把
-
测试体系不足
- 建议:
- 单元测试:config、workflow 注入、scene 解析
- 集成测试:mock 渲染链路
- 冒烟测试:Docker 启动 +
/api/health
9. 推荐重构路线(4 周)
- 第 1 周:任务状态持久化 + API 状态查询接口(
/api/tasks/:id) - 第 2 周:渲染队列化(先单 worker),替换
isBusy - 第 3 周:统一事件协议(JSON line + version),前后端同时改
- 第 4 周:补测试与可观测(结构化日志、错误码、性能指标)
10. 建议新增接口(便于运维和前端)
GET /api/tasks/:task_id:任务状态与阶段信息POST /api/tasks/:task_id/cancel:取消任务GET /api/tasks/:task_id/artifacts:列出产物路径和类型GET /api/system/checks:最近一次自检结果
11. 性能优化清单(先易后难)
- TTS 结果按文本 hash 缓存,避免重复合成
- 分镜视频生成支持失败重试与断点继续
- MoviePy 合成参数按场景切换(开发
veryfast,生产medium/slow) - 对
outputs/增加清理策略(TTL + 最大磁盘占用阈值)
12. 你下一步可以直接做的事
- 先落地任务持久化和状态查询(收益最大、侵入最小)
- 再把
isBusy改为队列(并保留单 worker) - 最后统一事件协议,减少前后端耦合与兼容风险
这份文档的目的不是重写现有实现,而是在保留当前可用链路的前提下,把系统逐步推进到可扩展的生产形态。