virtual-characters / MODAL_DEPLOYMENT.md
ShadowInk's picture
Deploy Virtual Characters for Build Small Hackathon
005e075 verified
|
Raw
History Blame Contribute Delete
14.5 kB

A newer version of the Gradio SDK is available: 6.20.0

Upgrade

Modal 部署与模型选择

目标

Modal 负责承载重模型和 GPU 推理,Hugging Face Space / Gradio 只负责交互界面、状态编排和轻量逻辑。

目标不是把所有模型常驻在 GPU 上,而是按需调用、缓存权重、控制冷启动和并发,尽量节省 hackathon 额度。

Gradio Space
  -> Modal LLM / VLM endpoint
  -> Modal TTS endpoint
  -> Modal image generation endpoint

当前模型判断

Google / Gemma 系列

截至 2026-06-11,Google 在 Hugging Face 上有最新的 Gemma 4 12B 规格:

  • google/gemma-4-12B-it
  • google/gemma-4-12B

其中 google/gemma-4-12B-it 是 Gemma 4 12B Unified instruction-tuned 模型。它支持文本、图片、音频、视频输入并生成文本输出,许可证是 Apache-2.0。这个模型非常适合本项目:它可以同时处理角色对话、图片理解和视频片段理解。当前页面先保留文字 + 图片输入,不接语音输入链路。

当前官方 Hugging Face 页面核到的 Gemma 4 规格是:

  • google/gemma-4-E2B-it
  • google/gemma-4-E4B-it
  • google/gemma-4-12B-it
  • google/gemma-4-26B-A4B-it
  • google/gemma-4-31B-it

关键差异:

  • E2B / E4B:支持文本、图片、音频输入,适合“音频直接进模型”的实验。
  • 12B Unified:支持文本、图片、音频、视频输入;约 11.95B 参数,256K context;encoder-free unified multimodal 架构,适合作为主力中等规模模型。
  • 26B A4B:MoE,总参数约 26B,活跃参数约 4B,支持文本和图片,适合主对话、角色推理、视觉理解,但不支持原生音频输入。
  • 31B:文本和图片能力强,但更贵,不适合作为默认在线模型。

结论:

  • google/gemma-4-12B-it 应该作为当前首选主模型候选。
  • 当前改版暂不提供语音输入,先集中验证文字、图片和 TTS 输出。
  • 如果后续重新评估语音输入,优先单独做一轮 Gemma 音频输入实验,不并入当前 TTS 交付。
  • 如果想要更强文本/图像推理质量,再试 gemma-4-26B-A4B-it
  • 不建议默认上 31B,除非做离线评估或最终 demo 高质量路线。

TTS 候选

Chatterbox

推荐作为第一优先级实验。

原因:

  • 支持多语言,包括中文。
  • 支持情绪夸张度 / intensity control。
  • Hugging Face 模型页标 MIT。
  • Modal 官方有 Chatterbox TTS API 示例。
  • 很适合角色项目,因为可以把模型输出的 voice.energy / emotion 映射成 exaggeration、cfg、语速等参数。

注意:

  • 有 voice cloning 能力,但公开 demo 不要克隆商业角色或真实声优。
  • 可先使用内置 voice prompt 或原创 voice prompt。

Kokoro-82M

只适合作为省额度 fallback,不建议作为中文角色主 TTS。

原因:

  • 82M,非常轻。
  • Apache-2.0。
  • 推理成本低,速度快。

限制:

  • Hugging Face 模型页顶部标签偏 English,虽然模型事实里写 v1.0 是多语言。
  • 中文路径依赖额外 G2P 包,实测暴露 ordered_set / pypinyin 等间接依赖问题。
  • 角色表现力和中文稳定性都不如 Chatterbox Multilingual。

Dia-1.6B

适合作为表达力实验,不建议作为第一默认。

原因:

  • 能生成对话式 TTS。
  • 支持笑声、叹气等非语言表达。

