AGENTS.md 6.4 KB

ASR Agent

项目架构

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

1. 本地编译校验

python3 -m py_compile asr_agent/worker.py python3 -m py_compile asr_agent/dispatcher.py

2. 上传文件到服务器

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/

3. 服务器构建镜像

sshpass -p '123456' ssh ubuntu@200.200.18.11 ' cd /tmp/asr_build docker build -t asr-dispatcher:v1 . '

4. 重启容器

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 '

5. 查看日志

sshpass -p '123456' ssh ubuntu@200.200.18.11 'docker logs --tail 30 asr-dispatcher'

6. 测试 API

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 调用流程

  1. App → POST http://36.152.142.37:10005/connect → {room, url, token}
  2. App → WebSocket ws://36.152.142.37:10003 (LiveKit) with token
  3. Dispatcher 自动启动 Worker 加入同一房间
  4. App 说话 → VAD → ASR → LLM → TTS 播放
  5. App 断开 → Worker 退出 → 房间释放

    
    ## Flutter App 编译
    
    

    bash

cd flutter_asr_client flutter build apk --debug

产物: build/app/outputs/flutter-apk/app-debug.apk


## nginx 配置

Dispatcher 代理配置(端口 10005):

/data/nginx/config/http/dispatcher-proxy.conf

server {

listen 10005;
location / {
    proxy_pass http://127.0.0.1:9100;
}

} `` 重载:docker exec nginx nginx -s reload`