# 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. 最后统一事件协议，减少前后端耦合与兼容风险

这份文档的目的不是重写现有实现，而是在保留当前可用链路的前提下，把系统逐步推进到可扩展的生产形态。