限制:

  • 模型页说明当前主要支持英文生成。
  • 需要约 10GB VRAM。
  • 也有 voice cloning 能力,公开 demo 要避免身份滥用。

Sesame CSM-1B

可作为研究候选。

原因:

  • Conversational Speech Model,支持文本和音频上下文。
  • Transformers 已支持。

限制:

  • 模型 gated,需要接受访问条件。
  • 上手和稳定性要单独验证。

语音输入策略

当前实现不提供语音输入,也不部署独立转写服务。第一阶段只做文字输入、图片输入和 TTS 输出,避免把交互问题和模型冷启动问题混在一起。

生图候选

推荐先用 FLUX.1-schnell。

原因:

  • 适合快速生成角色头像、半身像、背景。
  • Modal 官方有 Flux on H100 示例。
  • schnell 步数少,适合按需生成。

使用原则:

  • 不在每轮对话里调用。
  • 只在角色资产生成、重绘、创建自定义角色时调用。
  • 生成后缓存到本地或 Modal Volume,Gradio 直接读缓存图。

Modal 服务拆分

建议拆成多个 Modal app 或多个 class,避免一个容器装所有模型。

1. modal_llm.py

用途:

  • 角色对话。
  • 图片理解。
  • 输出 SSE 事件流。

候选模型:

  • 省资源路线:google/gemma-4-E4B-it
  • 主推路线:google/gemma-4-12B-it
  • 高质量路线:google/gemma-4-26B-A4B-it

接口:

POST /persona/events
Accept: text/event-stream

输出:

data: {"type":"stage","expression":"thinking","motion":"look_at_user"}

data: {"type":"voice","style":"soft","speed":0.92,"energy":0.45}

data: {"type":"text_delta","text":"嗯,"}

data: {"type":"sentence_end"}

data: {"type":"done"}

2. modal_tts.py

用途:

  • 分句 TTS。
  • 返回 WAV/MP3 bytes。
  • 可选支持流式音频。

候选模型:

  • 默认:Chatterbox Multilingual。
  • fallback:Kokoro-82M。
  • 实验:Dia / CSM。

接口:

POST /tts

输入:

{
  "text": "我在听。",
  "voice_id": "star_knight_soft",
  "emotion": "concerned",
  "speed": 0.92,
  "energy": 0.45
}

输出:

  • audio/wav bytes
  • 或 JSON 包含临时文件 URL

3. modal_image.py

用途:

  • 生成内置角色图。
  • 重绘角色头像。
  • 自定义角色资产生成。

候选模型:

  • FLUX.1-schnell
  • SDXL fallback

接口:

POST /image/character

省额度策略

模型路由

默认不要所有请求都打最大模型。

建议路由:

纯文字日常聊天 -> E4B 或 12B
图片输入 -> Gemma 4 12B / E4B / 26B A4B
音频输入实验 -> Gemma 4 12B 或 E4B
中等成本角色回复 -> Gemma 4 12B
高质量角色回复 / 最终 demo -> 26B A4B
TTS -> Chatterbox 或 Kokoro
生图 -> 用户明确点击才调用

冷启动和缓存

必须使用 Modal Volume 缓存 Hugging Face 权重:

/root/.cache/huggingface -> huggingface-cache volume
/root/.cache/vllm -> vllm-cache volume

服务参数建议:

  • 开发期 scaledown_window=60-180s
  • demo 录制期 scaledown_window=5-15min
  • 公开 demo 期只让主 vLLM LLM 使用 min_containers=1,不要把 TTS、生图一起常驻
  • 大模型用固定 revision,避免模型仓库更新导致不可复现
  • 不要在 Gradio 启动时预热所有模型
  • 只预热当前选择的角色和当前模型

主 vLLM 常驻开关:

python scripts/set_modal_vllm_autoscaler.py on
python scripts/set_modal_vllm_autoscaler.py off

如果希望部署配置本身保持常驻,部署前设置:

