wenhongquan 3 недель назад
Родитель
Сommit
6e9276af75
2 измененных файлов с 78 добавлено и 37 удалено
  1. 75 36
      AGENTS.md
  2. 3 1
      asr_agent/Dockerfile

+ 75 - 36
AGENTS.md

@@ -1,5 +1,5 @@
 # ASR Agent
-
+每次开发都要跟新本文件  这非常重要!!!要保证本文件能实时反应当前系统状态
 ## 项目架构
 
 ```
@@ -13,7 +13,8 @@ asr/
 │   │   ├── qwen_engine.py      # Qwen3-ASR 本地引擎
 │   │   └── transcript_processor.py  # 转录去重
 │   ├── silero_vad.onnx         # Silero VAD ONNX 模型
-│   ├── Dockerfile              # Worker 镜像
+│   ├── requirements.txt        # Python 依赖
+│   ├── Dockerfile              # Dispatcher + Worker 统一镜像
 │   └── Dockerfile.base         # 基础依赖镜像
 ├── flutter_asr_client/         # Flutter 移动客户端
 │   ├── lib/services/           # LiveKit / Settings 服务
@@ -30,20 +31,30 @@ asr/
 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
-    
+    F --> V[声纹识别<br/>Zvec ← MFCC]
+
     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
+    V -->|speaker_id| I[LLM 流式 SSE<br/>Mimo v2.5 / vLLM]
+    H --> I
+
+    V -->|加载历史| J[对话记忆<br/>JSON 持久化]
+    J --> I
+
+    I -->|TTS Segmenter<br/>分段| K[TTS 并发生成<br/>Mimo API 24kHz]
+    K -->|play_q 顺序入队| L[_player 单轨播放]
+    L -->|LocalAudioTrack| B
     B -->|Opus| A
+
+    I -->|保存对话| J
+
+    H -.->|新 utterance<br/>cur_gen++ drain_queue| L
 ```
 
 ## 服务部署架构
@@ -54,27 +65,31 @@ flowchart TD
         P10005[":10005"]
         P10003[":10003"]
     end
-    
+
     subgraph Nginx[nginx]
         N1[HTTP Proxy]
         N2[WebSocket Proxy]
     end
-    
-    subgraph Services[内网服务]
+
+    subgraph Host[内网服务 200.200.18.11]
         D[Dispatcher<br/>:9100]
+        W1[Worker 子进程 room-xxx]
+        W2[Worker 子进程 room-yyy]
         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
+
+    D -->|spawn_worker asyncio.Task| W1
+    D -->|spawn_worker asyncio.Task| W2
+    W1 -->|加入房间| LK
+    W2 -->|加入房间| LK
     A2 -->|同一房间| LK
 ```
 
@@ -101,19 +116,20 @@ sequenceDiagram
     LK->>W: participant_disconnected
     W->>W: 检测房间空 → exit
     W-->>LK: disconnect
+```
 
 ## Worker 流水线详解
 
 | 组件 | 实现 | 说明 |
 |------|------|------|
-| **VAD** | Silero VAD (ONNX) | 双阈值 0.5/0.2,5帧滑动窗口,无 ONNX 时回退能量 VAD |
+| **VAD** | Silero VAD (ONNX) | 双阈值 0.5/0.2,5帧滑动窗口,min_speech 0.3s / min_silence 0.4s / max_speech 8s;无 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 |
-| **声纹** | scipy MFCC + Zvec | 自动注册 / 识别说话人,识别结果注入 LLM 上下文 |
-| **记忆** | Zvec + JSON | 声纹向量存 Zvec;对话历史存 JSON,按用户隔离 |
+| **ASR** | Mimo API / Qwen3 本地 | `ASR_PROVIDER` 切换;`mimo` 为流式 SSE,`qwen` 为本地 Qwen3-ASR-0.6B |
+| **LLM** | Mimo v2.5 / vLLM | `LLM_PROVIDER` 切换;流式 SSE + `<think>` 过滤;注入 speaker 上下文与历史记忆 |
+| **TTS** | Mimo v2.5 API | 24kHz PCM,按句子分段,并行生成 + `play_q` 顺序入队 |
+| **播放** | 单轨 `LocalAudioTrack` | 每会话复用一条 track;`_player` 协程持续 drain;新 utterance 通过 `cur_gen++` 打断旧音频 |
+| **声纹** | scipy MFCC + Zvec | 自动注册 / 识别说话人,识别结果注入 LLM 上下文,不暴露名字 |
+| **记忆** | Zvec + JSON | 声纹向量存 Zvec;对话历史存 JSON,按 `speaker_id` 隔离,保留最近 20 轮 |
 
 ## 声纹识别
 
