A newer version of the Gradio SDK is available: 6.20.0
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-itgoogle/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-itgoogle/gemma-4-E4B-itgoogle/gemma-4-12B-itgoogle/gemma-4-26B-A4B-itgoogle/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/wavbytes- 或 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-cacheModal 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/ttsendpoint- Gradio 调用 TTS 并播放音频
第二步:LLM event endpoint
先用 mock 事件流,再接 Gemma。
产物:
modal_llm.py/persona/eventsSSE endpoint- Gradio 能显示 text_delta、stage、voice、skill
第三步:图像输入
先用上传图片,不做实时摄像头。
产物:
- 图片传到 LLM/VLM endpoint
- 生成
vision_note - 角色基于
vision_note回复
第四步:生图
按需生成角色头像。
产物:
modal_image.py/image/character- 缓存生成结果
参考链接
- Modal vLLM / Gemma 示例: https://modal.com/docs/examples/vllm_inference
- Modal streaming endpoints: https://modal.com/docs/guide/streaming-endpoints
- Modal GPU: https://modal.com/docs/guide/gpu
- Modal Volumes: https://modal.com/docs/guide/volumes
- Modal Chatterbox TTS: https://modal.com/docs/examples/chatterbox_tts
- Modal Flux: https://modal.com/docs/examples/flux
- Gemma 4 12B IT: https://huggingface.co/google/gemma-4-12B-it
- Gemma 4 26B A4B: https://huggingface.co/google/gemma-4-26B-A4B-it
- Gemma 4 E4B: https://huggingface.co/google/gemma-4-E4B-it
- vLLM PyPI: https://pypi.org/project/vllm/
- vLLM supported models latest docs: https://docs.vllm.ai/en/latest/models/supported_models/
- vLLM nightly wheels: https://docs.vllm.ai/en/latest/contributing/ci/nightly_builds/
- Kokoro-82M: https://huggingface.co/hexgrad/Kokoro-82M
- Chatterbox: https://huggingface.co/ResembleAI/chatterbox
- Dia-1.6B: https://huggingface.co/nari-labs/Dia-1.6B
- Sesame CSM-1B: https://huggingface.co/sesame/csm-1b