集中式离线语音识别(ASR)任务管理系统。对接 FunASR 服务器集群,提供文件上传、智能调度、实时进度追踪和多格式结果下载。
三种使用方式:CLI 命令行 · REST API · Web UI
核心能力:
- 多服务器智能调度 — LPT + 最早完工时间预规划,按真实可用 slot 补位,空闲节点自动工作窃取
- 长音频 VAD 分段并行 — 超过阈值的音频自动切分,分发到多台服务器并行转写,结果按时间戳合并
- AI Agent 原生支持 — 10 个配套 Skills,让 AI Agent(OpenClaw / Hermes / Cursor 等)开箱即用地完成聊天渠道转写、服务器本地批量转写和急停恢复全流程
├── 人类用户
│ ├── 转写 1 个文件 → python -m cli transcribe audio.mp4
│ ├── 转写多个文件(自动并行) → python -m cli transcribe *.wav --format txt
│ ├── 只提交不等待 → python -m cli transcribe files --no-wait
│ ├── 查看批次进度 → python -m cli task list --group <group_id>
│ ├── 下载批次结果 → python -m cli task result --group <group_id> --format txt,srt
│ ├── 管理 ASR 服务器 → python -m cli server list / probe / benchmark
│ ├── 发送渠道实时通知 → python -m cli notify send --text "..." --chat-id <chat_id>
│ ├── 排查系统问题 → python -m cli doctor
│ └── API 集成开发 → 阅读下方 API 参考
│
└── AI Agent
├── 环境初始化 → init Skill(自动检测、安装、配置)
├── 用户发送音视频 → 自动转写 → channel-intake + result-delivery Skills
├── 安装 Skills 到 Agent 平台 → init Phase 6
└── 详见下方「Agent Skills 体系」
注:
pip install -e .后可使用asr-cli替代python -m cli(二者等价)。开发环境推荐python -m cli,生产环境推荐asr-cli。
- Python 3.11+
- Node.js 20+(前端可选)
- ffmpeg / ffprobe(推荐安装:用于精确提取音频时长和本地 WAV 预处理;未安装时系统会按文件大小粗估时长,并直传原始文件给 FunASR 服务器解码)
cd 3-dev/src/backend
# 安装依赖
pip install -e ".[dev]"
# 数据库迁移
alembic upgrade head
# 启动后端
uvicorn app.main:app --host 0.0.0.0 --port 15797 --reload工作目录说明 后端默认把数据库、上传结果和临时文件写到仓库根目录的
runtime/storage/,日志与 PID 文件写到runtime/logs/。这些默认路径已经与当前工作目录解耦,不会再因为从不同目录启动而把持久化数据写进3-dev/src/或仓库根目录的历史data/目录。
也可使用一键启停脚本(推荐开发环境):
# Windows PowerShell
cd 3-dev\src
.\start.ps1 # 启动前后端
.\start.ps1 -NoFrontend # 仅后端
.\stop.ps1 # 停止所有# Linux / macOS
cd 3-dev/src
bash start.sh # 启动前后端
bash start.sh --no-frontend # 仅后端
bash stop.sh # 停止所有cd 3-dev/src/backend
# 上传并转写一个文件(自动上传→创建任务→等待→下载结果)
python -m cli transcribe recording.mp4 --language zh --format txt
# 默认会把 CLI 下载副本写到仓库根目录 runtime/storage/downloads/
# 指定输出目录
python -m cli transcribe meeting.wav --format srt --output-dir ./runtime/storage/downloads/manual/本项目为 AI Agent 提供了 10 个配套 Skills,安装到 Agent 平台(OpenClaw / Hermes / Cursor 等)后,Agent 可自主完成从环境检测到转写交付和急停恢复的全流程。
Agent 体系有两条主要入口:
- 聊天渠道实时转写:用户在飞书/企微/Slack 等渠道发送音视频文件,Agent 自动识别意图、下载文件、预检查、提交后端转写、监控状态,并以文件附件回复结果。
- 服务器本地批量转写:用户要求扫描服务器本地目录或 inbox,Agent 自动建清单、批量提交、持续播报进度、归档结果并处理失败重试。
所有用户可见的阶段进度都必须走统一实时通知原语 send_user_notice():OpenClaw/Hermes 环境优先调用平台 message tool;没有可用 message tool 时才 fallback 到 CLI python -m cli notify ...。普通 assistant 文本只用于最终总结或纯本地终端场景,不能当作聊天渠道实时进度通知。
| Skill | 职责 | 触发时机 |
|---|---|---|
| init | 环境检测、依赖安装、后端启动、Skill 安装、渠道凭据配置、systemd 服务注册 | 首次部署或环境异常时 |
| channel-intake | 意图识别、渠道文件下载(飞书/企微/Slack)、预检查、任务提交 | 用户发送音视频文件时 |
| local-batch-transcribe | 扫描本地目录、生成清单、分 chunk 提交、委托子 Agent 监控、失败重试 | 用户要求批量转写本地目录/inbox 时 |
| batch-monitor | 子 Agent 专用:绑定 task_group_id,定期查询进度并通过 message tool 播报 | 主 Agent 提交批量任务后委托 |
| emergency-stop | 诊断 active slot、执行 dry-run 急停、取消活跃任务并释放僵尸 segment | 批次失控、取消后 slot 未释放或需要立即中止转写时 |
| media-preflight | ffprobe 格式验证、时长检测、转码评估 | 文件上传到后端前 |
| result-delivery | 任务状态轮询、结果拉取、质量初筛、格式化通知、文件附件发送 | 转写完成时 |
| server-benchmark | ASR 节点性能校准(单线程 RTF + 梯度并发吞吐量) | 节点注册或定期校准时 |
| reset-test-db | 测试环境数据库重置与 dry-run 评估 | 测试前 |
| web-e2e | 浏览器 E2E 测试流程编排与素材管理 | 发版验证时 |
入口 A:聊天渠道实时转写
用户发送音视频
↓
channel-intake ← 意图识别 + 渠道文件下载 + 任务提交
↓
media-preflight ← ffprobe 预检 + 转码决策
↓
[后端自动调度转写] ← VAD 分段 + 多服务器并行
↓
result-delivery ← 状态轮询 + 质量初筛 + 文件附件回复
入口 B:服务器本地批量转写
用户要求扫描目录/inbox
↓
local-batch-transcribe ← 扫描目录 + manifest + 批量预检
↓
CLI --batch 批量提交
↓
local-batch-transcribe ← 进度监控 + 主动通知 + 结果归档 + 失败重试
↓
result-delivery 格式规范 ← 复用结果摘要和附件交付模板
协作规则:init 是所有入口的前置环境准备;media-preflight 可被两条入口复用;result-delivery 是任务创建后的结果出口;emergency-stop 处理取消/急停后的 slot 恢复;server-benchmark、reset-test-db、web-e2e 分别服务性能校准、测试环境和浏览器验证。聊天渠道任务与本地批量任务并发时,聊天渠道入口优先,本地批量入口暂停新提交并等待恢复。
所有 Skill 中出现“通知用户 / 汇报 / 反馈 / 进度更新”的位置,都必须遵循 渠道实时通知规范。
适配优先级:
- 平台原生
messagetool(OpenClaw / Hermes):{"action": "send", "message": "...", "filePath": "..."} - CLI fallback:
python -m cli notify send --text "..." --chat-id <chat_id>或python -m cli notify send-file --file result.txt --chat-id <chat_id> - 普通 assistant 文本:仅限用户能直接看到实时输出的纯本地终端场景
路由验证:message tool 无法指定投递目标,平台上下文在 session 切换时可能滞后。因此每条通知发出后都会验证返回的 chatId 是否匹配预期目标(群聊精确匹配 chat_id;私聊排除 oc_ 前缀)。验证失败时自动锁定到 CLI notify 并使用显式路由参数重发,确保"从哪来回哪去"。详见 通知原语规范。
notify CLI 当前支持飞书/Lark 文本通知、话题回复、文件附件、token 缓存、soft-fail / --strict 模式。CLI 允许使用 FEISHU_CHAT_ID / notify.default_chat_id 作为本地默认目标;Agent/Skill 工作流必须优先传入本轮上下文里的显式 --chat-id,避免消息发到上一次会话。详见下方 notify — 渠道实时通知(飞书)。
运行 init Skill 的 Phase 6 即可一键安装所有 10 个 Skills 和共享工作流文件到目标平台:
- OpenClaw:
~/.openclaw/workspace-{name}/skills/ - Hermes:
~/.hermes/skills/ - Cursor:
{project}/.cursor/skills/或~/.cursor/skills-cursor/
安装后 Agent 启动时自动加载 ASR 转写能力,无需用户手动指挥。若需要聊天渠道实时进度,还应按 init Phase 7 配置渠道凭据与 notify fallback。OpenClaw 环境下,Phase 7.5 会自动检查 CLI 和 Skill 版本一致性,确保部署环境与源仓库同步。详见 init Skill、渠道凭据配置 和 部署同步指南。
| 服务 | 默认端口 | 说明 |
|---|---|---|
| 后端 API | 15797 | Agent 和 CLI 通过此端口调用转写 API |
| 前端 Web UI | 15798 | 人类操作员使用,Agent 不依赖 |
| 预留 | 15799 | 未来扩展 |
后端推荐注册为 systemd 用户级服务(systemctl --user),无需 sudo,Agent 可直接管理服务生命周期。详见 init Skill Phase 8。
多文件时自动启用批量模式,一次性上传并创建所有任务,后端并行调度到多台 ASR 服务器:
# 转写目录下所有 wav 文件(默认下载到 runtime/storage/downloads/)
python -m cli transcribe *.wav --format txt
# 转写指定文件
python -m cli transcribe ep01.mp4 ep02.mp4 ep03.mp4 --format srt --output-dir ./runtime/storage/downloads/srt/
# 强制批量模式(即使只有 1 个文件)
python -m cli transcribe single.wav --batch --format json
# 异步提交,不等待完成
python -m cli transcribe *.mp3 --no-wait --json-summary
# 完成后下载多种格式结果 + 生成摘要
python -m cli task result --group <group_id> --format txt,json,srt说明:服务端原始持久化始终写入仓库根目录 runtime/storage/uploads/ 和 runtime/storage/results/;CLI 的 --output-dir 只是额外下载一份副本,默认放到 runtime/storage/downloads/,避免误写到 3-dev/src/backend/ 当前工作目录。
公开 benchmark 样本统一放在 3-dev/benchmark/samples/(已纳入版本控制),当前固定样本为 test.mp4 和 tv-report-1.wav。
批量下载后的输出目录结构:
runtime/storage/downloads/
├── batch-summary.json # 批次摘要(所有任务状态和输出路径)
├── ep01_result.txt
├── ep01_result.srt
├── ep02_result.txt
├── ep02_result.srt
└── ...
当前版本 V0.5.2(2026-09-11):完成全库代码审计并关闭 BUG-052~087 全部 36 项缺陷(8 项高危 + 28 项中危,其中 1 项复核为审计误判关闭,详见 docs/code-audit-report-20260910.md 与 task-list.md),CLI/后端/前端版本统一为 0.5.2,回归测试从 963 例增至 1125 例。本版本以缺陷修复为主,调度、转写与批量主路径行为不变,稳定性验证结论继承下列 V0.4.28 双轮基准。
V0.4.28 继承 2026-05-16 同一组 45 个本地音视频文件连续两轮批量转写验证结论,并在 2026-05-17 至 2026-05-18 补充调度边界、批量测试独占窗口、monitor 清理规则和 benchmark 互斥防护:
| 批次 | 结果 | 总音频时长 | 墙钟耗时 | 加速比 | 异常 |
|---|---|---|---|---|---|
| 第三轮 | 45/45 成功 | 486.72 分钟 | 426.5 秒 | 68.47x | 0 失败 / 0 重试 / 0 错误标记 |
| 第四轮 | 45/45 成功 | 486.72 分钟 | 424.9 秒 | 68.73x | 0 失败 / 0 重试 / 0 错误标记 |
两轮输出目录分别为 runtime/agent-local-batch/outputs/local-20260516/ 和 runtime/agent-local-batch/outputs/local-20260516-v2/,均生成 45 个结果文件,文件名和内容哈希一致。V0.4.28 额外修复 work-steal 多槽收益计算、虚拟 slot future 任务派发边界和零并发服务器防御,并为本地批量测试增加 runtime/agent-local-batch/locks/ 独占窗口与 runtime/agent-local-batch/monitors/ stale monitor 清理规程;benchmark 侧新增物理 ASR 端点互斥、客户端断开清理和 CLI 流式断连可读错误。该结论表示本地 45 文件批量转写主路径已较稳定;服务下线恢复、故障注入、更多超长文件混合批次仍需单独验证。
一次请求提交多个任务:
# 1. 上传多个文件
curl -X POST http://localhost:15797/api/v1/files/upload -F "file=@ep01.wav"
# → {"file_id": "01JQXXX1..."}
curl -X POST http://localhost:15797/api/v1/files/upload -F "file=@ep02.wav"
# → {"file_id": "01JQXXX2..."}
# 2. 批量创建任务
curl -X POST http://localhost:15797/api/v1/tasks \
-H "Content-Type: application/json" \
-d '{
"items": [
{"file_id": "01JQXXX1...", "language": "zh"},
{"file_id": "01JQXXX2...", "language": "zh"}
]
}'
# 3. 查看批次进度
curl http://localhost:15797/api/v1/task-groups/<group_id>
# 4. 下载结果(zip 打包)
curl -o results.zip http://localhost:15797/api/v1/task-groups/<group_id>/results?format=zipWeb UI 支持拖拽多文件上传和批量任务管理。访问 http://localhost:15798 打开前端界面。
所有命令的工作目录为 3-dev/src/backend,命令前缀为 python -m cli。
| 选项 | 简写 | 环境变量 | 说明 | 默认值 |
|---|---|---|---|---|
--server |
-s |
ASR_API_SERVER |
API 服务地址 | http://localhost:15797 |
--api-key |
-k |
ASR_API_KEY |
API 认证 Token | 无 |
--output |
-o |
ASR_OUTPUT_FORMAT |
输出格式: table/json/text |
table |
--quiet |
-q |
— | 静默模式 | false |
--verbose |
-v |
— | 调试模式 | false |
--timeout |
— | — | HTTP 超时(秒) | 30 |
配置可通过 config set 持久化,优先级:CLI 参数 > 环境变量 > 配置文件 > 默认值。
# 单文件
python -m cli transcribe audio.wav
python -m cli transcribe meeting.wav --quality-profile meeting --format srt
python -m cli transcribe audio.wav --model sensevoice --use-spk --response-format verbose_json
# 批量(自动并行调度到多台服务器,默认下载到 runtime/storage/downloads/)
python -m cli transcribe *.wav --format txt
# 异步提交
python -m cli transcribe *.mp3 --no-wait --json-summary| 选项 | 说明 |
|---|---|
--language, -l |
识别语言,默认 auto |
--hotwords |
热词列表,逗号分隔 |
--model |
模型偏好(如 sensevoice、fun-asr-nano;funasr-server v1.4+ 另支持 moss-transcribe-diarize 联合说话人分离模型) |
--use-spk |
启用说话人分离 |
--response-format |
OpenAI HTTP 响应格式(text/verbose_json) |
--quality-profile |
质量预设:fast/balanced/meeting(meeting 自动启用说话人分离) |
--format, -f |
结果格式: json/txt/srt |
--output-dir, -d |
下载副本目录,默认 runtime/storage/downloads/ |
--save |
单文件时保存到指定路径 |
--no-wait |
只提交不等待 |
--batch |
强制批量模式 |
--download/--no-download |
完成后是否下载 |
--json-summary |
输出 JSON 格式摘要 |
--callback |
完成后回调地址 |
--poll-interval |
轮询间隔(秒),默认 5 |
--timeout |
等待超时(秒),默认 3600 |
--segment-level |
切分策略:off(关闭切分)/ 10m(默认,按时长阈值自动决定)/ 20m / 30m |
# 创建任务
python -m cli task create <file_id1> <file_id2> --language zh
# 创建任务(指定切分策略)
python -m cli task create <file_id> --segment-level 20m
# 查看任务列表
python -m cli task list
python -m cli task list --status SUCCEEDED --page 1
python -m cli task list --group <group_id> # 按批次筛选
# 查看任务详情
python -m cli task info <task_id>
# 下载单个任务结果
python -m cli task result <task_id> --format srt --save output.srt
# 下载整批结果(支持多格式同时导出,默认下载到 runtime/storage/downloads/)
python -m cli task result --group <group_id> --format txt,json,srt
# 等待任务完成
python -m cli task wait <task_id1> <task_id2>
python -m cli task wait --group <group_id> # 等待整批完成
# 取消任务
python -m cli task cancel <task_id>
# 删除批次
python -m cli task delete --group <group_id>
# 实时进度
python -m cli task progress <task_id># 查看所有节点
python -m cli server list
# 注册 FunASR OpenAI HTTP 节点(funasr-server v1.3.2+;v1.4+ 新增 moss-transcribe-diarize 模型与原生说话人标签)
python -m cli server register --id asr-http-01 --host 192.168.1.100 --port 8000 --protocol funasr-openai --benchmark
# 注册 WebSocket 节点
python -m cli server register --id asr-01 --host 192.168.1.100 --port 10095 --protocol v2_new
# 注册并立即执行 benchmark(自动校准 RTF 基线和推荐并发,终端实时显示进度)
python -m cli server register --id asr-01 --host 192.168.1.100 --port 10095 --protocol v2_new --benchmark
# 探测节点连通性和能力
python -m cli server probe asr-01
# 单节点 benchmark(单线程 RTF + 梯度并发吞吐量测试,自动校准 max_concurrency)
python -m cli server benchmark asr-01
# 全量 benchmark(对所有在线节点执行,刷新 RTF 基线与吞吐量指标)
python -m cli server benchmark
# 更新节点配置
python -m cli server update asr-01 --max-concurrency 8
python -m cli server update asr-01 --name "高配服务器" --max-concurrency 12
# 删除节点
python -m cli server delete asr-01探测级别说明:
| 级别 | 说明 |
|---|---|
connect_only |
仅检查 WebSocket / 网络连通性,不做性能测试 |
offline_light |
连通性 + 离线转写能力探测(默认) |
twopass_full |
完整双通道探测 |
probe 只做连通性与能力探测,不参与 benchmark,也不会更新任何 RTF 基线。
notify 用于 Agent 在没有平台原生 message tool 时,通过飞书 API 发送实时进度通知和结果附件。默认 soft-fail:通知失败只输出 warning,不中断主流程;加 --strict 后失败返回 exit 1。
# 检查飞书凭据
python -m cli notify auth-check
# 发送文本
python -m cli notify send --text "⏳ 正在从飞书下载文件..." --chat-id oc_xxxxxxxx
# 从文件或 stdin 发送多行文本
python -m cli notify send --text-file /tmp/notice.txt --chat-id oc_xxxxxxxx
echo "批量转写进度:35/50 已完成" | python -m cli notify send --stdin --chat-id oc_xxxxxxxx
# 发送结果附件
python -m cli notify send-file --file /tmp/result.txt --filename "会议记录.txt" --chat-id oc_xxxxxxxx
# 回复到指定飞书消息线程
python -m cli notify send --text "处理中..." --chat-id oc_xxxxxxxx --reply-to om_xxxxxxxx
# 向用户私聊发送(使用 open_id)
python -m cli notify send --text "转写已完成" --receive-id-type open_id --chat-id ou_xxxxxxxx配置来源优先级:环境变量 > CLI 配置文件。
| 配置 | 环境变量 | CLI 配置键 |
|---|---|---|
| 飞书 App ID | FEISHU_APP_ID |
notify.feishu_app_id |
| 飞书 App Secret | FEISHU_APP_SECRET |
notify.feishu_app_secret |
| 默认群聊 ID | FEISHU_CHAT_ID |
notify.default_chat_id |
| 默认回复消息 ID | FEISHU_REPLY_TO |
notify.default_reply_to |
默认群聊 ID 适合本地手动调试;Agent 自动通知应显式传入本次消息上下文中的 --chat-id / --receive-id-type。
# 健康检查
python -m cli health
# 系统统计
python -m cli stats
# 系统诊断(数据库、依赖、服务连通性)
python -m cli doctor
# Prometheus 指标
python -m cli metrics# 设置默认服务器地址
python -m cli config set server http://asr-server:15797
# 设置 API Key
python -m cli config set api_key my-token
# 查看当前配置
python -m cli config list| 方法 | 路径 | 说明 |
|---|---|---|
| 文件 | ||
| POST | /api/v1/files/upload |
上传音频文件 |
| GET | /api/v1/files/{file_id} |
查询文件元信息 |
| 任务 | ||
| POST | /api/v1/tasks |
创建转写任务(支持批量) |
| GET | /api/v1/tasks |
任务列表(分页/状态筛选/搜索/按批次;默认 20,page_size 最大 500) |
| GET | /api/v1/tasks/{task_id} |
任务详情 |
| POST | /api/v1/tasks/{task_id}/cancel |
取消任务 |
| GET | /api/v1/tasks/{task_id}/result?format= |
下载结果(json/txt/srt) |
| GET | /api/v1/tasks/{task_id}/progress |
SSE 实时进度 |
| 批次管理 | ||
| GET | /api/v1/task-groups/{group_id} |
批次概况 |
| GET | /api/v1/task-groups/{group_id}/tasks |
批次任务列表 |
| GET | /api/v1/task-groups/{group_id}/results?format= |
批次结果(txt/json/srt/zip) |
| DELETE | /api/v1/task-groups/{group_id} |
删除批次 |
| 节点管理 | ||
| POST | /api/v1/servers |
注册 ASR 节点 |
| GET | /api/v1/servers |
节点列表 |
| POST | /api/v1/servers/{server_id}/probe |
探测单节点连通性与能力,不执行 benchmark |
| POST | /api/v1/servers/{server_id}/benchmark |
对单节点执行真实 benchmark |
| POST | /api/v1/servers/benchmark |
对所有在线节点执行真实 benchmark 并刷新 RTF 基线 |
| PATCH | /api/v1/servers/{server_id} |
更新节点配置 |
| DELETE | /api/v1/servers/{server_id} |
注销节点 |
| 管理员恢复 | ||
| GET | /api/v1/admin/active-slots |
诊断服务器真实 active slot 占用和僵尸 segment |
| POST | /api/v1/admin/emergency-stop |
dry-run 或确认执行急停,取消活跃任务并释放 segment slot |
| 系统 | ||
| GET | /health |
健康检查 |
| GET | /api/v1/stats |
系统统计 |
| GET | /api/v1/diagnostics |
系统诊断 |
| GET | /metrics |
Prometheus 指标 |
# 上传文件
curl -X POST http://localhost:15797/api/v1/files/upload \
-F "file=@recording.wav"
# 创建单个任务
curl -X POST http://localhost:15797/api/v1/tasks \
-H "Content-Type: application/json" \
-d '{"items": [{"file_id": "01JQXXX...", "language": "zh"}]}'
# 创建批量任务(含回调)
curl -X POST http://localhost:15797/api/v1/tasks \
-H "Content-Type: application/json" \
-d '{
"items": [
{"file_id": "FILE_ID_1", "language": "zh"},
{"file_id": "FILE_ID_2", "language": "zh", "options": {"hotwords": "FunASR,转写"}}
],
"callback": {"url": "https://your-server.com/webhook", "secret": "your-hmac-key"}
}'
# 创建任务(控制 VAD 切分行为)
curl -X POST http://localhost:15797/api/v1/tasks \
-H "Content-Type: application/json" \
-d '{
"items": [{"file_id": "FILE_ID", "language": "zh"}],
"segment_level": "20m"
}'
# 开启重复批次检测(默认跳过;task-group submit 会自动启用)
curl -X POST 'http://localhost:15797/api/v1/tasks?skip_dedup=false' \
-H "Content-Type: application/json" \
-d '{"items": [{"file_id": "FILE_ID", "language": "zh"}], "segment_level": "10m"}'
# 查看批次状态
curl http://localhost:15797/api/v1/task-groups/<group_id>
# → {"task_group_id": "...", "total": 5, "succeeded": 3, "failed": 0, ...}
# 下载转写结果(txt 格式)
curl http://localhost:15797/api/v1/tasks/<task_id>/result?format=txt
# 下载批次 zip
curl -o results.zip http://localhost:15797/api/v1/task-groups/<group_id>/results?format=zip
# 探测节点
curl -X POST http://localhost:15797/api/v1/servers/asr-01/probe?level=offline_light
# 对单节点执行真实 benchmark(仅使用公开样本 test.mp4 + tv-report-1.wav)
curl -X POST http://localhost:15797/api/v1/servers/asr-01/benchmark
# 系统诊断
curl http://localhost:15797/api/v1/diagnosticsASR 任务管理器支持注册多台 FunASR 服务器,使用 LPT(最长处理时间优先)+ 最早完工时间做首轮预规划,并按槽位队列持续补位;只有 ONLINE 且 max_concurrency > 0 的节点参与调度。新 plan 刚生成时只会派发可立即启动的 work item,后续某个 slot 释放后才允许从既有计划中释放未来槽位任务补位,避免未来短任务抢占当前空闲槽。快节点先清空自己的队列时,会从其他节点队列尾部工作窃取更适合的任务,以缩短整批完工时间。分段任务会遵守单个父任务的段级并发上限,某个 segment 暂时不可窃取时,调度器会跳过该候选继续寻找其它可窃取任务。
# 1. 注册节点(推荐加 --benchmark,一步完成注册 + 性能基准测试)
python -m cli server register --id asr-gpu-01 --host 10.0.0.1 --port 10095 --protocol v2_new --benchmark
python -m cli server register --id asr-gpu-02 --host 10.0.0.2 --port 10095 --protocol v2_new --benchmark
# 也可以先注册再单独测速
python -m cli server register --id asr-gpu-03 --host 10.0.0.3 --port 10095 --protocol v2_new
python -m cli server benchmark asr-gpu-03
# 2. 探测连通性和能力(可选,benchmark 已包含连通性检查)
python -m cli server probe asr-gpu-01
# 3. 全量 benchmark(刷新所有在线节点的 RTF 基线与吞吐量指标)
python -m cli server benchmark
# 4. 查看节点状态
python -m cli server list| 概念 | 说明 |
|---|---|
| RTF(Real-Time Factor) | 处理1秒音频需要的时间。RTF=0.1 表示10x加速 |
| 双指标 Benchmark | single_rtf(单线程延迟)+ throughput_rtf(并发满载吞吐)。single_rtf 写入 rtf_baseline 后参与 ETA 和配额分配;throughput_rtf 用于容量对比,不直接参与调度计算 |
| 梯度并发 + 退化检测 | 逐级测试 N∈[1,2,4,8] 并发,自动识别吞吐退化点并校准 max_concurrency |
| LPT 调度 | 优先将最长文件分配给最快的服务器 |
| 最早完工时间 | 考虑当前队列负载,选择预估最早完成的节点 |
| 配额分配 | 按 max_concurrency / base_rtf 速度比例在全局批次内分配任务配额,并发多且单线程快的节点获得更多任务;max_concurrency <= 0 的节点不参与调度 |
| 槽位队列预规划 | 首轮为每个虚拟并发槽位生成有序任务队列;新 plan 只派发可立即启动的任务,既有 plan 在 slot 释放后可释放未来槽位任务补位 |
| 工作窃取 | 快节点空闲且自身队列清空时,可从其他节点队列尾部窃取更快完成的任务;收益计算使用候选任务的计划完成时间,避免多槽队列被当作单队列串行相加;若最佳候选 segment 已达到父任务并发上限,会在本轮跳过该候选继续寻找其它候选 |
| VAD 分段并行 | 超过阈值(target × 1.2,默认 12 分钟)的长音频自动 VAD 切分为多个段,采用双向交替搜索最佳切点,并行分发到多台服务器,结果按时间戳合并。支持 10m/20m/30m 三级切分力度,搜索步长按档位比例设定 |
| 自动重试 | 瞬时故障(网络超时、协议错误)的任务自动从 FAILED 回到 QUEUED 重新调度,回调和并发槽位仅在真正终态时释放 |
| 自动探测 | 服务器协议版本(v1_old/v2_new)自动探测 |
| 断路器 | CLOSED→OPEN→HALF_OPEN 三态切换,故障服务器自动隔离 |
批次概况接口 GET /api/v1/task-groups/{group_id} 的 scheduling 字段包含:
| 字段 | 说明 |
|---|---|
work_steal_count |
当前批次实际发生的工作窃取次数 |
work_steal_estimated_gain_sec |
工作窃取带来的预估收益秒数合计 |
est_stolen_total_sec |
被窃取任务在目标服务器上的预估处理时间合计 |
cross_server_segment_tasks |
同一个父任务的 segment 被分发到多台服务器的任务数 |
idle_slot_seconds |
批次 wall-clock 时间内可用服务器总 slot 的空闲量:wall_clock × ONLINE+enabled 总并发 - busy_processing_seconds。完全空闲但可用的服务器也计入总 slot;若当前没有可用服务器,则退回按本批次已分配过的服务器容量估算 |
当系统出现异常时,使用 doctor 命令排查:
python -m cli doctor输出示例:
┌──────────────────────────────────────────────────────┐
│ 系统诊断报告 │
├─────────────────────┬──────┬────────────────────────┤
│ 检查项 │ 状态 │ 说明 │
├─────────────────────┼──────┼────────────────────────┤
│ database_schema │ ✅ │ schema aligned │
│ alembic_version │ ✅ │ version: 007_add_run_generation │
│ ffprobe │ ⚠️ │ not found │
│ upload_dir │ ✅ │ runtime/storage/uploads writable │
│ asr_servers │ ✅ │ 2/2 online │
└─────────────────────┴──────┴────────────────────────┘
✅ 系统诊断通过,无阻断性问题
状态等级:
| 等级 | 含义 | 需要操作 |
|---|---|---|
| ✅ ok | 正常 | 无 |
| 功能降级但可运行 | 建议修复 | |
| ❌ error | 阻断性问题 | 必须修复 |
常见问题:
- ffprobe not found:安装 ffmpeg,用于音频时长估算。缺失时使用文件大小估算,精度降低。
- schema drift:运行
alembic upgrade head更新数据库。 - server offline:检查 ASR 服务器进程和网络连通性。
API 使用静态 Token 认证,通过 X-API-Key 请求头传递:
| Token | 用户 | 权限 |
|---|---|---|
dev-token-user1 |
user1 | 普通用户 |
dev-token-user2 |
user2 | 普通用户 |
dev-token-admin |
admin | 管理员(含节点管理、active slot 诊断与急停恢复) |
默认关闭认证(开发模式),生产环境需通过配置启用。
CLI 设置认证:
python -m cli config set api_key dev-token-user1Q: 多文件转写是串行还是并行?
当你使用 transcribe 命令传入多个文件时,所有文件会一次性上传并创建为一个批次,后端调度器会将任务智能分配到所有在线 ASR 服务器并行处理。
Q: task-group submit 重复执行会怎样?
task-group submit 默认启用 30 分钟活跃批次去重。若相同文件名、大小、语言、热词/选项和 segment_level 的批次仍在运行,CLI 会复用已有 task_group_id,避免重复批次抢占服务器 slot。确实需要重新提交同一批文件时,加 --force。
Q: ffprobe 告警可以忽略吗? 可以。缺少 ffprobe 时,系统使用文件大小估算音频时长,调度精度略降。安装 ffmpeg 后会自动检测。
Q: benchmark 用的是什么样本?
当前统一使用 3-dev/benchmark/samples/ 下的 test.mp4 和 tv-report-1.wav(已纳入版本控制,克隆即可用)。probe 仅做 WebSocket 连通性探测,不发送真实音频、不计算 RTF;benchmark 使用上述两个真实样本执行端到端转写来测量 RTF。
Q: 如何查看某个批次的所有任务?
优先使用批次端点:python -m cli task list --group <group_id> 或 curl 'http://localhost:15797/api/v1/task-groups/<group_id>/tasks?page_size=500'。
注意:通用任务列表 GET /api/v1/tasks 默认分页大小是 20,只适合看最近任务。批量统计不要直接用默认 /api/v1/tasks 结果推断完整批次,否则 45/180 这类批次会被截断。
Q: 如何一次性下载所有结果?
python -m cli task result --group <group_id> --format txt,srt 会把所有格式下载到 runtime/storage/downloads/ 并生成 batch-summary.json 摘要文件;如果你只想导出副本到别处,再显式指定 --output-dir。
Q: 单个任务如何按源文件名导出结果?
python -m cli task result <task_id> --format txt --output-dir /tmp/funasr-task-manager/<task_id>/ 会查询当前任务的原始文件名,并导出为 {原始文件名去扩展名}_result.txt,例如 办公平台20250724-144218.mp4 会生成 办公平台20250724-144218_result.txt。Agent 交付飞书附件时应优先使用这个命令生成文件,避免复用上一轮临时文件名。
Q: 任务显示 FAILED 但随后又变成 SUCCEEDED 了?
系统内置自动重试机制。任务首次失败(如网络抖动)后会自动从 FAILED 重新排队,在未达到最大重试次数前属于可恢复状态。API 响应中的 is_terminal 字段标识任务是否已到达真正终态——SUCCEEDED 和 CANCELED 总是终态,FAILED 仅在重试耗尽后才是终态。SSE 流和 CLI 批量轮询都依赖此字段判断何时停止观察。
Q: 长音频(10 分钟以上)是怎么处理的?
系统内置 VAD 分段并行转写。默认当音频超过 12 分钟(target × 1.2 触发阈值)时,自动执行 canonical WAV 转换 → VAD 静音检测 → 双向交替搜索最佳切点 → 物理切段 → 并行分发到多台服务器 → 结果按时间戳合并。用户侧看到的仍然是一个任务,段级调度和合并完全透明。可通过 --segment-level=off 关闭切分,或用 --segment-level=20m/30m 调整切分粒度(减少切分次数)。
Q: segment_level 的各个级别有什么区别?
segment_level 合并了原来的 auto_segment 和 segment_level 两个参数,取值为 off/10m/20m/30m。off 关闭切分,整文件直接转写。10m(默认)以 10 分钟为目标切分,双向搜索窗口 912 分钟,<12 分钟不拆;24 分钟,<24 分钟不拆;20m 以 20 分钟为目标,双向搜索窗口 1830m 以 30 分钟为目标,双向搜索窗口 27~36 分钟,<36 分钟不拆。搜索采用"后→前→后"双向交替策略,搜索步长按档位比例设定(60s/120s/180s)。级别越大,切分次数越少,边界质量损失越小,但单段处理时间更长。
Q: 任务创建后处于 PREPROCESSING 状态很久?
检查是否有 ASR 服务器在线:python -m cli server list。如果没有在线节点,任务会排队等待。
Q: 如何给 AI Agent 或脚本使用 CLI?
使用 --output json --quiet 模式,输出结构化 JSON。例如:
python -m cli task list --output json --quiet
Q: 回调通知怎么配置?
在创建任务时指定 --callback URL(CLI)或 "callback": {"url": "...", "secret": "..."} (API)。系统使用 Outbox 模式 + HMAC-SHA256 签名确保可靠投递。
Q: 批量上传时部分文件失败怎么办?
CLI 会在输出和 JSON 摘要中列出失败的文件(upload_failures 字段),并以非零退出码(exit 1)退出。已成功上传的文件仍会正常转写,不会因为部分失败而全部放弃。
Q: 从旧版本数据库升级需要注意什么?
运行 alembic upgrade head。迁移 002_fix_callback_outbox_schema 会自动为已有记录回填 outbox_id(ULID);003_add_throughput_rtf 会为 server_instances 表新增 throughput_rtf 和 benchmark_concurrency 列(nullable),已有服务器首次 benchmark 后自动填充;004_create_task_segments 会创建 task_segments 表,用于 VAD 分段并行转写的段级调度和状态跟踪;005_fix_nullable_and_defaults 修复 task_events.from_status nullable、server_instances.status 默认值,以及 files/server_instances 的 updated_at NOT NULL 对齐;006_add_server_enabled 为服务器增加 enabled 开关,允许临时禁用节点且不被心跳覆盖;007_add_run_generation 为任务和分段增加 run_generation,避免取消/重试后的旧 worker 回写污染结果。
| 层次 | 技术 |
|---|---|
| 后端框架 | FastAPI + Uvicorn |
| 数据库 | SQLite (aiosqlite) / PostgreSQL (asyncpg, 可选) + SQLAlchemy 2.0 |
| 任务调度 | 进程内 BackgroundTaskRunner(asyncio)+ LPT/EFT + 槽位队列预规划 + 工作窃取 + RTF 运行时校准 |
| ASR 对接 | WebSocket (websockets) |
| 前端 | Vue 3 + Vite + Element Plus + ECharts |
| 监控 | Prometheus + Grafana + Alertmanager |
| CLI | Typer + Rich + httpx |
| 部署 | Docker Compose |
cd 3-dev/src/backend
# 启动全部服务
docker-compose up -d
# 可选:使用 PostgreSQL
# 创建 .env 文件:
# POSTGRES_PASSWORD=your_secure_password
# ASR_DATABASE_URL=postgresql+asyncpg://asr:your_secure_password@postgres:5432/asr_tasks
docker compose --profile postgres up -d
docker compose exec web alembic upgrade head服务端口:
| 服务 | 地址 |
|---|---|
| API 文档 | http://localhost:15797/docs |
| 前端 | http://localhost:15798 |
| Prometheus | http://localhost:9090 |
| Grafana | http://localhost:3001(admin/admin) |
cd 3-dev/src/backend
# 先做只读评估,确认当前 runtime/storage 状态
python ../../../6-skills/funasr-task-manager-reset-test-db/scripts/reset_db.py --dry-run
# 需要干净测试环境时执行重置(会自动检测数据库是否被后端占用)
python ../../../6-skills/funasr-task-manager-reset-test-db/scripts/reset_db.py
# 重置服务器配置并插入默认测试节点
python ../../../6-skills/funasr-task-manager-reset-test-db/scripts/reset_db.py --reset-servers
# CI 环境:跳过备份和确认
python ../../../6-skills/funasr-task-manager-reset-test-db/scripts/reset_db.py --no-backup --force详细参数和行为说明见 6-skills/funasr-task-manager-reset-test-db/SKILL.md。该技能默认针对 runtime/storage/ 工作,不会扫描旧的 3-dev/src/backend/data/ 或仓库根目录 data/ 目录。
cd 3-dev/src/backend
# 全量测试
python -m pytest "../../../4-tests/scripts/" -v --cov=app
# 单元测试
python -m pytest "../../../4-tests/scripts/unit/" -v
# 集成测试
python -m pytest "../../../4-tests/scripts/integration/" -v
# E2E 测试
python -m pytest "../../../4-tests/scripts/e2e/" -v
# 压力测试
locust -f "../../../4-tests/scripts/load/locustfile.py" --headless -u 50 -r 10 -t 5m浏览器端到端测试覆盖真实用户路径:文件上传 → 批量转写 → 任务列表观察 → 结果下载 → 工件归档。提供 4 个测试配置(profile),按覆盖范围递增:
| Profile | 用途 | 文件数 |
|---|---|---|
smoke |
日常快速回归 | 3 |
remote-standard |
远端节点 / 受限带宽 | 5 |
standard |
功能合并前验证 | 4-6 |
full |
发布前全量验证 | 全部 |
cd 3-dev/src/frontend
# 生成测试素材批次
npm run test:e2e:prepare:smoke
npm run test:e2e:prepare:remote-standard
# 执行浏览器 E2E(自动启动前后端)
npm run test:e2e:smoke
npm run test:e2e:remote-standard详细的流程编排、断言策略、跨平台适配和工件归档规范见 6-skills/funasr-task-manager-web-e2e/SKILL.md。
funasr-task-manager/
├── 2-design/ # 设计文档与评审报告
├── 3-dev/src/
│ ├── benchmark/ # 公开 benchmark 样本(纳入版本控制)
│ │ └── samples/ # test.mp4 + tv-report-1.wav
│ ├── start.sh / start.ps1 # 一键启停脚本
│ ├── stop.sh / stop.ps1
│ ├── backend/
│ │ ├── app/ # FastAPI 应用
│ │ │ ├── api/ # 路由层(tasks, servers, task_groups, admin, health)
│ │ │ ├── models/ # SQLAlchemy ORM 模型
│ │ │ ├── services/ # 业务逻辑(scheduler, task_runner, diagnostics)
│ │ │ └── storage/ # 仓储层 + 文件管理
│ │ ├── cli/ # CLI 工具
│ │ │ ├── commands/ # 子命令(transcribe, task, server, admin, notify, system)
│ │ │ ├── api_client.py # HTTP API 客户端
│ │ │ └── main.py # 入口 + 全局选项
│ │ └── alembic/ # 数据库迁移
│ └── frontend/ # Vue 3 前端 + Playwright E2E
├── 4-tests/
│ ├── batch-testing/ # 批量测试(scripts 入库;assets/outputs 本地 gitignore)
│ │ ├── assets/ # 音视频测试素材
│ │ └── outputs/ # cli / e2e / benchmark 输出
│ └── scripts/
│ ├── unit/ # 单元测试
│ ├── integration/ # 集成测试
│ ├── e2e/ # 端到端测试(API/CLI 视角)
│ └── load/ # 压力测试
├── 6-skills/ # Agent 可复用的自动化技能
│ ├── _shared/ # 共享 ASR 工作流、实时通知规范、部署同步指南
│ ├── funasr-task-manager-init/ # 环境初始化与启动(Python/Docker)
│ ├── funasr-task-manager-channel-intake/ # 音频入口与意图编排(飞书/企微/Slack)
│ ├── funasr-task-manager-local-batch-transcribe/ # 服务器本地文件批量转写
│ ├── funasr-task-manager-batch-monitor/ # 子 Agent 批量任务进度监控
│ ├── funasr-task-manager-emergency-stop/ # 急停与 active slot 清理
│ ├── funasr-task-manager-media-preflight/ # 媒体文件预检查(格式/时长/转码评估)
│ ├── funasr-task-manager-result-delivery/ # 结果交付与质量初筛
│ ├── funasr-task-manager-server-benchmark/ # 服务器性能校准(RTF 基线/并发梯度)
│ ├── funasr-task-manager-reset-test-db/ # 测试前数据库重置与 dry-run 评估
│ └── funasr-task-manager-web-e2e/ # 浏览器 E2E 测试流程编排与素材管理
├── runtime/ # 运行时目录(gitignore)
│ ├── storage/ # 数据库 / 上传 / 结果 / 临时文件
│ └── logs/ # 后端/前端日志、PID 与迁移日志
└── README.md
每个 Skill 包含 SKILL.md(触发条件、执行流程、参数规则)和配套脚本/参考文档。完整介绍见上方 Agent Skills 体系。
内部项目,仅限授权使用。