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
# Modal 部署与模型选择
## 目标
Modal 负责承载重模型和 GPU 推理,Hugging Face Space / Gradio 只负责交互界面、状态编排和轻量逻辑。
目标不是把所有模型常驻在 GPU 上,而是按需调用、缓存权重、控制冷启动和并发,尽量节省 hackathon 额度。
```text
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`
接口:
```http
POST /persona/events
Accept: text/event-stream
```
输出:
```text
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。
接口:
```http
POST /tts
```
输入:
```json
{
"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
接口:
```http
POST /image/character
```
## 省额度策略
### 模型路由
默认不要所有请求都打最大模型。
建议路由:
```text
纯文字日常聊天 -> 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 权重:
```text
/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 常驻开关:
```powershell
python scripts/set_modal_vllm_autoscaler.py on
python scripts/set_modal_vllm_autoscaler.py off
```
如果希望部署配置本身保持常驻,部署前设置:
```powershell
$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` 生成中文回复。
当前持久部署:
```text
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 部署命令:
```powershell
$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 需要保守。
建议初始值:
```text
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:
```python
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 名称,可以在运行或部署前设置:
```powershell
$env:VC_HF_SECRET_NAME="your-secret-name"
```
如果只是做 health smoke test,且还没有创建 HF Secret,可以临时设置:
```powershell
$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`
- 缓存生成结果
## 参考链接
- 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