458 lines
11 KiB
Markdown
458 lines
11 KiB
Markdown
# AI Workflow Platform MVP Design
|
||
|
||
## 1. MVP 目标
|
||
|
||
用最小但完整的闭环验证这个架构:管理员在后台注册节点、编排工作流并发布;普通用户只上传视频,后端动态组装节点并执行;节点无人使用后自动回收。
|
||
|
||
MVP 只做单机版本,不做 Kubernetes、多租户、计费和分布式 GPU 池。目标是在一台机器上跑通:
|
||
|
||
```text
|
||
上传视频 -> ffmpeg 提音 -> faster-whisper 转写 -> LLM 翻译 -> VR ASS 生成 -> 下载结果
|
||
```
|
||
|
||
## 2. 整体架构
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
Admin[管理后台] --> AdminAPI[FastAPI Admin API]
|
||
AdminAPI --> DB[(SQLite)]
|
||
AdminAPI --> Registry[节点注册中心]
|
||
Registry --> Manager[节点管理器]
|
||
Manager --> NodeA[ASR 节点]
|
||
Manager --> NodeB[LLM 节点]
|
||
Manager --> NodeC[ASS 节点]
|
||
|
||
User[用户前端] --> UserAPI[FastAPI User API]
|
||
UserAPI --> Scheduler[工作流调度器]
|
||
Scheduler --> Registry
|
||
Scheduler --> Runs[WorkflowRun]
|
||
Runs --> DB
|
||
Scheduler --> Manager
|
||
```
|
||
|
||
关键原则:
|
||
|
||
- 节点不是写死在应用里的函数,而是独立进程,按统一协议提供服务。
|
||
- 工作流是数据库里的版本化 DAG,不是业务代码。
|
||
- 调度器只负责编排,不直接加载模型。
|
||
- 节点实例由节点管理器统一创建、分配、回收。
|
||
|
||
## 3. MVP 范围
|
||
|
||
### 3.1 本期包含
|
||
|
||
- 节点注册中心,支持登记节点类型、能力、参数、启动命令、资源要求。
|
||
- 节点生命周期:COLD / STARTING / READY / BUSY / IDLE / STOPPING。
|
||
- 工作流 DAG 定义、校验、版本化、发布。
|
||
- 单机工作流执行器,按拓扑顺序调度节点。
|
||
- 用户上传接口、任务状态查询、进度事件、产物下载。
|
||
- 管理后台节点管理和工作流画布。
|
||
- 用户端工作流列表、上传页、进度页、结果页。
|
||
- 一条可运行的视频字幕示例工作流。
|
||
|
||
### 3.2 本期不做
|
||
|
||
- Kubernetes、GPU 自动扩缩、跨机器节点池。
|
||
- 多租户、用户认证、计费、配额。
|
||
- 节点在线热部署和插件市场。
|
||
- 可视化工作流调试器。
|
||
- 分布式任务队列和 Worker 集群。
|
||
|
||
## 4. 节点抽象
|
||
|
||
### 4.1 节点 Manifest
|
||
|
||
```json
|
||
{
|
||
"id": "faster-whisper",
|
||
"name": "Faster Whisper ASR",
|
||
"version": "1.0.0",
|
||
"capability": "asr",
|
||
"command": ["python", "-m", "nodes.whisper_node"],
|
||
"env": {
|
||
"MODEL_PATH": "./models/faster-whisper-large-v3"
|
||
},
|
||
"input_schema": {
|
||
"audio_uri": "file"
|
||
},
|
||
"output_schema": {
|
||
"srt_uri": "file"
|
||
},
|
||
"max_concurrency": 1,
|
||
"idle_ttl_seconds": 300,
|
||
"health_timeout_seconds": 10
|
||
}
|
||
```
|
||
|
||
字段含义:
|
||
|
||
- `capability`:节点能力类型,例如 `asr`、`llm`、`ffmpeg`、`subtitle`。
|
||
- `command`:节点进程启动命令。
|
||
- `env`:模型路径、API Key 等环境变量。
|
||
- `input_schema` / `output_schema`:用于工作流校验和节点参数检查。
|
||
- `max_concurrency`:单实例最大并发任务数。
|
||
- `idle_ttl_seconds`:空闲多久后回收。
|
||
|
||
### 4.2 节点进程协议
|
||
|
||
每个节点是一个独立进程,对外提供两个 HTTP 接口:
|
||
|
||
```text
|
||
GET /health
|
||
POST /invoke
|
||
```
|
||
|
||
`POST /invoke` 请求:
|
||
|
||
```json
|
||
{
|
||
"run_id": "run_123",
|
||
"node_instance_id": "ni_456",
|
||
"inputs": {
|
||
"audio_uri": "storage/runs/run_123/audio.wav"
|
||
},
|
||
"params": {
|
||
"language": "ja",
|
||
"beam_size": 1
|
||
},
|
||
"output_dir": "storage/runs/run_123/steps/whisper"
|
||
}
|
||
```
|
||
|
||
`POST /invoke` 响应:
|
||
|
||
```json
|
||
{
|
||
"status": "completed",
|
||
"outputs": {
|
||
"srt_uri": "storage/runs/run_123/steps/whisper/output.srt"
|
||
}
|
||
}
|
||
```
|
||
|
||
节点执行过程中可以上报进度事件:
|
||
|
||
```json
|
||
{
|
||
"run_id": "run_123",
|
||
"node_id": "faster-whisper",
|
||
"progress": 0.45,
|
||
"message": "正在转录第 120/300 段"
|
||
}
|
||
```
|
||
|
||
MVP 阶段节点通过本地文件系统交换产物,未来把 `storage/...` 换成对象存储 URI 即可。
|
||
|
||
### 4.3 节点生命周期
|
||
|
||
```text
|
||
COLD -> STARTING -> READY -> BUSY -> READY -> IDLE -> STOPPING -> STOPPED
|
||
| |
|
||
+---------- ERROR <------------+
|
||
```
|
||
|
||
- `COLD`:没有进程,只有注册信息。
|
||
- `STARTING`:进程已拉起,等待 `/health` 就绪。
|
||
- `READY`:进程就绪,模型已加载,可以接收任务。
|
||
- `BUSY`:正在执行任务。
|
||
- `IDLE`:任务完成,等待下一任务或回收。
|
||
- `STOPPING`:超过 `idle_ttl_seconds`,正在优雅退出。
|
||
- `ERROR`:启动或执行失败。
|
||
|
||
节点管理器定时扫描实例状态:
|
||
|
||
- 任务申请节点时,优先分配 `READY` 实例。
|
||
- 没有实例时,从 `COLD` 启动。
|
||
- `BUSY` 实例不能被回收。
|
||
- `IDLE` 超过 TTL 后先卸载模型并停止进程。
|
||
- 被调度器标记为长期热门的节点可以配置 `keep_warm=true`,不回收。
|
||
|
||
## 5. 工作流定义
|
||
|
||
工作流是一份带版本号的 DAG JSON:
|
||
|
||
```json
|
||
{
|
||
"name": "video-to-vr-ass",
|
||
"version": 1,
|
||
"nodes": [
|
||
{
|
||
"id": "extract",
|
||
"node_type": "ffmpeg-extract",
|
||
"params": {
|
||
"sample_rate": 16000,
|
||
"channels": 1
|
||
}
|
||
},
|
||
{
|
||
"id": "asr",
|
||
"node_type": "faster-whisper",
|
||
"params": {
|
||
"language": "ja"
|
||
}
|
||
},
|
||
{
|
||
"id": "translate",
|
||
"node_type": "llm-translate",
|
||
"params": {
|
||
"target_language": "zh-CN"
|
||
}
|
||
},
|
||
{
|
||
"id": "ass",
|
||
"node_type": "srt-to-dual-eye-ass",
|
||
"params": {
|
||
"resolution": "3840x1920"
|
||
}
|
||
}
|
||
],
|
||
"edges": [
|
||
{"from": "extract", "to": "asr"},
|
||
{"from": "asr", "to": "translate"},
|
||
{"from": "translate", "to": "ass"}
|
||
],
|
||
"entry_inputs": {
|
||
"video_uri": "file"
|
||
},
|
||
"final_outputs": {
|
||
"cn_srt": "translate.srt_uri",
|
||
"ass": "ass.ass_uri"
|
||
}
|
||
}
|
||
```
|
||
|
||
工作流校验规则:
|
||
|
||
- 所有 `node_type` 必须已注册。
|
||
- 边必须引用存在的节点。
|
||
- 每个节点的输入必须有上游产物或工作流入口输入。
|
||
- 工作流必须是无环图。
|
||
- 发布前必须通过一次 dry-run 校验。
|
||
|
||
## 6. 执行模型
|
||
|
||
用户触发工作流后:
|
||
|
||
1. 创建 `WorkflowRun`,状态为 `QUEUED`。
|
||
2. 调度器读取工作流 DAG,做拓扑排序。
|
||
3. 对每个节点调用节点管理器申请实例。
|
||
4. 节点执行完毕后,产物 URI 写入 `Artifact`。
|
||
5. 所有节点完成后,`WorkflowRun` 状态变为 `COMPLETED`。
|
||
6. 任一节点失败,按重试策略重试;超过次数后标记 `FAILED`。
|
||
|
||
MVP 的调度策略:
|
||
|
||
- 只支持线性链和简单 DAG。
|
||
- 没有依赖关系的节点可以并发执行。
|
||
- 节点之间只通过产物 URI 通信,不允许共享内存。
|
||
- 每个节点执行前检查健康状态,节点不可用时重新启动。
|
||
|
||
WorkflowRun 状态:
|
||
|
||
```text
|
||
QUEUED -> RUNNING -> COMPLETED
|
||
| |
|
||
+-> FAILED +
|
||
|
|
||
+-> CANCELLED
|
||
```
|
||
|
||
## 7. 数据模型
|
||
|
||
MVP 使用 SQLite,表结构如下。
|
||
|
||
### nodes
|
||
|
||
```text
|
||
id, name, version, capability, manifest_json, status, created_at
|
||
```
|
||
|
||
### workflows
|
||
|
||
```text
|
||
id, name, slug, description, published, latest_version, created_at
|
||
```
|
||
|
||
### workflow_versions
|
||
|
||
```text
|
||
id, workflow_id, version, definition_json, created_at
|
||
```
|
||
|
||
### workflow_runs
|
||
|
||
```text
|
||
id, workflow_id, workflow_version, status, current_node_id,
|
||
progress, error, input_uri, created_at, updated_at
|
||
```
|
||
|
||
### node_instances
|
||
|
||
```text
|
||
id, node_id, status, pid, address, started_at, last_used_at,
|
||
busy_since, error
|
||
```
|
||
|
||
### artifacts
|
||
|
||
```text
|
||
id, run_id, node_id, name, uri, mime_type, size, created_at
|
||
```
|
||
|
||
## 8. API 设计
|
||
|
||
### 管理端
|
||
|
||
```text
|
||
GET /api/admin/nodes
|
||
POST /api/admin/nodes
|
||
GET /api/admin/nodes/{node_id}
|
||
DELETE /api/admin/nodes/{node_id}
|
||
|
||
GET /api/admin/node-instances
|
||
POST /api/admin/node-instances/{id}/stop
|
||
|
||
GET /api/admin/workflows
|
||
POST /api/admin/workflows
|
||
GET /api/admin/workflows/{workflow_id}
|
||
PUT /api/admin/workflows/{workflow_id}
|
||
POST /api/admin/workflows/{workflow_id}/validate
|
||
POST /api/admin/workflows/{workflow_id}/publish
|
||
```
|
||
|
||
### 用户端
|
||
|
||
```text
|
||
GET /api/apps
|
||
POST /api/apps/{workflow_id}/runs
|
||
GET /api/runs/{run_id}
|
||
GET /api/runs/{run_id}/events
|
||
GET /api/runs/{run_id}/artifacts/{artifact_name}
|
||
```
|
||
|
||
上传接口使用 `multipart/form-data`:
|
||
|
||
```text
|
||
POST /api/apps/{workflow_id}/runs
|
||
Content-Type: multipart/form-data
|
||
body: file=video.mp4
|
||
```
|
||
|
||
进度接口使用 SSE:
|
||
|
||
```text
|
||
GET /api/runs/{run_id}/events
|
||
Accept: text/event-stream
|
||
```
|
||
|
||
## 9. 前端设计
|
||
|
||
### 管理后台
|
||
|
||
页面:
|
||
|
||
- `/admin/nodes`:节点注册列表。
|
||
- `/admin/workflows`:工作流列表。
|
||
- `/admin/workflows/{id}`:React Flow 画布。
|
||
- `/admin/workflows/{id}/publish`:发布确认。
|
||
|
||
画布能力:
|
||
|
||
- 左侧节点面板,按 capability 分组。
|
||
- 拖拽节点到画布。
|
||
- 节点参数面板。
|
||
- 保存、校验、发布。
|
||
|
||
MVP 不实现节点在线调试器,只实现“保存 -> 校验 -> 发布 -> 后台试跑”。
|
||
|
||
### 用户端
|
||
|
||
页面:
|
||
|
||
- `/`:已发布工作流列表。
|
||
- `/app/{workflow_id}`:上传页。
|
||
- `/runs/{run_id}`:进度页和结果下载。
|
||
|
||
用户端不展示任何工作流细节,只展示输入表单、进度、产物。
|
||
|
||
## 10. 示例工作流
|
||
|
||
### 节点清单
|
||
|
||
| 节点 | capability | 作用 |
|
||
| --- | --- | --- |
|
||
| `ffmpeg-extract` | media | 提取 16kHz 单声道音频 |
|
||
| `faster-whisper` | asr | 日语语音转写为 SRT |
|
||
| `srt-normalize` | subtitle | 清洗 SRT 格式 |
|
||
| `llm-translate` | llm | 调用 Ollama/OpenAI 兼容接口翻译 |
|
||
| `srt-to-dual-eye-ass` | subtitle | 生成 VR 双眼 ASS |
|
||
|
||
### 验收标准
|
||
|
||
1. 管理员注册上述节点,画一条工作流并发布。
|
||
2. 用户上传一个测试视频。
|
||
3. 后端自动按 DAG 执行提音、转写、翻译、ASS 转换。
|
||
4. 用户能看到进度并能下载 `.CN.srt` 和 `_dual_eye.ass`。
|
||
5. 节点空闲超过 TTL 后进程被回收。
|
||
6. 新注册一个 ASR 节点后,编辑器中可以替换节点并重新发布,不需要改业务代码。
|
||
|
||
## 11. 技术选型
|
||
|
||
```text
|
||
Backend: Python 3.12 + FastAPI + Pydantic + SQLite
|
||
Executor: 单进程调度器 + 子进程节点
|
||
Node host: Python HTTP 服务(Uvicorn)
|
||
Storage: 本地 storage/ 目录
|
||
Admin UI: React + Vite + React Flow
|
||
User UI: React + Vite
|
||
Deploy: Docker Compose
|
||
```
|
||
|
||
MVP 不引入 Redis 和 Celery,因为单机单调度器已经可以演示核心概念。任务状态持久化在 SQLite,节点状态由节点管理器维护。
|
||
|
||
## 12. 实现里程碑
|
||
|
||
### M1:节点基础能力
|
||
|
||
- 节点注册表。
|
||
- 节点管理器。
|
||
- 一个 `echo` 示例节点。
|
||
- 节点生命周期与 TTL 回收。
|
||
|
||
### M2:工作流执行
|
||
|
||
- 工作流 CRUD 和版本化。
|
||
- DAG 校验。
|
||
- 调度器按拓扑顺序执行。
|
||
- WorkflowRun 状态机和产物管理。
|
||
|
||
### M3:用户服务闭环
|
||
|
||
- 上传接口。
|
||
- 任务状态和 SSE 进度。
|
||
- 产物下载。
|
||
|
||
### M4:管理后台 UI
|
||
|
||
- React Flow 工作流画布。
|
||
- 节点注册页面。
|
||
- 发布流程。
|
||
|
||
### M5:字幕演示工作流
|
||
|
||
- ffmpeg 提音。
|
||
- faster-whisper 转写。
|
||
- LLM 翻译。
|
||
- SRT 转 VR 双眼 ASS。
|
||
- 完整端到端验收。
|
||
|
||
## 13. 后续扩展路径
|
||
|
||
- SQLite -> PostgreSQL。
|
||
- 本地文件 -> MinIO/S3。
|
||
- 单机调度器 -> Redis + Celery/Temporal。
|
||
- 子进程节点 -> 容器节点 + Kubernetes。
|
||
- 节点管理器 -> KEDA 自动扩缩和 scale-to-zero。
|
||
- 注册表 -> 插件市场。
|
||
- 单用户 -> 多租户、鉴权、配额、计费。
|