# 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` - `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/health` - `GET /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 生成 1. Node 收到 `GET /api/script` 2. 生成 `task_id`,创建 `outputs/{task_id}` 3. Node spawn Python `--step script` 4. Python 调 LLM 生成三分镜(无 key 时可 mock fallback) 5. Python 输出多行 `SCENE_JSON ...` 6. Node 将其转发为 SSE `scene` 事件 ### 5.2 Refine 润色 1. Node 收到 `POST /api/refine` 2. 透传当前 scenes/scene 到 Python stdin 3. Python 调 LLM 润色指定分镜 4. 返回 `SCENE_JSON`,Node 组装 JSON 响应 ### 5.3 Render 渲染 1. Node 收到 `POST /api/render`(SSE) 2. 全局 `isBusy` 判定是否可渲染 3. Python 先做 TTS(并发),再逐分镜调 Comfy 4. 收集视频 + 音频,MoviePy 合成 `final.mp4` 5. Python 输出 `PROG` 进度与 `RENDER_DONE` 6. 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(优先处理) 1. **作业状态只在内存** - 问题:Node 重启后任务状态丢失,前端不可恢复 - 建议:引入任务元数据存储(SQLite/Redis),记录状态机(queued/running/succeeded/failed) 2. **单点渲染锁 `isBusy`** - 问题:无法扩展并发;请求高峰体验差 - 建议:升级为队列模型(本地队列或 Redis/BullMQ),支持排队和取消 3. **SSE 协议基于字符串前缀** - 问题:协议演进脆弱,难版本化 - 建议:统一 JSON line 协议(字段:`type`, `task_id`, `ts`, `payload`, `version`) ### P1(中期) 4. **配置与环境耦合较松散** - 建议:增加 config schema 校验(pydantic/JSON schema),启动即检查缺项与类型 5. **Comfy 产物识别依赖 history + 文件存在** - 建议:扩展更稳定的完成判定(WebSocket event 或更严格 history 状态判断) 6. **缺少全链路 trace id** - 建议:在 Node/Python/Comfy 请求中统一注入 `task_id` 和 `request_id` ### P2(长期) 7. **引擎内聚度可再提升** - 建议:把 `script/refine/render` 拆成独立 use-case 模块,CLI 仅作参数适配 8. **测试体系不足** - 建议: - 单元测试: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. 你下一步可以直接做的事 1. 先落地任务持久化和状态查询(收益最大、侵入最小) 2. 再把 `isBusy` 改为队列(并保留单 worker) 3. 最后统一事件协议,减少前后端耦合与兼容风险 这份文档的目的不是重写现有实现,而是在保留当前可用链路的前提下,把系统逐步推进到可扩展的生产形态。