Files
voice_test/README.md
T

149 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# voice_test — 实时 ASR 客服机器人测试台
Chrome 麦克风 → 实时 ASR(可选多模型)→ LLM 客服人设回答 → 下拉选择 TTS 模型、预置/复刻音色并配置语速、语调、音量和表达指令;页面还可录制并下载复刻用 WAV 样本。
## 运行
```bash
cp .env.example .env # 按注释填写百炼/火山凭证及地域地址
go build -o voicetest . && ./voicetest
# 程序启动时自动加载当前目录的 .env;系统环境变量优先
```
`.env.example` 已注明 API Key、Workspace ID、复刻音色及火山语音凭证的控制台获取入口;`.env` 已加入 `.gitignore`,不要提交真实密钥。
## Chrome 在 HTTP 地址授权麦克风
`http://localhost:8090` 默认可以使用麦克风。通过局域网 IP 或域名访问 HTTP 服务时,Chrome 默认禁止 `getUserMedia`,开发测试可这样放行:
1. 打开 `chrome://flags/#unsafely-treat-insecure-origin-as-secure`
2.**Insecure origins treated as secure** 设置为 **Enabled**
3. 填入完整来源,例如 `http://192.168.1.10:8090`,不能省略协议或端口。
4. 点击 **Relaunch** 重启 Chrome。
5. 重新打开页面,点击地址栏左侧站点图标 → **网站设置****麦克风****允许**
也可使用独立测试配置启动 Chrome:
```bash
google-chrome \
--user-data-dir=/tmp/voice-test-chrome \
--unsafely-treat-insecure-origin-as-secure=http://192.168.1.10:8090
```
该选项仅用于受信任的开发网络;正式部署应使用 HTTPS。也可以通过 `ssh -L 8090:localhost:8090 user@server` 转发后访问 `http://localhost:8090`,无需放宽 Chrome 安全策略。
## 可选 ASR 模型
| 模型 | 厂商 | 已验证 |
|---|---|---|
| `qwen-audio-3.0-asr-flash-streaming` | 阿里百炼 | ✅ 支持 continue-task 上下文 |
| `fun-asr-realtime` | 阿里百炼 | ✅ 支持 continue-task 上下文 |
| `fun-asr-flash-2026-06-15` | 阿里百炼 | ✅ 不支持上下文(自动跳过) |
| `volc-bigmodel` (Doubao-Seed-ASR / sauc) | 火山引擎 | ✅ X-Api-Key 新版鉴权 |
火山需要语音技术控制台(非 VOLCENGINE_ACCESS_KEY)的凭证,两种鉴权方式自动适配:
```bash
# 新版控制台(已验证可用):单 APP Key → X-Api-Key
export VOLC_ASR_APP_KEY=...
# 旧版控制台:APP ID + Access Token → X-Api-App-Key + X-Api-Access-Key
export VOLC_ASR_APP_ID=...
export VOLC_ASR_ACCESS_TOKEN=...
export VOLC_ASR_RESOURCE_ID=volc.bigasr.sauc.duration # 2.0 用 volc.seedasr.sauc.duration
```
## 环境变量
完整模板与参数获取方式见 [`.env.example`](.env.example)。
| 变量 | 默认 | 说明 |
|---|---|---|
| `BAILIAN_BASE_URL` / `BAILIAN_API_KEY` / `BAILIAN_WSS_BASE_URL` | - | 阿里百炼地址与 API Key |
| `BAILIAN_LLM_MODEL` | `qwen3.8-flash` | 客服回答模型(OpenAI 兼容流式) |
| `BAILIAN_TTS_MODEL` | `cosyvoice-v3.5-plus` | 语音合成模型 |
| `BAILIAN_TTS_VOICE` | 空 | 默认音色或百炼复刻/设计生成的 `voice_id` |
| `BAILIAN_TTS_INSTRUCTION` | 空 | 默认声音表达指令,网页可覆盖 |
| `BAILIAN_VOICE_API_URL` | DashScope 声音管理接口 | 查询当前账号的复刻/设计音色下拉列表 |
| `PORT` | `:8090` | 监听地址 |
## 已验证的端到端指标(同一句“查询订单发货”,50ms/块推流,句级流式 TTS + 无 thinking
| 模型 | ASR final | 首 token | 首音频 | 全轮完成 |
|---|---|---|---|---|
| qwen-audio-3.0-asr-flash-streaming | 2.04s | 2.54s | 3.35s | 5.73s |
| fun-asr-realtime | 2.17s | 2.60s | 3.31s | 5.59s |
| fun-asr-flash-2026-06-15 | 1.86s | 2.16s | 2.89s | 5.10s |
| volc-bigmodel (Doubao-Seed-ASR) | 2.60s | 4.29s | 5.18s | 7.84s |
优化前(TTS 等全文 + LLM 默认 thinking):首 token ~4.6s / 首音频 ~5.5s / 全轮 ~8.2s。
两项优化:
1. `enable_thinking: false`(qwen3 默认开思考,纯耗时 +0.5s,非流式实测 1.10s→0.55s
2. 句级流式 TTS:LLM 出字遇句末标点(。!?;超长退逗号)即切句喂 TTS,不等全文
ASR 尾静音实测:语音结束→final 判定 ≈ 0.5sqwen 0.6s),`max_end_pausing_time` 参数在该端点不生效(垃圾参数同样延迟,服务端忽略未知参数)。
多轮对话(含 ASR 上下文注入)验证通过:两轮 finals=2 replies=2 无错误(百炼与火山均验证)。
## 结构
- `main.go` — HTTP + `/agent` WS 会话编排(ASR 事件 → LLM 流式 → TTS 流式)
- `asr.go` — provider 接口
- `asr_bailian.go` — 百炼实时识别(run-task/continue-task 文本协议)
- `asr_volc.go` — 火山 sauc bigmodel(二进制帧协议)
- `llm.go` — OpenAI 兼容流式对话 + 客服 system prompt
- `tts.go` — CosyVoice 流式合成(PCM16/16k
- `voices.go` — TTS 模型、预置音色与百炼复刻音色列表
- `.env.example` — 环境变量模板及各参数获取方式
- `web/index.html` — 测试页(AudioWorklet 采集 PCM16/16k、WAV 样本录制、音量条、双栏转录)
## 已知简化
- 打断依赖 ASR 产生 partial 事件;外放回声可能误触发,建议戴耳机测试。
- 会话历史仅存内存、每连接独立;刷新页面即重置。
## 验证清单
### ✅ 已自动化验证(脚本模拟浏览器客户端,50ms/块推流)
- [x] 百炼 ASR 三模型协议全通:run-task → task-started → 音频流 → finish-task → task-finished
- [x] 识别正确性:同一句“你好,我想查询一下我的订单什么时候发货。”三模型 100% 准确
- [x] LLM `qwen3.8-flash` 流式回答,客服人设生效(口语化、无 markdown、主动要订单号)
- [x] TTS `cosyvoice-v3.5-plus` + 百炼复刻音色:PCM16/16k 音频正常返回(cosyvoice-v2 会报 418,勿用)
- [x] E2E 指标见上表;多轮对话 finals=2 replies=2 无错误,ASR 上下文注入生效
- [x] `fun-asr-flash` 不支持 continue-task → 已加模型守卫自动跳过
- [x] 火山 sauc 二进制帧协议:新版 X-Api-Key 鉴权、句级 utterance definite 判定 final、负包(flags=2/3)结束,真实凭证 E2E 验证通过(含多轮)
### 👤 需人工在 Chrome 验证
```bash
go build -o voicetest . && ./voicetest
```
1. [ ] Chrome 打开 `http://localhost:8090`,ASR/TTS 模型及预置/复刻音色均显示为下拉框
2. [ ] 选 `qwen-audio-3.0-asr-flash-streaming` → 🎤 开始 → 授权麦克风
3. [ ] 说“你好,我想查一下我的订单什么时候发货”:
- [ ] 左栏灰色 partial 实时上屏,停顿后固化为 final
- [ ] 延迟数字合理(<2s
- [ ] 右栏客服回复流式出现
- [ ] 扬声器播出 TTS(建议戴耳机)
4. [ ] 追问“订单号是 12345”:确认多轮上下文生效(回答应引用订单号)
5. [ ] 停止 → 重新开始,切换 `fun-asr-realtime` / `fun-asr-flash-2026-06-15` 重复 3
6. [ ] 点停止后 asr badge 回 idle;服务端日志出现 `ASR started` 而非报错
7. [ ] 点击“录制复刻样本”,录制后可试听并下载单声道 PCM WAV
8. [ ] 火山:选 `volc-bigmodel` 重复 3、4(已自动化验证通过,人工复测语音即可)
### 🧹 清理
```bash
pkill -x voicetest
```
### 边界情况备忘
- 远程机器:SSH 端口转发 `ssh -L 8090:localhost:8090`,浏览器访问 `localhost:8090` 仍是安全上下文,麦克风可用
- 外放会自收音导致 ASR 把 TTS 也转写(无回声消除的浏览器实现依赖耳机)
- 端口冲突:`PORT=:9090 ./voicetest` 可换