$env:VC_VLLM_MIN_CONTAINERS="1"
$env:VC_VLLM_BUFFER_CONTAINERS="0"
$env:VC_VLLM_SCALEDOWN_WINDOW="1200"
modal deploy modal_apps/modal_vllm_gemma.py

scaledown_window 只能减少短时间空闲后的冷启动;真正避免从零启动要使用 min_containers=1。这会让 GPU 24 小时计费。

一周常驻成本

按 Modal 2026-06-14 公开 GPU 价格,7 天是 168 小时:

GPU 约每小时 约 7 天
T4 $0.5904 $99.19
L4 $0.7992 $134.27
A10 $1.1016 $185.07
L40S $1.9512 $327.80
A100-40GB $2.0988 $352.60
A100-80GB $2.4984 $419.73
H100 $3.9492 $663.47

这些是 GPU-only 估算,CPU、内存、区域倍率、非抢占、Volume 存储等另计。当前已验证的 google/gemma-4-12B-it vLLM 路线使用 L40S,因此 7 天常驻只算 GPU 也超过 $240;$240 约能覆盖 L40S 123 小时。A10 一周 GPU-only 约 $185,但当前 12B vLLM 没有在 A10 上验证,显存余量风险较高。

当前 Modal 实测

2026-06-11 已在 verno / veronicaulises0 Modal workspace 测试 google/gemma-4-12B-it + L40S

  • 首次运行:包含镜像构建和首次权重缓存,客户端总耗时约 149.1s。
  • 缓存后运行:客户端总耗时约 33.7s;模型加载约 10.3s;生成 100 tokens 用时约 7.84s,约 12.75 tokens/s。
  • 显存峰值:约 22.34 GB。

详细记录见 BENCHMARK_RESULTS.md

vLLM 稳定版已尝试 google/gemma-4-12B-it,当前不可用。0.21.0 和 PyPI 最新稳定版 0.22.1 都会把模型解析为 TransformersMultiModalForCausalLM,提示没有 vLLM 原生实现并 fallback 到 Transformers,随后在 profile run 出现 shape mismatch。

截至 2026-06-12,Gemma 4 Unified 的可用 vLLM 路线是 main/nightly,而不是 PyPI 稳定版。当前已在 Modal L40S 上跑通 vllm==0.22.1rc1.dev468+gfbc3a1907.cu129,vLLM 日志解析架构为 Gemma4UnifiedForConditionalGeneration,并成功通过 OpenAI-compatible /v1/chat/completions 生成中文回复。

当前持久部署:

https://veronicaulises0--virtual-characters-vllm-gemma-serve.modal.run

部署注意:

  • 当前部署设置 VC_SKIP_HF_SECRET=1,没有把 hf-token 挂载到 nightly vLLM 环境。
  • 能启动是因为 vc-hf-cache Modal Volume 中已有 google/gemma-4-12B-it 权重缓存。
  • 如果清空 Volume 或迁移 workspace,需要先预缓存权重,或者明确批准 nightly 环境挂载 hf-token
  • 正式 endpoint 冷启动约 3 分钟;warm 后短中文回复实测约 10-16 tok/s。

短期策略:

  • demo 主线可以切到当前 vLLM nightly endpoint,但要保留 Transformers Modal 服务作为 fallback。
  • 如果继续追 vLLM 速度,优先优化当前 nightly endpoint 的冷启动、warmup 和 --enforce-eager 策略。
  • 不要再浪费额度反复测试 vllm==0.22.1 + google/gemma-4-12B-it 这一组合。

nightly 部署命令:

