asr/
├── asr_agent/ # 服务端(Python)
│ ├── dispatcher.py # HTTP Dispatcher — 多房间调度
│ ├── worker.py # VAD → ASR → LLM → TTS 完整流水线
│ ├── whisper_asr/ # ASR 核心引擎
│ │ ├── audio_processor.py # AudioBuffer 环形缓冲
│ │ ├── qwen_engine.py # Qwen3-ASR 本地引擎
│ │ └── transcript_processor.py # 转录去重
│ ├── silero_vad.onnx # Silero VAD ONNX 模型
│ ├── Dockerfile # Worker 镜像
│ └── Dockerfile.base # 基础依赖镜像
├── flutter_asr_client/ # Flutter 移动客户端
│ ├── lib/services/ # LiveKit / Settings 服务
│ ├── lib/models/ # 消息模型
│ └── lib/pages/recording/ # 录音页面 + 对话列表
└── livekit-server/ # LiveKit Docker Compose
├── docker-compose.yml
└── livekit.yaml
flowchart TD
A[App 麦克风] -->|Opus| B[LiveKit Room]
B -->|auto_subscribe| C[Worker]
C --> D[AudioStream<br/>16kHz PCM]
D --> E[Silero VAD<br/>ONNX 双阈值]
D --> F[AudioBuffer<br/>环形缓冲]
E -->|语音结束| G[ASR]
F --> G
G -->|Mimo API / Qwen3| H[transcription]
H --> I[LLM 流式 SSE<br/>Mimo v2.5 / vLLM]
I -->|分段| J[TTS 并发生成<br/>Mimo API 24kHz]
J -->|play_q 顺序入队| K[_player 单轨播放]
K -->|AudioTrack| B
B -->|Opus| A
flowchart TD
subgraph External[36.152.142.37 外网]
P10005[":10005"]
P10003[":10003"]
end
subgraph Nginx[nginx]
N1[HTTP Proxy]
N2[WebSocket Proxy]
end
subgraph Services[内网服务]
D[Dispatcher<br/>:9100]
LK[LiveKit Server<br/>:7880]
end
P10005 --> N1
P10003 --> N2
N1 -->|proxy_pass| D
N2 -->|proxy_pass| LK
A2[App] -->|POST /connect| P10005
A2 -->|WebSocket| P10003
D -->|启动 Worker| W[Worker 进程]
W -->|加入房间| LK
A2 -->|同一房间| LK
sequenceDiagram
participant A as App A
participant D as Dispatcher :9100
participant LK as LiveKit
participant W as Worker
A->>D: POST /connect
D->>D: 创建房间 room-xxx
D->>W: spawn_worker(room-xxx)
D-->>A: {room, url, token}
A->>LK: WebSocket connect (room-xxx)
W->>LK: connect (room-xxx)
Note over A,W: A 说话 → VAD → ASR → LLM → TTS
A->>LK: disconnect
LK->>W: participant_disconnected
W->>W: 检测房间空 → exit
W-->>LK: disconnect
## Worker 流水线详解
| 组件 | 实现 | 说明 |
|------|------|------|
| **VAD** | Silero VAD (ONNX) | 双阈值 0.5/0.2,5帧滑动窗口,无 ONNX 时回退能量 VAD |
| **VP** | LiveKit WebRTC | AEC/ANS/AGC 由 WebRTC 处理 |
| **ASR** | Mimo API / Qwen3 本地 | `ASR_PROVIDER` 环境变量切换;支持流式 SSE |
| **LLM** | Mimo v2.5 / vLLM | `LLM_PROVIDER` 切换;流式 SSE + `<think>` 过滤 |
| **TTS** | Mimo v2.5 API | 24kHz PCM,并行生成 + 顺序入队播放 |
| **播放** | 单轨 `LocalAudioTrack` | `play_q` 队列 + `_player` 协程持续 drain |
## 部署步骤
## SSH 连接
bash sshpass -p '123456' ssh -o StrictHostKeyChecking=no ubuntu@200.200.18.11
## 构建 & 部署
bash
python3 -m py_compile asr_agent/worker.py python3 -m py_compile asr_agent/dispatcher.py
sshpass -p '123456' scp asr_agent/worker.py ubuntu@200.200.18.11:/tmp/asr_build/worker.py sshpass -p '123456' scp asr_agent/dispatcher.py ubuntu@200.200.18.11:/tmp/asr_build/dispatcher.py sshpass -p '123456' scp -r asr_agent/whisper_asr ubuntu@200.200.18.11:/tmp/asr_build/
sshpass -p '123456' ssh ubuntu@200.200.18.11 ' cd /tmp/asr_build docker build -t asr-dispatcher:v1 . '
sshpass -p '123456' ssh ubuntu@200.200.18.11 ' docker stop asr-dispatcher 2>/dev/null docker rm asr-dispatcher 2>/dev/null docker run -d --name asr-dispatcher --network host --restart unless-stopped \ -e ASR_MODEL_PATH=/data/models/Qwen3-ASR \ -e MIMO_KEY=tp-cilplawdowf6ljqlb5lrldr0c39pe55ip1riir6zqvc3z9te \ -e LIVEKIT_URL=ws://localhost:7880 \ -e PUBLIC_LIVEKIT_URL=ws://36.152.142.37:10003 \ -e LLM_PROVIDER=mimo \ -e ASR_PROVIDER=mimo \ -v /data/models:/data/models:ro \ asr-dispatcher:v1 '
sshpass -p '123456' ssh ubuntu@200.200.18.11 'docker logs --tail 30 asr-dispatcher'
sshpass -p '123456' ssh ubuntu@200.200.18.11 ' curl -s http://localhost:9100/health curl -s -X POST http://localhost:10005/connect -H "Content-Type: application/json" -d "{\"identity\":\"test\"}" '
## 环境变量
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `LIVEKIT_URL` | `ws://localhost:7880` | Worker 连接 LiveKit 地址 |
| `PUBLIC_LIVEKIT_URL` | 同 LIVEKIT_URL | 返回给 App 的外网地址 |
| `LLM_PROVIDER` | `vllm` | `vllm` / `mimo` |
| `ASR_PROVIDER` | `qwen` | `qwen` / `mimo` |
| `LLM_MODEL` | `qwen3.6-35b-awq` | vLLM 模式下的模型名 |
| `MIMO_KEY` | — | Mimo API Key |
| `MIMO_API_BASE` | `https://token-plan-cn.xiaomimimo.com/v1` | Mimo API 地址 |
| `VAD_MODEL_PATH` | `whisper_asr/silero_vad.onnx` | Silero VAD 模型路径 |
## 端口
| 端口 | 用途 |
|------|------|
| 7880 | LiveKit 核心服务(内网) |
| 9100 | Dispatcher HTTP(内网) |
| 10003 | LiveKit nginx 代理(外网 `36.152.142.37`) |
| 10005 | Dispatcher nginx 代理(外网) |
## App 调用流程
App 断开 → Worker 退出 → 房间释放
## Flutter App 编译
bash
cd flutter_asr_client flutter build apk --debug
## nginx 配置
Dispatcher 代理配置(端口 10005):
server {
listen 10005;
location / {
proxy_pass http://127.0.0.1:9100;
}
}
``
重载:docker exec nginx nginx -s reload`