- Go 42.6%
- TypeScript 27.8%
- Python 19%
- JavaScript 5.3%
- CSS 4.9%
- Other 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| Controller | ||
| docs/guides | ||
| EManager | ||
| EWorker | ||
| Platform | ||
| .gitignore | ||
| go.work | ||
| README.md | ||
Layla_TAB_V2
Layla_TAB_V2 是业务无关的任务执行与管理平台:Platform 管理身份、注册信息、任务和文件元数据,EManager 协调一台执行机器,EWorker 运行具体业务,Controller 提供独立打包的操作界面。Worker 与 Controller 通过不可变的 Spec 共享输入、输出与版本契约。
- 仓库:Cloverdo/Layla-TAB-V2
- 开发分支:
dev;主分支:main。 - Go 基准版本:1.26.8。开发、测试、发布构建和 Docker Go 构建阶段统一使用此版本。
- 当前测试主机:
10.10.10.102;项目工作目录:/opt/agents/Layla_TAB_V2。
本文的命令以 Linux Bash 为例。测试主机上的源码、构建产物、临时文件和原生运行目录均放在上述项目目录内。需要到其他路径开展开发、测试或部署工作时,先向项目负责人汇报;Docker 部署例外。子目录文档中的 /opt/Emanager 是历史部署示例,不能直接作为本测试环境的目标路径。账号、令牌、数据库密码和 Manager Key 通过未跟踪的配置或运行环境提供。
目录
- 主要功能
- 架构与执行流程
- 仓库结构
- 开发环境与 Go 版本
- 启动 Platform
- 配置 EManager
- 安装 Worker 与 Controller
- 构建与测试
- 协议与扩展开发
- 运维与排错
- 分支与发布流程
- 相关文档
主要功能
Platform 提供本地账号登录、可配置的 OIDC 登录、Manager 注册、Worker 管理、Spec 注册与版本校验、Controller ZIP 安装与启用、任务状态及历史查询、文件预览与下载、S3 配置和运行遥测。管理界面支持中英文显示。
已包含以下独立业务扩展;源码存在不代表运行环境、模型和工具已经安装。
| 扩展 | Spec | 输入与输出 | 运行依赖 |
|---|---|---|---|
| MarkItDown | MarkItDown 1.0 |
文档转换为 Markdown | Python、MarkItDown 及所选文档格式依赖 |
| CrispASR | CrispASR 1.0 |
上传音频或公共 URL 转录为 Markdown | CUDA 版 CrispASR、Qwen3-ASR 模型、独立媒体工具 |
| IndexTTS2 | IndexTTS2 1.0 |
文本与授权的参考音频生成 WAV | NVIDIA CUDA、IndexTTS2 运行时和模型 |
| ComfyUI | ComfyUI 2.0 |
文本生成一个或多个 PNG 图像 | NVIDIA CUDA、ComfyUI、FLUX.2 模型及匹配的文本编码器 |
| AudioExtractor | AudioExtractor 1.0 |
上传视频或 Bilibili/YouTube URL 提取音频片段 | CPU、Worker 私有的 ffmpeg、ffprobe、yt-dlp |
AudioExtractor 默认上传上限为 100 MiB,输出 24 kHz、单声道、16 位 PCM WAV。时间范围要求 0 <= start_seconds < end_seconds <= 86400;结束时间超出媒体长度时截断,开始时间达到或超出媒体长度时失败。CrispASR 与 IndexTTS2 的任务推理使用子进程,在任务结束或取消后释放推理进程的资源;GPU 扩展不能用 CPU 假装完成 GPU 验收。
架构与执行流程
flowchart LR
Browser[浏览器 / 管理界面] --> Platform[Platform / Go API]
Controller[Controller / 沙箱 iframe] --> SDK[Controller Host SDK]
SDK --> Platform
Platform --> PostgreSQL[(PostgreSQL / 核心数据)]
Platform --> S3[(S3 / 上传与产物)]
Manager[EManager / 原生进程] -->|主动心跳、领取任务、回报状态| Platform
Manager -->|本机 Runtime Protocol| Worker[EWorker / 独立进程]
Manager --> Workspace[任务工作区]
Worker --> Workspace
组件职责
| 组件 | 职责 | 数据与连接边界 |
|---|---|---|
| Platform | 登录、注册、Spec 校验、调度、任务历史、Artifact 与素材库元数据 | PostgreSQL;持有 S3 凭据;不执行业务任务、不主动连接 Manager |
| EManager | Worker 同步、任务领取、工作区管理、文件搬运、状态与遥测上报 | 本地 SQLite;持有 Manager Key;主动连接 Platform |
| EWorker | 解释 Spec 的业务输入、运行适配器、产生声明的输出 | 只连接本机 Manager;不持有 Manager Key、Platform 登录或 S3 凭据 |
| Controller | 业务表单、上传、提交任务、进度展示、预览与下载 | 沙箱 iframe;只使用公共 Host SDK |
| Spec | 约定业务输入、输出、Artifact 角色与版本 | 同名同版本不可变,使用规范化 JSON 的 SHA-256 标识 |
一次任务如何执行
- Platform 管理员注册 Manager 并发放 Key,创建 Spec 家族与 Worker 分配。
- EManager 主动连接 Platform;Worker 通过本机 Runtime Protocol 报告运行版本和支持的 Spec。
- Platform 校验 Worker/Controller 的 Spec 定义;双方的活动版本和规范化摘要必须一致。
- Controller 经 Host SDK 上传输入并创建面向具体 Worker 的任务,任务冻结 Spec 名称、版本和摘要。
- EManager 领取任务,把输入 Artifact 下载到该任务的工作区,然后交给 Worker。
- Worker 回报进度,将正式输出写到工作区的
output/,返回相对路径和结果元数据。 - EManager 上传输出并回报终态,Platform 保存历史,Controller 经平台接口预览或下载产物。
主要任务状态为 pending、claimed、running、succeeded、failed、cancelled、interrupted。当前调度对同一 Manager 的 claimed/running 任务进行串行约束;多个 Worker 注册在同一 Manager 下不意味着它们会同时执行任务。业务失败不会自动重试;再次提交形成新的任务。
存储边界
- PostgreSQL 是 Platform 的必需依赖,保存账号、配置、Spec、Manager、Worker、Controller、任务与 Artifact 元数据。缺少
LAYLA_DATABASE_URL或数据库不可达时,Platform 启动失败,不回退到 SQLite。 - SQLite 只用于 EManager 本地状态,不能替代 Platform 的数据库。
- S3 保存用户上传、素材文件和 Worker 输出。没有配置 S3 时,平台仍可管理注册信息与核心配置,依赖文件传输的业务需要可用的存储。
- Controller 的安装包与静态资源还需要 Platform 的本地数据目录;容器运行时应持久化该目录。
- 清理或更换 S3 不会删除数据库中的历史,但可能导致历史文件不可访问。备份必须分别覆盖数据库、Platform 数据目录与对象存储。
仓库结构
Layla-TAB-V2/
├── README.md
├── go.work # 三个 Go 模块的开发工作区
├── Platform/
│ ├── cmd/layla-platform/ # Platform 入口
│ ├── internal/platform/ # API、核心逻辑、数据库、Controller、存储
│ ├── pkg/platformprotocol/v1/ # Spec 解析与规范化摘要
│ ├── protocol/ # Platform / Spec 的机器可读契约
│ ├── web/ # React + TypeScript 管理界面
│ ├── tests/ # 契约测试及浏览器验收
│ ├── docs/
│ └── Dockerfile
├── EManager/
│ ├── cmd/layla-manager/
│ ├── internal/manager/ # 调度、Runtime、SQLite、工作区、遥测
│ ├── sdk/go/platformclient/ # 公共 Platform Protocol 客户端
│ ├── Release/ # 已跟踪的历史原生发行产物
│ ├── systemd/ # 原生服务模板
│ ├── .env.example
│ └── Dockerfile.test # 测试辅助镜像,非原生部署替代品
├── EWorker/
│ ├── protocol/ # Manager / Worker Runtime Protocol
│ ├── sdk/go/worker/
│ ├── sdk/python/
│ ├── MarkItDown/
│ ├── CrispASR/
│ ├── IndexTTS2/
│ ├── ComfyUI/
│ └── AudioExtractor/
├── Controller/
│ ├── sdk/typescript/ # Controller Host SDK
│ ├── protocol/controller/v1/
│ └── <功能名称>/ # manifest、Spec、UI、打包脚本
└── docs/guides/
└── NEW_FUNCTION_WORKER_CONTROLLER_STANDARD.md
每个可部署 Go 组件拥有自己的 go.mod。node_modules/、dist/、.venv/、本地数据库、.env 和 .acceptance/ 等开发文件不提交。仓库内已有部分 Controller ZIP 和 EManager 二进制;升级工具链后,应重新构建需要发布的产物,不能假定历史二进制已由 Go 1.26.8 编译。
开发环境与 Go 版本
| 工具 | 项目要求 |
|---|---|
| Go | 1.26.8;go.work、三个 go.mod 与 Go Docker 构建镜像统一对齐 |
| Node.js | Docker 前端使用 Node 22;测试机已验证 Node 24.21.0,依赖安装以锁文件为准 |
| npm / pnpm | package-lock.json 使用 npm;pnpm-lock.yaml 使用 pnpm |
| Python | SDK/多数适配器要求 Python >= 3.10;IndexTTS2 要求 >= 3.10 且 < 3.12,统一适配层测试可选择 Python 3.11 |
| PostgreSQL | Platform 必需;以下示例和基础测试使用 PostgreSQL 17 |
| Docker Compose | 支持 docker compose 与示例中的依赖健康/完成条件 |
| NVIDIA CUDA | CrispASR、IndexTTS2、ComfyUI 的真实推理需要 |
在测试机进入项目目录并确认工具链:
cd /opt/agents/Layla_TAB_V2
go version
# 预期:go version go1.26.8 linux/amd64
export GOTOOLCHAIN=local
test "$(go env GOVERSION)" = go1.26.8
# 将编译缓存、模块缓存和临时文件限制在项目内。
mkdir -p .acceptance/cache/go-build .acceptance/cache/go-mod \
.acceptance/cache/go-path .acceptance/tmp
export GOCACHE="$PWD/.acceptance/cache/go-build"
export GOMODCACHE="$PWD/.acceptance/cache/go-mod"
export GOPATH="$PWD/.acceptance/cache/go-path"
export TMPDIR="$PWD/.acceptance/tmp"
export npm_config_cache="$PWD/.acceptance/cache/npm"
export PIP_CACHE_DIR="$PWD/.acceptance/cache/pip"
go 1.26.8 声明了模块最低工具链要求,不能阻止手动使用更新版本;上面的版本检查和 Docker 的 golang:1.26.8 标签用于保持实际构建基准一致。版本变更应同时更新工作区、三个模块和全部 Go 构建镜像。
启动 Platform
Docker Compose 最小环境
以下是可保存为项目根目录 compose.local.yml 的示例,仓库不预置该文件。示例针对 Linux 测试主机,镜像构建阶段使用宿主网络以绕过当前容器构建网络的 DNS 问题;应用运行仍使用 Compose 网络。它提供 PostgreSQL、Platform 数据卷初始化和 Platform;不包含 S3、Manager、Worker 或模型。
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: layla
POSTGRES_USER: layla
POSTGRES_PASSWORD: ${LAYLA_DB_PASSWORD:?set LAYLA_DB_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U layla -d layla"]
interval: 5s
timeout: 3s
retries: 20
restart: unless-stopped
platform-data-init:
image: postgres:17-alpine
command: ["chown", "65532:65532", "/data"]
volumes:
- platform-data:/data
restart: "no"
platform:
image: cloverdo/layla-tab:dev
build:
context: ./Platform
dockerfile: Dockerfile
network: host
environment:
LAYLA_DATABASE_URL: postgres://layla:${LAYLA_DB_PASSWORD:?set LAYLA_DB_PASSWORD}@postgres:5432/layla?sslmode=disable
LAYLA_ADMIN_USERNAME: ${LAYLA_ADMIN_USERNAME:-admin}
LAYLA_ADMIN_PASSWORD: ${LAYLA_ADMIN_PASSWORD:?set LAYLA_ADMIN_PASSWORD}
LAYLA_LISTEN_ADDRESS: :8080
LAYLA_DATA_DIR: /tmp/layla-data
LAYLA_SECURE_COOKIES: "false"
ports:
- "${LAYLA_HTTP_PORT:-8080}:8080"
volumes:
- platform-data:/tmp/layla-data
depends_on:
postgres:
condition: service_healthy
platform-data-init:
condition: service_completed_successfully
restart: unless-stopped
volumes:
postgres-data:
platform-data:
Platform 镜像以 UID/GID 65532 运行,初始化服务为新数据卷设置写权限。以下 .env 是占位示例;数据库密码使用随机字母数字串,若包含 URI 保留字符,需要对连接串中的密码进行 URL 编码。
LAYLA_DB_PASSWORD=replace-with-random-alphanumeric-value
LAYLA_ADMIN_USERNAME=admin
LAYLA_ADMIN_PASSWORD=replace-with-a-strong-password
LAYLA_HTTP_PORT=8080
cd /opt/agents/Layla_TAB_V2
chmod 600 .env
docker compose -p layla-tab-v2 -f compose.local.yml config --quiet
docker compose -p layla-tab-v2 -f compose.local.yml up -d --build
curl -fsS http://127.0.0.1:8080/healthz
curl -fsS http://127.0.0.1:8080/readyz
然后访问 http://10.10.10.102:8080 并使用自己配置的管理员账号。示例端口需要空闲;更改 LAYLA_HTTP_PORT 时同时更改访问和探测地址。示例 sslmode=disable 与非 Secure Cookie 面向隔离测试环境,公网部署应使用 HTTPS 并按实际数据库连接配置 TLS。数据卷由 Compose 管理,down 与 down -v 的数据保留效果不同。
原生源码运行与前端开发
原生启动适用于源码调试;正式 Platform 部署使用 Docker。先准备独立 PostgreSQL 和连接串,再运行:
cd /opt/agents/Layla_TAB_V2/Platform/web
npm ci --no-audit --no-fund
npm run build
cd /opt/agents/Layla_TAB_V2/Platform
export LAYLA_DATABASE_URL='postgres://USER:URL_ENCODED_PASSWORD@HOST:5432/DATABASE?sslmode=disable'
export LAYLA_ADMIN_USERNAME='admin'
export LAYLA_ADMIN_PASSWORD='replace-with-a-strong-password'
export LAYLA_DATA_DIR='/opt/agents/Layla_TAB_V2/.acceptance/runtime/platform-data'
go run ./cmd/layla-platform
Platform 本身不会自动读取 .env;通过 shell、Compose 或进程管理器注入环境变量。前端开发可在另一终端执行 npm run dev -- --host 127.0.0.1;Vite 仅为 /api 和 /healthz 配置了到 127.0.0.1:8080 的代理。完整 Controller iframe 和静态资源链路应通过 Platform 提供的生产构建验证,不能仅凭 Vite 页面加载成功判定端到端可用。
Platform 环境变量
| 变量 | 默认值 / 要求 | 用途 |
|---|---|---|
LAYLA_DATABASE_URL |
必填 | PostgreSQL 连接 URI,启动时执行建表/迁移 |
LAYLA_LISTEN_ADDRESS |
:8080 |
HTTP 监听地址 |
LAYLA_ADMIN_USERNAME |
admin |
初始化本地管理员 |
LAYLA_ADMIN_PASSWORD |
change-me |
初始化密码;部署时必须显式替换 |
LAYLA_DATA_DIR |
原生 data;Docker /tmp/layla-data |
Controller 包和静态资源等本地数据 |
LAYLA_WEB_DIR |
原生 web/dist;Docker /app/web |
管理界面构建产物 |
LAYLA_PROTOCOL_DIR |
原生 protocol/platform/v1;Docker /app/protocol/platform/v1 |
协议文档目录 |
LAYLA_SECURE_COOKIES |
false |
HTTPS 部署设置为 true |
LAYLA_PRESIGN_TTL_SECONDS |
86400 |
预签名操作有效期,秒 |
LAYLA_S3_ENDPOINT |
空,禁用文件存储 | S3 兼容服务地址 |
LAYLA_S3_REGION |
us-east-1 |
S3 Region |
LAYLA_S3_BUCKET |
配置 S3 时提供 | Bucket 名称 |
LAYLA_S3_ACCESS_KEY / LAYLA_S3_SECRET_KEY |
配置 S3 时提供 | 仅由 Platform 持有 |
LAYLA_S3_PATH_STYLE |
true |
S3 路径形式 |
LAYLA_S3_OBJECT_PREFIX |
layla |
对象键前缀 |
已有本地账号不会因为重启时更改初始化变量而被重置,密码变更通过账号功能完成。S3 也可在管理界面配置;数据库已保存的存储配置优先于启动环境中的 S3 配置。OIDC 配置通过平台认证设置保存,不存在本文未列出的 OIDC 启动环境变量约定。
配置 EManager
一个执行机器运行原生 EManager,其 Worker 是独立进程。测试机示例目录:
/opt/agents/Layla_TAB_V2/.acceptance/runtime/executor/
├── layla-emanager
├── .env
├── data/manager.db
├── workspaces/<task-id>/
├── run/
└── workers/<worker-name>/
编译与启动
cd /opt/agents/Layla_TAB_V2
EXECUTOR_ROOT="$PWD/.acceptance/runtime/executor"
mkdir -p "$EXECUTOR_ROOT"/{data,workspaces,run,workers}
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-trimpath -ldflags='-s -w' \
-o "$EXECUTOR_ROOT/layla-emanager" ./EManager/cmd/layla-manager
cp EManager/.env.example "$EXECUTOR_ROOT/.env"
chmod 600 "$EXECUTOR_ROOT/.env"
先在 Platform 注册 Manager,取得其发放的 Key。编辑示例 .env,将所有数据路径改为本项目目录:
LAYLA_PLATFORM_URL=http://127.0.0.1:8080
LAYLA_MANAGER_KEY=replace-with-issued-manager-key
LAYLA_MANAGER_DATA_DIR=/opt/agents/Layla_TAB_V2/.acceptance/runtime/executor/data
LAYLA_MANAGER_SQLITE_PATH=/opt/agents/Layla_TAB_V2/.acceptance/runtime/executor/data/manager.db
LAYLA_MANAGER_WORKSPACE_DIR=/opt/agents/Layla_TAB_V2/.acceptance/runtime/executor/workspaces
LAYLA_WORKER_LISTEN_ADDRESS=127.0.0.1:9090
LAYLA_MANAGER_HEALTH_ADDRESS=127.0.0.1:9091
LAYLA_HEARTBEAT_INTERVAL_SECONDS=15
LAYLA_WORKER_SYNC_INTERVAL_SECONDS=15
LAYLA_TASK_POLL_INTERVAL_SECONDS=2
LAYLA_PLATFORM_REQUEST_TIMEOUT_SECONDS=30
cd /opt/agents/Layla_TAB_V2/.acceptance/runtime/executor
./layla-emanager
# 另开终端检查:
curl -fsS http://127.0.0.1:9091/healthz
curl -fsS http://127.0.0.1:9091/readyz
Manager 自动读取当前工作目录或可执行文件所在目录的 .env;显式指定 LAYLA_MANAGER_ENV_FILE 可选择其他配置文件。已注入的环境变量优先于文件值。LAYLA_PLATFORM_URL 与 LAYLA_MANAGER_KEY 必填,Worker Runtime 必须监听 loopback 地址。
/healthz 表示进程存活;Platform 不可达或 Key 不被接受时,Manager 仍可启动,但 /readyz 返回 503,直到成功完成心跳。Platform 拒绝未知 Key 时返回 401 INVALID_MANAGER_KEY,不会因此创建 Manager 注册记录。
长期运行可参考 EManager/systemd/ 模板,调整工作目录、环境文件、执行路径和 ReadWritePaths。模板中的旧路径与运行账号需要按当前环境修改;安装到项目外的 systemd 目录前先汇报。
安装 Worker 与 Controller
注册顺序
- 在 Platform 创建 Manager,启动已配置的 EManager。
- 创建对应 Spec 家族,并在 Manager 下创建 Worker 分配,取得 Worker ID。
- 安装该 Worker 的独立运行环境、工具/模型,配置 Worker ID 与本机 Manager 地址。
- 启动 Worker,检查在线状态、运行版本和 Spec 接受状态。
- 构建对应 Controller ZIP,在平台安装、检查校验结果并启用匹配的 Spec 版本。
- 从 Functions 进入 Controller,提交真实输入,验证进度、取消、输出预览与下载。
Worker 默认业务并发为一;当前同 Manager 调度还受机器级串行约束。Worker/Controller 活动 Spec 不匹配、Worker 离线、Spec 被禁用或文件存储不可用时,应先解决对应状态,再提交业务任务。
MarkItDown 原生安装示例
下面只安装 SDK 和 MarkItDown;其他扩展还需依照其专属 README 准备第三方运行时与模型。
cd /opt/agents/Layla_TAB_V2
WORKER_ROOT="$PWD/.acceptance/runtime/executor/workers/markitdown"
mkdir -p "$WORKER_ROOT"
python3 -m venv "$WORKER_ROOT/venv"
"$WORKER_ROOT/venv/bin/python" -m pip install \
./EWorker/sdk/python ./EWorker/MarkItDown
cp EWorker/MarkItDown/.env.example "$WORKER_ROOT/.env"
chmod 600 "$WORKER_ROOT/.env"
# 编辑 .env,替换 Worker ID。Worker 不自动读取该文件,启动时显式加载:
cd "$WORKER_ROOT"
set -a
. ./.env
set +a
./venv/bin/layla-markitdown-worker
AudioExtractor 必须在自己的 AUDIO_EXTRACTOR_ROOT/tools/ 下拥有可执行的 ffmpeg、ffprobe、yt-dlp,不能复用另一 Worker 的工具目录。CrispASR、IndexTTS2 和 ComfyUI 的源代码适配层不包含完整上游项目和模型;资源布局、模型校验和 GPU 条件见各组件文档。
Controller 构建与 ZIP
| Controller | 安装命令(进入对应目录后) | ZIP |
|---|---|---|
| MarkItDown | npm ci |
markitdown-controller-1.0.0.zip |
| CrispASR | npm ci |
crispasr-controller-1.0.0.zip |
| IndexTTS2 | pnpm install --frozen-lockfile |
indextts2-controller-1.0.0.zip |
| ComfyUI | pnpm install --frozen-lockfile |
comfyui-controller-2.0.0.zip |
| AudioExtractor | pnpm install --frozen-lockfile |
audio-extractor-controller-1.0.0.zip |
统一构建与打包方式:
cd /opt/agents/Layla_TAB_V2/Controller/MarkItDown
npm ci --no-audit --no-fund
npm run build
python3 scripts/package.py
使用 pnpm 锁文件的组件改用 pnpm run build。AudioExtractor 还应运行 pnpm test。显式执行 python3 scripts/package.py 可避免其 npm run package 使用 python 时,在仅提供 python3 的 Linux 上找不到解释器。ZIP 包包含 controller.json、声明的 Spec 与 dist/;构建输出需要先生成。包内不包含服务器代码、Manager/S3 凭据或外部业务调用能力。
构建与测试
Go 测试与二进制
Platform 数据库测试依赖 LAYLA_TEST_DATABASE_URL,不会在缺少数据库时自动通过或改用 SQLite。使用独立测试数据库,不复用业务库;测试创建隔离 schema,并在正常清理时删除 schema。
cd /opt/agents/Layla_TAB_V2
export GOTOOLCHAIN=local
test "$(go env GOVERSION)" = go1.26.8
export LAYLA_TEST_DATABASE_URL='postgres://TEST_USER:URL_ENCODED_PASSWORD@127.0.0.1:5432/layla_test?sslmode=disable'
go test ./Platform/... ./EManager/... ./EWorker/...
mkdir -p .acceptance/build
CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' \
-o .acceptance/build/layla-platform ./Platform/cmd/layla-platform
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags='-s -w' \
-o .acceptance/build/layla-emanager-linux-amd64 ./EManager/cmd/layla-manager
go version -m .acceptance/build/layla-emanager-linux-amd64
Windows 开发环境可用 PowerShell 设置相同环境变量;原生发布产物的 GOOS/GOARCH 必须与目标执行机匹配。生产发布前重新生成目标二进制及校验和。EManager/Release/ 的历史产物不随 go.mod 自动更新。
Platform 前端与 Docker
cd /opt/agents/Layla_TAB_V2/Platform/web
npm ci --no-audit --no-fund
npm test
npm run build
cd /opt/agents/Layla_TAB_V2
docker build --network=host -t cloverdo/layla-tab:dev ./Platform
# 仅用于 Manager 测试辅助镜像:
docker build --network=host -f EManager/Dockerfile.test -t layla-emanager:test .
Platform 的 Docker 构建上下文是 Platform/;Manager 测试镜像的上下文是仓库根目录。前者包含前端构建与 Go 1.26.8 后端构建,后者不能作为生产原生 Manager 部署方式。
Python SDK 与适配器测试
在项目内准备独立测试 venv,安装 SDK 和需要验证的适配器。以下完整安装组合使用 Python 3.11,以满足 IndexTTS2 的 <3.12 约束;如主机没有该解释器,先确认安装方案,不直接修改项目外系统环境。MarkItDown 的转换测试依赖真实 MarkItDown 包,CrispASR/IndexTTS2 的导入依赖也需按其项目声明安装。
cd /opt/agents/Layla_TAB_V2
python3.11 -m venv .acceptance/test-venv
.acceptance/test-venv/bin/python -m pip install \
./EWorker/sdk/python ./EWorker/MarkItDown ./EWorker/CrispASR \
./EWorker/IndexTTS2 ./EWorker/ComfyUI ./EWorker/AudioExtractor
for suite in sdk/python MarkItDown CrispASR IndexTTS2 ComfyUI AudioExtractor; do
.acceptance/test-venv/bin/python -m unittest discover \
-s "EWorker/$suite/tests" -v || exit 1
done
各目录内的测试模块存在同名文件,独立进程运行每个 suite 可避免 unittest 导入冲突。适配层使用假引擎的单元测试通过,不代表上游模型或真实 GPU 推理已经验证。
文件存储、媒体与浏览器集成
| 测试 | 需要的环境 |
|---|---|
| S3 预签名往返 | LAYLA_TEST_S3_ENDPOINT、LAYLA_TEST_S3_BUCKET、LAYLA_TEST_S3_ACCESS_KEY、LAYLA_TEST_S3_SECRET_KEY |
| AudioExtractor 本地媒体 | AUDIO_EXTRACTOR_TEST_TOOLS 指向含 ffmpeg、ffprobe、yt-dlp 的独立目录 |
| AudioExtractor 公共 URL | 另配置 AUDIO_EXTRACTOR_TEST_URL |
| 浏览器路由与业务链路 | 已运行的 Platform、测试账号、Playwright 浏览器;业务测试还需 Worker 和 S3 |
未配置 S3 或真实媒体条件时,相应集成测试会跳过。浏览器脚本可能修改平台配置,应使用隔离的测试部署并阅读脚本要求:
cd /opt/agents/Layla_TAB_V2/Platform/tests/acceptance/e2e
npm ci --no-audit --no-fund
export PLAYWRIGHT_BROWSERS_PATH='/opt/agents/Layla_TAB_V2/.acceptance/playwright-browsers'
npx playwright install chromium
export LAYLA_PLATFORM_URL='http://127.0.0.1:8080'
export LAYLA_PLATFORM_USERNAME='your-test-user'
export LAYLA_PLATFORM_PASSWORD='your-test-password'
npm run platform:routes
已知测试限制
2026-10-07 在 Go 1.26.8 上的既有基线中,TestHealthLoginAndManagerContract 使用固定 collected_at=2026-08-24T12:00:00Z,而遥测接收接口会清理超过 30 天的非任务样本。因此当前日期执行该测试会在遥测查询断言处失败。此 README 与工具链变更不修改该业务测试;修复时应使测试时间与保留策略一致,不能把失败报告为已通过。
协议与扩展开发
| 契约 | 源码 / 文档 |
|---|---|
| Platform Protocol v1 | Platform/protocol/platform/v1/openapi.yaml |
| Spec Protocol v1 | Platform/protocol/spec/v1/spec-protocol.schema.json |
| Manager / Worker Runtime v1 | EWorker/protocol/schema/v1/message.schema.json 与 EWorker/protocol/go/v1/ |
| Controller Host SDK v1 | Controller/protocol/controller/v1/ 与 Controller/sdk/typescript/ |
| Go Manager 客户端 | EManager/sdk/go/platformclient/ |
| Worker SDK | EWorker/sdk/go/worker/ 与 EWorker/sdk/python/ |
新增功能应先阅读统一开发标准,然后依照 Worker 开发合约和 Controller 开发合约开展工作。
- 明确功能输入、输出、Artifact 角色、运行依赖和验收要求。
- 先定义 Spec;同名同版本的规范化摘要不可变化,破坏兼容性的修改发布新版本。
- 实现独立 Worker,并通过 SDK 报告运行版本、支持的 Spec、任务进度与终态。
- 实现独立 Controller,通过 Host SDK 访问上下文、上传、素材库、任务与产物。
- 校验两端 Spec,测试异常输入、路径边界、取消、重连、版本不匹配及真实业务链路。
- 打包、注册并启用扩展,确认对既有功能没有回归。
Platform/EManager 不增加功能名称专用分支。业务需要核心特判才能运行时,应先确认公共协议或 SDK 的缺口。Worker 只读任务声明的输入,在任务 output/ 下生成正式输出;第三方源码、模型和大型依赖不作为 Layla 自有源码提交。Controller 运行在沙箱内,不绕过 Host SDK 直连 Manager、Worker 或 S3。
运维与排错
| 现象 | 优先检查 |
|---|---|
| Platform 启动失败 | LAYLA_DATABASE_URL、数据库连通性、建表权限、本地数据目录写权限 |
Platform /readyz 返回 storage_ready:false |
S3 配置、Bucket/权限和可达性;这与数据库就绪是独立状态 |
Manager /healthz 正常、/readyz 为 503 |
Platform URL、网络、已发放且未删除的 Manager Key |
INVALID_MANAGER_KEY |
使用平台发放的 Key,不把 Worker ID 或登录密码当成 Key |
| Worker 离线或被隔离 | Worker ID、loopback Runtime 地址、Spec 上报结果、独立依赖与进程日志 |
任务保持 pending |
Worker/Spec 启用状态、Worker 在线和摘要匹配、同 Manager 是否已有任务 |
| Controller 不显示或无法提交 | ZIP 校验、活动 Spec、兼容 Worker、SDK 握手及浏览器日志 |
| 预览/下载失败 | Artifact 状态、对象是否仍存在、S3 连接、代理路径及支持的内容类型 |
| GPU 扩展启动失败 | 驱动、CUDA、指定模型与路径,按真实 GPU 条件验收 |
| Docker 构建下载依赖时报 DNS / network unreachable | Linux 测试机使用 docker build --network=host 或 Compose build.network: host;仅改变构建网络,不修改系统 DNS |
| Python venv 创建失败 | 是否安装了匹配解释器的 venv/ensurepip 支持;需修改项目外系统软件时先汇报 |
Platform 输出 JSON 日志,可通过 Compose 查看:
cd /opt/agents/Layla_TAB_V2
docker compose -p layla-tab-v2 -f compose.local.yml logs --tail=100 platform
Manager/Worker 的日志查看方式取决于实际进程管理器。对失败请求记录 X-Request-ID、任务 ID、Worker ID 和错误码,便于关联日志;避免把令牌、完整签名 URL 或用户文件内容写入公开问题报告。
非任务遥测当前保留 30 天;任务相关样本随任务历史保留,不受这条非任务样本清理规则影响。文件与历史清理应分别评估数据库、对象存储和工作区,不能用删除 S3 来重置平台核心注册与任务历史。
分支与发布流程
dev用于日常开发与集成;main接收确认要合入的 dev 状态。- 提交前检查差异、Go 版本和与改动对应的构建/测试,清楚记录未通过或跳过的验证。
- 合并 dev 到 main 会包含 dev 尚未进入 main 的全部提交,应检查提交范围。
- 分支推送不等于应用部署。构建镜像、推送镜像、更新容器及原生发行产物是独立发布步骤。
- 不通过强制推送覆盖未知的远程改动;使用正常合并或快进,并核对远程分支最终提交。
- 不提交
.env、Forgejo Token、Manager Key、S3 密钥、测试数据库或本地验收凭据。
cd /opt/agents/Layla_TAB_V2
git fetch origin
git switch dev
git status --short
git log --oneline origin/main..dev
# 完成必要验证并提交后:
git push origin dev
git switch main
git merge --ff-only dev # 仅适用于 main 是 dev 祖先的情况
git push origin main
git switch dev
单分支克隆可能没有 origin/main,需要先为 main 配置远程跟踪映射(该配置只添加一次),再获取分支:
git config --add remote.origin.fetch '+refs/heads/main:refs/remotes/origin/main'
git fetch origin refs/heads/main:refs/remotes/origin/main
# 如本地尚无 main:
git branch --track main origin/main
相关文档
- 架构约束
- 新功能统一开发标准
- EManager 部署说明 / 原生发行产物
- MarkItDown Worker
- CrispASR Worker / Controller
- IndexTTS2 Worker / Controller
- ComfyUI Worker / Controller
- AudioExtractor Worker / Controller
- Worker 开发合约 / Controller 开发合约
阶段验收报告记录的是当时的工具链、部署目录和环境结果;当前工作以此 README、实际源码和项目负责人最新指示为准。