恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
MOSS-Transcribe-Diarize Web后端架构解析:任务状态机、作业管理与 FastAPI 实现
首页
资讯中心
/
MOSS-Transcribe-Diarize Web后端架构解析:任务状态机、作业管理与 FastAPI 实现
MOSS-Transcribe-Diarize Web后端架构解析:任务状态机、作业管理与 FastAPI 实现
发布时间:2026/10/9 14:28:54
MOSS-Transcribe-Diarize Web后端架构解析任务状态机、作业管理与 FastAPI 实现【免费下载链接】MOSS-Transcribe-DiarizeA 0.9B model for long-form transcription in 50 languages with speaker diarization, timestamps, and acoustic event awareness项目地址: https://gitcode.com/gh_mirrors/mo/MOSS-Transcribe-DiarizeMOSS-Transcribe-Diarize 是一款 0.9B 参数量的开源长音频转写与说话人分离模型随包内置了一个名为mtd-subtitle-web的本地 FastAPI Web 应用上传音视频、异步生成带说话人标签的时间戳字幕、编辑后导出 SRT/ASS 或直接烧录 MP4。本文聚焦它的 Web 后端架构拆解任务状态机、作业管理JobManager与双推理后端HF/vLLM的协作方式帮助你在几分钟内看懂这套轻量但完整的后端设计。 一图看懂整体架构虽然上架构图展示的是模型本身但整套 Web 应用围绕这个 0.9B 模型构建了一个清晰的三层解耦核心模块分布如下层级模块职责入口层moss_transcribe_diarize/app/web_cli.py解析 CLI 参数启动 uvicorn服务层moss_transcribe_diarize/app/server.pycreate_app()工厂 FastAPI 路由业务层moss_transcribe_diarize/app/jobs.pyJobManager状态机 持久化推理层moss_transcribe_diarize/app/model_runner.py / moss_transcribe_diarize/app/vllm_runner.pyHF 本地 / vLLM 远程推理媒体层moss_transcribe_diarize/app/ffmpeg.pyffprobe 探测 FFmpeg 字幕烧录️ FastAPI 应用工厂create_app 如何装配路由create_app()是整个后端的组装线server.py#L29-L74。它接受模型路径、设备、推理参数、vLLM 端点等参数返回一个配置完毕的FastAPI实例。核心装配步骤静态资源挂载app.mount(/assets, StaticFiles(directorySTATIC_DIR))前端资源index.html、app.js、styles.css、i18n 语言包一次性挂到/assets前缀。推理后端选择backend vllm时构建VllmRunner远程 OpenAI 兼容 API否则构建ModelRunnerHF 本地懒加载。作业管理器初始化JobManager(runs_dir, runner, ...)负责所有任务生命周期构造时即启动后台工作线程。统一错误响应ERROR_STATUS_CODES将业务错误码映射到 HTTP 状态码如job_running → 409、media_missing → 404、ffmpeg_unavailable → 503。核心端点一览方法路径说明GET/返回index.html带Cache-Control: no-storeGET/api/runtime运行时信息FFmpeg 可用性、模型设备、推理参数POST/api/jobsmultipart 上传媒体 推理参数创建作业并入队GET/api/jobs按updated_at倒序列出全部作业GET/api/jobs/{id}查询单个作业状态DELETE/api/jobs/{id}删除作业运行中拒绝POST/api/jobs/{id}/rerun用相同/新参数重跑GET/api/jobs/{id}/media回传原始媒体流GET / PUT/api/jobs/{id}/segments读取/覆盖字幕分段与样式POST/api/jobs/{id}/render触发异步 MP4 渲染GET/api/jobs/{id}/download按kind下载 json/srt/ass/mp4/transcript关键设计上传即入队。POST /api/jobsserver.py#L109-L142先创建作业记录并持久化job.json随后按 1MB 分块写入媒体文件最后manager.enqueue(job.id)把任务 ID 丢进队列并立即返回job.to_dict()。前端拿到的是queued状态真正的推理发生在后台线程。⚙️ 任务状态机状态定义与流转jobs.py#L28 定义了所有合法状态并用TERMINAL_STATES标记终态TERMINAL_STATES {waiting_review, done, failed, cancelled}完整状态集状态进度触发点是否终态queued0.00create_job_*❌loading_model0.05ModelRunner._ensure_loaded❌transcribing0.10 → 0.85分片就绪 0.25随 token 数线性推进❌postprocessing0.85写原始转写 字幕文件❌waiting_review0.95后处理完成等待人工校对✅rendering0.97_render_job持有_render_lock❌done1.00渲染成功✅failed1.00任意异常✅cancelled—用户取消预留✅状态流转图queued ──► loading_model ──► transcribing ──► postprocessing ──► waiting_review │ │ │ │ │ │ │ │ │ ├──► rendering ──► done │ │ └────────────────┴──────┬───────────┴────────────────┴──► failed │ └────────────────────────────────────────┘ └──► (中断/删除)崩溃恢复服务重启时_load_existing_jobs()会扫描runs/*/job.json把仍处于非终态的作业queued、loading_model、transcribing、postprocessing、rendering统一标记为failed错误信息固定为Interrupted by previous server shutdown.保证用户看到的状态始终真实。 作业管理核心JobManager 深度剖析每个作业的磁盘布局JobRecord是slotsTrue的 dataclass字段涵盖状态、推理参数、进度、时间戳与字幕样式。每个作业对应runs/12位hex/目录runs/3f9a2c81b7d4/ ├── input.mp4 # 用户上传的原始媒体 ├── job.json # 作业元数据每次状态变更写入 ├── raw_transcript.txt # 模型原始输出 ├── segments.json # 结构化字幕分段 ├── subtitle.srt # SRT 导出utf-8-sig兼容 Excel/记事本 ├── subtitle.ass # ASS 导出用于 FFmpeg 烧录 └── output.mp4 # 烧录后的成片这种一作业一目录的设计让删除、审计、恢复都非常直接 ——shutil.rmtree(job_dir)即可彻底清理。持久化与进度节流_set_status每次都会写job.json但生成阶段的进度更新频率极高每个 token 都可能触发回调。_should_save_live_progress通过_progress_save_times字典做0.5 秒节流两次写盘间隔不足 500ms 时只更新内存不落盘。这个细节对 SSD 寿命和高并发场景都很重要。删除保护delete_job明确拒绝删除进行中的作业jobs.py#L288-L293if job.status in {queued, loading_model, transcribing, postprocessing, rendering}: raise JobManagerError(job_running, Cannot delete a job while it is running.)只有处于TERMINAL_STATES中的作业才能被删除 —— 这避免了删了记录但后台还在写文件的竞态。 后台工作线程与 GPU 锁JobManager.__init__启动一个守护线程mtd-job-workerjobs.py#L165-L166self._worker threading.Thread(targetself._worker_loop, namemtd-job-worker, daemonTrue)_worker_loop从queue.Queue中get()作业 ID调用_process_job完成后task_done()。这意味着HTTP 请求线程不阻塞上传接口立即返回推理在独立线程进行。GPU 串行ModelRunner._lock确保同一进程内任意时刻只有一个作业持有模型避免显存冲突。渲染并发安全_render_lockthreading.Lock保证同一作业同一时刻只有一个渲染线程在写output.mp4。进度映射函数generation_progress把已生成 token 数映射到 [0.25, 0.85] 区间def generation_progress(generated_tokens, max_new_tokens): if not max_new_tokens or max_new_tokens 0: return 0.25 ratio max(0.0, min(1.0, generated_tokens / max_new_tokens)) return 0.25 (0.85 - 0.25) * ratio这样前端进度条在输入就绪0.25之后到生成完毕0.85之间平滑推进不会因为 token 输出速率波动而跳变。 双推理后端ModelRunner 与 VllmRunnerModelRunner懒加载 注意力后端自动降级懒加载_ensure_loaded()只在首次transcribe时加载模型避免服务启动就占用数 GB 显存。注意力后端优先级CUDA 且装有flash-attn→flash_attention_2→sdpa→eagereager 会 OOM仅最后兜底。CPU 回退设备为 CPU 时强制dtypetorch.float32规避 bf16 兼容性问题。VllmRunnerOpenAI 兼容 API SSE 流式VllmRunner把媒体重采样为 16 kHz WAV 后 POST 到base_url/v1/audio/transcriptions支持SSE 流式解析_consume_sse_transcription逐行读data:行把usage.completion_tokens回传给状态回调复用同一套进度映射。自动 URL 补全_transcriptions_url智能判断用户传的是base、base/v1还是完整.../audio/transcriptions。Multipart 手动拼装_multipart_body不依赖requests仅用标准库urllib减少依赖。设计精髓两个 Runner 暴露同一个transcribe(audio_path, *, status_callback...)接口。上层JobManager完全不感知推理是在本地 GPU 还是远程 API 上发生替换后端只需改create_app的一个参数。 FFmpeg 字幕烧录render 端点背后的流水线POST /api/jobs/{id}/render不阻塞 HTTP而是启动独立线程mtd-render-job_id执行_render_job获取_render_lock序列化同作业渲染。状态置rendering0.97。probe_video_size探测视频分辨率默认 1920×1080。导出 ASS 并调用burn_ass_subtitlesffmpeg.py#L74-L92ffmpeg -y -i input.mp4 -vf subtitlessubtitle.ass \ -c:v libx264 -preset veryfast -crf 18 \ -c:a copy -movflags faststart output.mp4-preset veryfast换取渲染速度-crf 18保证视觉无损。-c:a copy不重编码音频显著缩短耗时。-movflags faststart把 moov 原子移到文件头支持 Web 边下边播。失败时状态回退到waiting_review0.95并记录Render failed: ...错误信息用户可修改样式后重试。 前端轮询与后端状态同步前端 moss_transcribe_diarize/app/static/app.js 使用1.5 秒间隔的setInterval轮询GET /api/jobs但只在页面存在进行中作业时启用if (shouldPoll !pollTimer) pollTimer setInterval(refreshJobs, 1500); if (!shouldPoll pollTimer) { clearInterval(pollTimer); pollTimer null; }这种按需轮询策略比 WebSocket/SSE 轻量得多对本地单用户场景足够。后端job.json每次写盘都更新updated_at前端按该字段倒序展示最新变化的作业自然浮到顶部。 架构设计要点总结设计决策解决的问题create_app工厂函数参数注入、测试可替换 runner、支持 CLI 与嵌入式两种用法queue.Queue 守护线程HTTP 线程不阻塞推理可并发排队threading.LockGPU 渲染同一进程内串行化重资源操作job.json每状态落盘崩溃可恢复审计可追溯0.5s 进度写盘节流避免高频 token 回调打爆磁盘TERMINAL_STATES显式定义删除保护、前端轮询停止、UI 态一致双 Runner 同接口本地 HF 与远程 vLLM 无缝切换ERROR_STATUS_CODES映射表业务错误码 → 标准 HTTP 状态码前端统一处理 延伸阅读模型推理与 Prompt 自定义README.md 的 Custom Prompt and Hotwords 一节微调指南FINETUNING.md字幕后处理与 ASS 导出moss_transcribe_diarize/subtitle/转写解析器moss_transcribe_diarize/transcript_parser.py完整 CLI 入口moss_transcribe_diarize/app/cli.py这套 Web 后端用不到 1500 行 Python 代码实现了一个可生产使用的异步字幕工作台状态机保证状态一致性队列 锁保证并发安全持久化 节流平衡了可恢复性与性能双 Runner 抽象屏蔽了推理位置差异。如果你想为本地 AI 项目搭建类似的异步任务系统这套代码几乎可以作为参考模板直接复用。【免费下载链接】MOSS-Transcribe-DiarizeA 0.9B model for long-form transcription in 50 languages with speaker diarization, timestamps, and acoustic event awareness项目地址: https://gitcode.com/gh_mirrors/mo/MOSS-Transcribe-Diarize创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考