From 15100a0c97fc7b25d0e17e829b45845fa0af10a1 Mon Sep 17 00:00:00 2001 From: cat-shark Date: Sat, 8 Aug 2026 21:08:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9D=E5=A7=8B=E5=8C=96=20WOV=20met?= =?UTF-8?q?a=20=E4=BB=93=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 10 ++ AGENTS.md | 219 ++++++++++++++++++++++++ MVP-DESIGN.md | 457 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 686 insertions(+) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 MVP-DESIGN.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..814d2fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# AI Workflow Platform is a workspace meta-repo. +# Product code and node repos live in independent git repositories below. +/* +!/AGENTS.md +!.gitignore +!MVP-DESIGN.md + +# Local/editor noise +.DS_Store +Thumbs.db diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6a21431 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,219 @@ +# AGENTS.md + +## 项目定位 + +本仓库是 WOV AI Workflow Platform 的工作区 meta-repo,不参与业务逻辑,也不包含平台或节点的业务代码。它的作用是维护跨仓库协调信息,把 API、前端、基础设施和各个模型节点关联起来。 + +业务代码分别放在独立 Git 仓库中,每个模型节点也是一个独立 Git 仓库,可以单独管理版本、发布和部署。 + +平台的目标是让管理员在后台注册和编排各种模型节点,把编排结果发布成独立服务;普通用户只上传视频或输入数据,由后端按工作流动态组装节点并执行。节点在无人使用时回收,新模型出现后可以快速注册为新节点并复用到已有工作流或新应用中。 + +详细 MVP 设计见 [MVP-DESIGN.md](./MVP-DESIGN.md)。 + +## 仓库结构与 Git 管理 + +本仓库的定位与 LPT 工作区 meta-repo 一致:主仓库不保存产品代码,只保存跨仓库说明、目录约定和初始化方式。子项目目录各自是独立 Git 仓库。 + +## Python 环境与 uv 管理 + +所有 Python 子项目统一使用 uv 管理虚拟环境和依赖,禁止直接使用 pip 修改依赖。 + +### 基础命令 + +```powershell +uv sync # 在子项目目录中安装当前 pyproject.toml 的依赖(含 dev group) +uv run # 在子项目虚拟环境中运行命令 +uv add # 添加依赖并更新 lockfile +uv lock # 仅更新 uv.lock +``` + +### 约定 + +- 每个 Python 子项目在自己的目录下执行 `uv sync`,虚拟环境位于对应目录的 `.venv`。 +- 测试依赖放在 `[dependency-groups] dev`,由 `uv sync` 默认安装,不需要额外 `--extra dev`。 +- 子项目之间的本地依赖使用路径依赖,例如 `wov-api` 和 `wov-node-*` 通过 `../wov-sdk` 引用 `wov-sdk`。 +- 新增 Python 依赖时先进入对应子项目目录,使用 `uv add`,不要修改系统 Python 或全局环境。 +- `uv.lock` 属于对应子仓库,由子仓库单独管理。 +- 不在 meta-repo 根目录创建统一 Python 环境;环境必须归属各独立业务仓库。 +- 不使用 `pip install`、`pip freeze` 或手写 `requirements.txt` 替代 uv。 + +## 测试与覆盖率 + +- 所有 Python 子项目必须达到 100% 行覆盖率。 +- 测试必须调用真实代码路径,不得在测试类中重写业务逻辑来模拟被测功能。 +- 只允许在 I/O 边界使用 mock/stub:文件系统、网络、子进程、环境变量、时间。 +- 每个子项目使用 pytest,并在 CI 中启用 `--cov-fail-under=100`,覆盖率不达标视为失败。 +- 新增业务代码时,必须同步补齐覆盖其真实路径的测试。 + +## 目标运行环境 + +- 本服务的最终部署目标是 Linux,通常以 Docker/Kubernetes 容器运行。 +- 当前 Windows 只作为本地开发环境,不允许在业务代码中写死 Windows 路径、盘符或 Windows 专用命令。 +- 路径处理统一使用 `pathlib`;节点 Python 解析必须同时兼容 Windows 的 `.venv/Scripts/python.exe` 和 Linux 的 `.venv/bin/python`。 +- ffmpeg 在 Linux 上可使用系统包,也允许通过 `imageio-ffmpeg` 使用内置二进制,节点代码不能假设 ffmpeg 一定在 PATH。 +- 测试必须可以在 Windows 和 Linux 上运行;涉及平台分支的代码应同时覆盖两种路径解析。 +- 100% 行覆盖率只保证代码路径被覆盖,不覆盖外部运行环境状态,例如端口占用、防火墙、权限和残留进程。 +- 端口、进程、网络权限类问题不属于行覆盖率范围,应通过启动检查、端口检查和 uvicorn 冒烟测试补充。 +- 本地出现 `WinError 10013` / `WinError 10048` 时,先用 `netstat -ano | findstr :` 确认是否有残留监听进程,再决定停进程或换端口。 + +## Windows / PowerShell 执行规则 + +- 默认 shell 视为 Windows PowerShell 5.1;不要假设 Bash、zsh 或 PowerShell 7。必要时先查 `$PSVersionTable.PSVersion`。 +- Windows 桌面、Visual Studio/MSBuild、WPF/WinForms/WinUI、COM、注册表、服务、系统托盘、PyInstaller、UI 自动化等任务优先用 Windows 原生环境;不要默认切 WSL。 +- 仅在 Linux 部署、Bash 脚本、Linux 工具链或项目本身位于 WSL 时考虑 WSL;跨环境前确认仓库路径、依赖和运行目标。 +- 禁止把 Bash 语法交给 PowerShell:`python - <<'PY'`、`cat <<$` 或引号时默认用单引号;只有需要变量插值时才用双引号。 +- 外部程序路径可能有空格时,用 `& 'C:\path with spaces\tool.exe' arg1`;多参数外部命令优先数组 splatting。 +- PowerShell 数组传给外部程序时直接 splat;不要把多个路径或参数拼成一个带空格的字符串。 +- 文件操作优先 PowerShell 原生命令和 `-LiteralPath`;遇到参数不存在先按 PowerShell 5.1 兼容写法处理。 +- 复杂 Python 不用 `python -c`;涉及 SQL、JSON、中文、反斜杠路径、换行或多层引号时,用仓库脚本、临时 `.py` 或 `apply_patch`。 +- 禁止在 PowerShell 用 Bash here-doc。临时传 Python 源码只允许 PowerShell here-string,且尽量保持 ASCII。 +- Python 源码含中文常量时,不通过 PowerShell 管道传给 `python -`;用 UTF-8 脚本文件、仓库脚本或 `\uXXXX`。 +- 不只依赖 `chcp 65001` 解决编码;必要时同时设置 `$OutputEncoding`、`[Console]::InputEncoding`、`[Console]::OutputEncoding`、`PYTHONUTF8`、`PYTHONIOENCODING`。 +- 搜索文本/文件优先 `rg` / `rg --files`;多个根目录作为多个独立参数传入。 +- `sqlite3.exe` 不存在时用 Python `sqlite3` 查询,不反复尝试不存在的 CLI。 +- 编辑文件优先 `apply_patch`;不要用复杂 PowerShell 字符串重写文件。 +- 数据库或生产内容写操作前先查询当前数据;写入必须有明确筛选条件,禁止无条件 `DELETE` / `UPDATE`。 +- 出现 `ParserError`、`CommandNotFoundException`、`ParameterBindingException`、`Cannot find path`、Python `SyntaxError` 时,先排查 shell 语法、引用、PATH、PowerShell 5.1 兼容和 `workdir`。 +- 同一 PowerShell 命令连续失败两次后,停止微调长命令;改短命令、脚本文件、数组 splatting 或分步验证。 + +### 子仓库规划 + +| 目录 | 用途 | 仓库 | +| --- | --- | --- | +| `wov-api` | FastAPI 后端:节点注册、工作流、调度、用户 API | `wov/wov-api.git` | +| `wov-web` | React 管理后台和用户端 | `wov/wov-web.git` | +| `wov-sdk` | 节点协议、Manifest、SDK 与测试工具 | `wov/wov-sdk.git` | +| `wov-node-echo` | 示例节点,验证节点协议和生命周期回收 | `wov/wov-node-echo.git` | +| `wov-node-ffmpeg` | 提音、转码、媒体处理节点 | `wov/wov-node-ffmpeg.git` | +| `wov-node-whisper` | faster-whisper ASR 节点 | `wov/wov-node-whisper.git` | +| `wov-node-llm` | LLM 翻译/润色节点 | `wov/wov-node-llm.git` | +| `wov-node-ass` | SRT/ASS 和 VR 双眼字幕节点 | `wov/wov-node-ass.git` | +| `wov-docs` | 系统文档与设计文档 | `wov/wov-docs.git` | +| `wov-infra` | Docker/Kubernetes/部署清单 | `wov/wov-infra.git` | + +仓库地址中的 `wov/` 是命名空间占位符,实际地址确定后必须更新本表。节点数量不限于上表,新增模型时新增独立仓库即可。 + +### meta-repo 规则 + +- 主仓库只维护 `AGENTS.md` 和 `.gitignore`,不创建业务代码。 +- MVP 阶段设计文档暂时保留在主仓库;`platform-docs` 建立后迁出。 +- `.gitignore` 忽略所有子项目目录,避免把独立 Git 仓库提交进主仓库。 +- 首次 clone 主仓库后,子项目目录缺失时按本表 clone;主仓库自身不复制子项目内容。 +- 修改子项目时必须进入对应目录,并使用 `git -C <目录> ` 执行 Git 操作。 +- 禁止在 meta-repo 根目录执行跨仓库 `git add .`,禁止删除子项目内的 `.git`。 +- 每个节点仓库必须包含 `AGENTS.md`、`node.manifest.json`,并使用 Git tag 作为节点版本。 +- 平台运行时不应依赖 meta-repo 源码树中的节点代码,节点由节点注册中心按仓库地址和版本拉取。 + +### 子仓库初始化 + +```powershell +git clone wov-api +git clone wov-web +git clone wov-sdk +git clone wov-node-echo +git clone wov-node-ffmpeg +git clone wov-node-whisper +git clone wov-node-llm +git clone wov-node-ass +git clone wov-docs +git clone wov-infra +``` + +进入或修改某个子仓库前,先读取该子仓库根目录的 `AGENTS.md`;如果缺失,则读取 `README.md`。 + +## 最终设计目标 + +以下原则是平台的北极星,任何设计决策都应朝这些方向移动: + +1. 模型能力标准化。每个模型都包装成统一协议下的节点,节点声明能力、输入输出、资源要求和生命周期策略。 +2. 工作流即数据。工作流以版本化 DAG 存储,不写死在业务代码里;发布后的工作流自动成为面向用户的服务。 +3. 节点按需运行。节点只在被使用时初始化,空闲后卸载模型、停止进程并回收资源。 +4. 动态组装。用户触发工作流时,调度器按 DAG 动态申请节点实例并执行,而不是预启动整条流水线。 +5. 高复用。ASR、LLM、TTS、字幕处理等节点可被任意工作流复用;新增模型通过注册新节点接入,无需重写业务应用。 +6. 可弹性伸缩。API、调度器、节点实例最终可以独立水平扩展,节点宿主可以漂移,不依赖单机状态。 + +## 最终目标下的关键不变式 + +后续开发中,以下约束不应被破坏: + +- 节点之间不直接调用,只通过产物 URI 交换数据。 +- 节点实例必须无状态,中间产物落在共享存储,不放在节点进程内部。 +- 工作流必须是数据文件或数据库记录,不允许把步骤顺序写死在应用代码里。 +- 节点管理器是节点实例生命周期的唯一所有者,API 和调度器不能直接拉起或杀死节点进程。 +- 节点协议和 Manifest 要长期稳定,宁可先少做功能,也不轻易改协议。 +- 存储、队列、调度器都要通过抽象边界隔离,方便从单机实现替换为分布式实现。 +- 用户端永远只看到“输入 -> 进度 -> 结果”,不暴露工作流细节。 + +## MVP 的妥协理由 + +MVP 采用单机简化实现,原因是先验证核心抽象,而不是过早引入分布式复杂度。每个妥协都必须保留通往最终目标的路径。 + +### 1. 单机运行,而不是 Kubernetes + +- 妥协:MVP 在一台机器上运行 API、调度器和节点进程。 +- 理由:先验证节点协议、生命周期和动态组装是否成立。 +- 通往目标的路径:节点已经独立成进程,后续可以把进程宿主替换为容器 Pod,把节点管理器升级为集群调度器。 + +### 2. SQLite,而不是 PostgreSQL + +- 妥协:MVP 使用 SQLite 持久化节点、工作流和任务状态。 +- 理由:降低运维成本,单机足够承载演示数据。 +- 通往目标的路径:数据访问通过 Repository 层隔离,后续替换为 PostgreSQL 不需要改业务逻辑。 + +### 3. 本地文件存储,而不是 S3/MinIO + +- 妥协:MVP 使用本地 `storage/` 目录保存输入和产物。 +- 理由:减少一个基础设施依赖,方便本地调试。 +- 通往目标的路径:所有产物都使用 URI 传递,禁止直接使用裸路径;后续引入存储适配器并切换到对象存储。 + +### 4. 单机调度器,而不是 Celery/Temporal + +- 妥协:MVP 由进程内调度器顺序执行工作流节点。 +- 理由:先证明工作流 DAG 和节点调度的正确性。 +- 通往目标的路径:调度逻辑和任务队列解耦,WorkflowRun 状态已持久化,后续可以替换为 Redis + Celery/Temporal。 + +### 5. 子进程节点,而不是独立容器 + +- 妥协:MVP 节点以本地子进程方式启动,通过 localhost HTTP 通信。 +- 理由:避免容器网络、镜像仓库和资源调度带来的初期复杂度。 +- 通往目标的路径:节点协议是 HTTP,进程启动参数来自 Manifest,后续可直接包成容器并保留同一协议。 + +### 6. 只实现线性链和简单 DAG + +- 妥协:MVP 先支持顺序执行和少量并行。 +- 理由:字幕示例工作流是线性链,先跑通完整闭环。 +- 通往目标的路径:调度器已经按 DAG 拓扑排序,复杂分支和循环留到后续阶段。 + +### 7. 无鉴权、计费、多租户 + +- 妥协:MVP 不实现用户认证、配额和计费。 +- 理由:这些不是验证平台核心抽象的必要条件。 +- 通往目标的路径:API 设计上保留用户与工作流的边界,后续增加 auth 中间件和用量记录。 + +### 8. 不做在线插件热部署 + +- 妥协:MVP 通过注册接口登记节点,不实现插件市场或热加载。 +- 理由:先保证节点协议和生命周期稳定。 +- 通往目标的路径:节点注册表结构保留版本和 Manifest,后续可以扩展为可下载、可安装的插件包。 + +## MVP 完成标准 + +1. 管理员可以注册节点,并在画布上编排工作流后发布。 +2. 用户上传视频,后端自动组装节点完成“提音 -> 转写 -> 翻译 -> ASS 转换”。 +3. 用户能看到进度并下载最终产物。 +4. 节点空闲超过 TTL 后被回收,新任务到达时能重新初始化。 +5. 新增一个同能力节点后,不修改业务代码即可替换工作流中的节点并重新发布。 + +## 开发时的检查清单 + +每完成一个功能,都应检查: + +- 是否仍然满足“工作流即数据”。 +- 节点是否无状态,是否只通过产物 URI 通信。 +- 是否新增了绕过节点管理器的生命周期操作。 +- 是否新增了无法平滑迁移到对象存储或分布式队列的硬编码路径。 +- 是否把工作流步骤、节点协议或模型调用写死在了业务代码里。 +- 是否保持了“用户端只看到输入、进度、结果”的边界。 diff --git a/MVP-DESIGN.md b/MVP-DESIGN.md new file mode 100644 index 0000000..c8a90f6 --- /dev/null +++ b/MVP-DESIGN.md @@ -0,0 +1,457 @@ +# 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。 +- 注册表 -> 插件市场。 +- 单用户 -> 多租户、鉴权、配额、计费。