$env:PYTHONIOENCODING="utf-8"
$env:PYTHONUTF8="1"
$env:VC_SKIP_HF_SECRET="1"
$env:VC_VLLM_MODEL="google/gemma-4-12B-it"
$env:VC_VLLM_PACKAGE="vllm==0.22.1rc1.dev468+gfbc3a1907.cu129"
$env:VC_VLLM_EXTRA_INDEX_URL="https://wheels.vllm.ai/nightly/cu129"
$env:VC_VLLM_UV_EXTRA_OPTIONS="--index-strategy unsafe-best-match"
$env:VC_VLLM_PRE="1"
$env:VC_VLLM_GPU="L40S"
$env:VC_VLLM_FAST_BOOT="1"
.venv\Scripts\modal.exe deploy modal_apps/modal_vllm_gemma.py

TTS 已测试 Chatterbox 分句流式:底层不是音频 token 流式,但可以按句合成、按句播放。warm 后每句约 2-3 秒,足够做 demo。

并发

TTS 可以允许较高并发;LLM 需要保守。

建议初始值:

LLM: max_inputs 4-16,按模型和 GPU 调
TTS: max_inputs 4-10
Image: max_inputs 1-2

GPU 选择

初始建议:

  • Chatterbox TTS:A10G / L4 起步。
  • Kokoro:CPU / L4 / T4 均可试。
  • Gemma 4 E4B:L4 / A10 / L40S 起步实测。
  • Gemma 4 12B:L40S / A100 起步更稳,量化后可再评估更低规格。
  • Gemma 4 26B A4B:A100 / H100 / H200 更稳,Modal 官方 vLLM 示例用了 H200。
  • FLUX.1-schnell:H100 最快,但开发期可以不常驻,按需运行。

流式协议与 Modal

Modal 支持 FastAPI StreamingResponse。因此推荐 Modal 端直接输出 SSE:

from fastapi.responses import StreamingResponse

def event_stream():
    yield b'data: {"type":"stage","expression":"thinking"}\n\n'
    yield b'data: {"type":"text_delta","text":"嗯,"}\n\n'
    yield b'data: {"type":"done"}\n\n'

return StreamingResponse(event_stream(), media_type="text/event-stream")

注意:

  • 不要要求模型原生输出完美 NDJSON。
  • 后端可以先发初始 stage/voice 事件,再转发模型 token。
  • Gradio 端消费 SSE,转换成组件多次 yield
  • 调试时可以把 SSE payload 保存成 NDJSON 文件。

Modal Secret

需要的 secret:

  • hf-token:默认 Hugging Face token secret,用于下载 gated 或大模型权重。
  • 可选 modal-proxy-auth:如果部署私有 endpoint。

如果 Modal 里已经有别的 Secret 名称,可以在运行或部署前设置:

$env:VC_HF_SECRET_NAME="your-secret-name"

如果只是做 health smoke test,且还没有创建 HF Secret,可以临时设置:

$env:VC_SKIP_HF_SECRET="1"
python scripts/check_modal_connectivity.py --mode remote-methods

这个开关默认只适合检查 Modal 容器启动。例外是当前 vLLM nightly + Gemma 4 路线:为了避免把 hf-token 暴露给 nightly 依赖栈,可以在确认 vc-hf-cache Volume 已经有完整权重缓存后使用 VC_SKIP_HF_SECRET=1 部署。常规稳定依赖栈仍建议创建 hf-token,并在 Hugging Face 上接受相关模型的访问条件。

注意不要把 token 写进仓库。

开发顺序

第一步:TTS endpoint

先做 Chatterbox 或 Kokoro,因为它最容易让 demo 有角色存在感。

产物:

  • modal_tts.py
  • /tts endpoint
  • Gradio 调用 TTS 并播放音频

第二步:LLM event endpoint

先用 mock 事件流,再接 Gemma。

产物:

  • modal_llm.py
  • /persona/events SSE endpoint
  • Gradio 能显示 text_delta、stage、voice、skill

第三步:图像输入

先用上传图片,不做实时摄像头。

产物:

  • 图片传到 LLM/VLM endpoint
  • 生成 vision_note
  • 角色基于 vision_note 回复

第四步:生图

按需生成角色头像。

产物:

  • modal_image.py
  • /image/character
  • 缓存生成结果

参考链接