@@ -123,13 +139,16 @@ flowchart LR
     B --> C[Zvec 向量检索<br/>余弦相似度]
     C -->|匹配 >0.65| D[返回已知用户]
     C -->|无匹配| E[自动注册新用户]
-    D --> F[注入 LLM 上下文]
-    E --> F
-    F --> G[个性化回复]
-    
-    B --> H[对话记忆<br/>JSON 持久化]
-    H -->|同一说话人| I[恢复历史会话]
-    I --> F
+
+    D --> F[加载对话历史<br/>JSON 记忆]
+    E --> G[注入 LLM 上下文]
+    F --> G
+
+    G --> H[LLM 生成<br/>个性化回复]
+    H --> I[保存对话<br/>JSON 持久化]
+
+    I --> J[同一说话人<br/>跨会话恢复]
+    J --> F
 ```
 
 ## 会话管理
@@ -138,18 +157,28 @@ flowchart LR
 sequenceDiagram
     participant A as App
     participant D as Dispatcher
+    participant LK as LiveKit
     participant W as Worker
-    
+
     Note over A,W: 首次连接
     A->>D: POST /connect {identity}
-    D-->>A: {room: "room-xxx", token}
-    Note over A: 保存 room
-    
+    D->>D: 创建 room-xxx
+    D->>W: spawn_worker(room-xxx)
+    D-->>A: {room: "room-xxx", url, token}
+    W->>LK: connect(room-xxx)
+    A->>LK: WebSocket connect(room-xxx)
+
     Note over A,W: 暂停 → 继续
     A->>D: POST /connect {identity, room: "room-xxx"}
     D->>D: 检测 room-xxx 存活
-    D-->>A: {room: "room-xxx", token}
+    D-->>A: {room: "room-xxx", url, token}
     Note over A,W: 同房间 → Worker 保持 → 上下文不丢
+
+    Note over A,W: 全部离开
+    A->>LK: disconnect
+    LK->>W: participant_disconnected
+    W->>W: 房间空 → 退出
+    W->>LK: disconnect
 ```
 
 ## 三层记忆系统
@@ -177,9 +206,11 @@ python3 -m py_compile asr_agent/worker.py
 python3 -m py_compile asr_agent/dispatcher.py
 
 # 2. 上传文件到服务器
+sshpass -p '123456' scp asr_agent/Dockerfile ubuntu@200.200.18.11:/tmp/asr_build/Dockerfile
 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 asr_agent/voiceprint.py ubuntu@200.200.18.11:/tmp/asr_build/voiceprint.py
+sshpass -p '123456' scp asr_agent/requirements.txt ubuntu@200.200.18.11:/tmp/asr_build/requirements.txt
 sshpass -p '123456' scp -r asr_agent/whisper_asr ubuntu@200.200.18.11:/tmp/asr_build/
 
 # 3. 服务器构建镜像
@@ -222,10 +253,18 @@ curl -s -X POST http://localhost:10005/connect -H "Content-Type: application/jso
 | `PUBLIC_LIVEKIT_URL` | 同 LIVEKIT_URL | 返回给 App 的外网地址 |
 | `LLM_PROVIDER` | `vllm` | `vllm` / `mimo` |
 | `ASR_PROVIDER` | `qwen` | `qwen` / `mimo` |
+| `ASR_MODEL_PATH` | `Qwen/Qwen3-ASR-0.6B` | 本地 Qwen3-ASR 模型路径 |
 | `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 模型路径 |
+| `VAD_THRESHOLD_HIGH` | `0.5` | VAD 触发阈值 |
+| `VAD_THRESHOLD_LOW` | `0.2` | VAD 保持阈值 |
+| `VAD_WINDOW_SIZE` | `5` | VAD 滑动窗口帧数 |
+| `VAD_VOICE_RATIO` | `0.5` | 窗口内判定为语音的比例 |
+| `MIN_SPEECH_S` | `0.3` | 最短有效语音时长 |
+| `MIN_SILENCE_S` | `0.4` | 结束静音时长 |
+| `MAX_SPEECH_S` | `8.0` | 最大语音切片时长 |
 | `VP_DB_PATH` | `/data/voiceprints` | 声纹数据库目录 |
 | `VP_SIMILARITY_THRESHOLD` | `0.65` | 声纹匹配余弦相似度阈值 |
 

+ 3 - 1
asr_agent/Dockerfile

@@ -3,5 +3,7 @@ FROM k8s.device.wenhq.top:8583/docker_r/asr-base:latest
 RUN pip install --no-cache-dir onnxruntime
 COPY whisper_asr/ ./whisper_asr/
 COPY worker.py ./worker.py
+COPY dispatcher.py ./dispatcher.py
+COPY voiceprint.py ./voiceprint.py
 
-CMD ["python3", "worker.py", "--room", "asr-test"]
+CMD ["python3", "dispatcher.py"]