384 lines
7.5 KiB
Markdown
384 lines
7.5 KiB
Markdown
|
|
# 语音对话功能说明
|
|||
|
|
|
|||
|
|
## 🎯 功能概述
|
|||
|
|
|
|||
|
|
完整的语音对话流程已实现,包括:
|
|||
|
|
1. **ASR(语音识别)** - 用户语音 → 文字
|
|||
|
|
2. **LLM(对话生成)** - 文字 → AI 回复文字
|
|||
|
|
3. **TTS(语音合成)** - AI 回复文字 → 语音
|
|||
|
|
4. **前端播放** - 播放 AI 的语音回复
|
|||
|
|
|
|||
|
|
## 📋 API 端点
|
|||
|
|
|
|||
|
|
### 1. 仅 ASR 识别(已有)
|
|||
|
|
```
|
|||
|
|
POST /voice/call/asr
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**请求**:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"audio_data": "base64编码的音频数据",
|
|||
|
|
"format": "wav"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 1,
|
|||
|
|
"msg": "ok",
|
|||
|
|
"data": {
|
|||
|
|
"text": "识别的文字"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 完整语音对话(新增)
|
|||
|
|
```
|
|||
|
|
POST /voice/call/conversation
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**请求**:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"audio_data": "base64编码的音频数据",
|
|||
|
|
"format": "wav"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 1,
|
|||
|
|
"msg": "ok",
|
|||
|
|
"data": {
|
|||
|
|
"user_text": "用户说的话",
|
|||
|
|
"ai_text": "AI的回复文字",
|
|||
|
|
"audio_data": "base64编码的AI语音",
|
|||
|
|
"audio_format": "mp3"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🔄 完整流程
|
|||
|
|
|
|||
|
|
### 后端处理流程
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
用户录音
|
|||
|
|
↓
|
|||
|
|
1. 接收 base64 音频数据
|
|||
|
|
↓
|
|||
|
|
2. 解码为二进制
|
|||
|
|
↓
|
|||
|
|
3. 上传到 OSS
|
|||
|
|
↓
|
|||
|
|
4. 调用 DashScope ASR
|
|||
|
|
↓
|
|||
|
|
5. 等待识别结果
|
|||
|
|
↓
|
|||
|
|
6. 获取用户文字
|
|||
|
|
↓
|
|||
|
|
7. 构建对话上下文(包含恋人人格)
|
|||
|
|
↓
|
|||
|
|
8. 调用 LLM 生成回复
|
|||
|
|
↓
|
|||
|
|
9. 清理回复文本(去除 Markdown)
|
|||
|
|
↓
|
|||
|
|
10. 获取用户的音色配置
|
|||
|
|
↓
|
|||
|
|
11. 调用 TTS 合成语音
|
|||
|
|
↓
|
|||
|
|
12. 将语音编码为 base64
|
|||
|
|
↓
|
|||
|
|
13. 返回完整结果
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 前端处理流程
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
用户按住说话按钮
|
|||
|
|
↓
|
|||
|
|
1. 开始录音
|
|||
|
|
↓
|
|||
|
|
2. 松开按钮,停止录音
|
|||
|
|
↓
|
|||
|
|
3. 读取录音文件
|
|||
|
|
↓
|
|||
|
|
4. 转换为 base64
|
|||
|
|
↓
|
|||
|
|
5. 调用 /voice/call/conversation
|
|||
|
|
↓
|
|||
|
|
6. 显示"对话处理中..."
|
|||
|
|
↓
|
|||
|
|
7. 接收响应
|
|||
|
|
↓
|
|||
|
|
8. 显示用户说的话
|
|||
|
|
↓
|
|||
|
|
9. 解码 AI 语音数据
|
|||
|
|
↓
|
|||
|
|
10. 保存为临时文件
|
|||
|
|
↓
|
|||
|
|
11. 显示 AI 回复文字
|
|||
|
|
↓
|
|||
|
|
12. 播放 AI 语音
|
|||
|
|
↓
|
|||
|
|
13. 播放完成后清理临时文件
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🎨 前端代码示例
|
|||
|
|
|
|||
|
|
### 发送语音并接收回复
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
async sendAudioToASR(audioBytes) {
|
|||
|
|
// 1. 转换为 base64
|
|||
|
|
let base64Audio = ''
|
|||
|
|
for (let i = 0; i < audioBytes.length; i++) {
|
|||
|
|
base64Audio += String.fromCharCode(audioBytes[i])
|
|||
|
|
}
|
|||
|
|
base64Audio = btoa(base64Audio)
|
|||
|
|
|
|||
|
|
// 2. 调用 API
|
|||
|
|
const response = await uni.request({
|
|||
|
|
url: this.baseURLPy + '/voice/call/conversation',
|
|||
|
|
method: 'POST',
|
|||
|
|
header: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
'Authorization': 'Bearer ' + uni.getStorageSync("token")
|
|||
|
|
},
|
|||
|
|
data: {
|
|||
|
|
audio_data: base64Audio,
|
|||
|
|
format: 'wav'
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 3. 处理响应
|
|||
|
|
const data = response.data.data
|
|||
|
|
const userText = data.user_text
|
|||
|
|
const aiText = data.ai_text
|
|||
|
|
const audioData = data.audio_data
|
|||
|
|
|
|||
|
|
// 4. 显示识别结果
|
|||
|
|
uni.showToast({
|
|||
|
|
title: `你说: ${userText}`,
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 5. 播放 AI 语音
|
|||
|
|
await this.playAIVoice(audioData, aiText)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 播放 AI 语音
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
async playAIVoice(base64Audio, aiText) {
|
|||
|
|
// 1. 解码 base64
|
|||
|
|
const binaryString = atob(base64Audio)
|
|||
|
|
const bytes = new Uint8Array(binaryString.length)
|
|||
|
|
for (let i = 0; i < binaryString.length; i++) {
|
|||
|
|
bytes[i] = binaryString.charCodeAt(i)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 保存为临时文件
|
|||
|
|
const fs = uni.getFileSystemManager()
|
|||
|
|
const tempFilePath = `${wx.env.USER_DATA_PATH}/ai_voice_${Date.now()}.mp3`
|
|||
|
|
fs.writeFileSync(tempFilePath, bytes.buffer, 'binary')
|
|||
|
|
|
|||
|
|
// 3. 显示 AI 回复文字
|
|||
|
|
uni.showToast({
|
|||
|
|
title: `AI: ${aiText}`,
|
|||
|
|
icon: 'none',
|
|||
|
|
duration: 3000
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 4. 播放音频
|
|||
|
|
const innerAudioContext = uni.createInnerAudioContext()
|
|||
|
|
innerAudioContext.src = tempFilePath
|
|||
|
|
innerAudioContext.autoplay = true
|
|||
|
|
|
|||
|
|
innerAudioContext.onEnded(() => {
|
|||
|
|
// 清理临时文件
|
|||
|
|
fs.unlinkSync(tempFilePath)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🔧 配置要求
|
|||
|
|
|
|||
|
|
### 环境变量
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# DashScope API Key(ASR + LLM + TTS)
|
|||
|
|
DASHSCOPE_API_KEY=sk-xxx
|
|||
|
|
|
|||
|
|
# OSS 配置(存储音频文件)
|
|||
|
|
ALIYUN_OSS_ACCESS_KEY_ID=xxx
|
|||
|
|
ALIYUN_OSS_ACCESS_KEY_SECRET=xxx
|
|||
|
|
ALIYUN_OSS_BUCKET_NAME=xxx
|
|||
|
|
ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com
|
|||
|
|
ALIYUN_OSS_CDN_DOMAIN=https://xxx.oss-cn-hangzhou.aliyuncs.com
|
|||
|
|
|
|||
|
|
# LLM 配置
|
|||
|
|
LLM_MODEL=qwen-plus
|
|||
|
|
LLM_TEMPERATURE=0.8
|
|||
|
|
LLM_MAX_TOKENS=2000
|
|||
|
|
|
|||
|
|
# TTS 配置
|
|||
|
|
VOICE_CALL_TTS_MODEL=cosyvoice-v2
|
|||
|
|
VOICE_CALL_TTS_VOICE=longxiaochun_v2
|
|||
|
|
VOICE_CALL_TTS_FORMAT=mp3
|
|||
|
|
|
|||
|
|
# ASR 配置
|
|||
|
|
VOICE_CALL_ASR_MODEL=paraformer-v2
|
|||
|
|
VOICE_CALL_ASR_SAMPLE_RATE=16000
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 数据库表
|
|||
|
|
|
|||
|
|
需要以下表:
|
|||
|
|
- `fa_lover` - 恋人信息(personality_prompt, voice_id)
|
|||
|
|
- `fa_voice_library` - 音色库(voice_code, gender, is_default)
|
|||
|
|
- `fa_user` - 用户信息(nickname, gender)
|
|||
|
|
|
|||
|
|
## 📊 性能指标
|
|||
|
|
|
|||
|
|
### 预期处理时间
|
|||
|
|
|
|||
|
|
| 步骤 | 时间 | 说明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| 录音 | 2-5秒 | 用户说话时间 |
|
|||
|
|
| 上传 OSS | 0.5-1秒 | 取决于网络 |
|
|||
|
|
| ASR 识别 | 2-5秒 | DashScope 处理 |
|
|||
|
|
| LLM 生成 | 1-3秒 | 取决于回复长度 |
|
|||
|
|
| TTS 合成 | 1-2秒 | 取决于文本长度 |
|
|||
|
|
| 下载播放 | 0.5-1秒 | 取决于网络 |
|
|||
|
|
| **总计** | **7-17秒** | 完整对话周期 |
|
|||
|
|
|
|||
|
|
### 优化建议
|
|||
|
|
|
|||
|
|
1. **流式处理** - LLM 和 TTS 可以流式返回,边生成边播放
|
|||
|
|
2. **缓存** - 常见回复可以预先合成并缓存
|
|||
|
|
3. **并行处理** - 某些步骤可以并行执行
|
|||
|
|
4. **CDN 加速** - 使用 CDN 加速音频传输
|
|||
|
|
|
|||
|
|
## 🎯 用户体验流程
|
|||
|
|
|
|||
|
|
### 理想的对话体验
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
用户: [按住按钮] "今天天气怎么样?" [松开]
|
|||
|
|
↓
|
|||
|
|
界面: "对话处理中..."
|
|||
|
|
↓ (3-5秒)
|
|||
|
|
界面: "你说: 今天天气怎么样?"
|
|||
|
|
↓ (1秒)
|
|||
|
|
界面: "AI: 今天天气很好哦,阳光明媚,适合出去走走~"
|
|||
|
|
↓
|
|||
|
|
[播放 AI 语音]
|
|||
|
|
↓
|
|||
|
|
用户: [听完后继续对话]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🐛 常见问题
|
|||
|
|
|
|||
|
|
### 1. 识别结果为空
|
|||
|
|
|
|||
|
|
**原因**:
|
|||
|
|
- 录音时间太短
|
|||
|
|
- 环境噪音太大
|
|||
|
|
- 没有说话内容
|
|||
|
|
|
|||
|
|
**解决**:
|
|||
|
|
- 确保录音时长 > 1 秒
|
|||
|
|
- 在安静环境测试
|
|||
|
|
- 靠近麦克风说话
|
|||
|
|
|
|||
|
|
### 2. AI 回复太慢
|
|||
|
|
|
|||
|
|
**原因**:
|
|||
|
|
- 网络延迟
|
|||
|
|
- LLM 生成时间长
|
|||
|
|
- TTS 合成时间长
|
|||
|
|
|
|||
|
|
**解决**:
|
|||
|
|
- 优化网络连接
|
|||
|
|
- 使用更快的模型(qwen-flash)
|
|||
|
|
- 限制回复长度
|
|||
|
|
|
|||
|
|
### 3. 语音播放失败
|
|||
|
|
|
|||
|
|
**原因**:
|
|||
|
|
- 音频格式不支持
|
|||
|
|
- 临时文件写入失败
|
|||
|
|
- 权限问题
|
|||
|
|
|
|||
|
|
**解决**:
|
|||
|
|
- 使用标准 MP3 格式
|
|||
|
|
- 检查文件系统权限
|
|||
|
|
- 添加错误处理
|
|||
|
|
|
|||
|
|
## 🚀 下一步优化
|
|||
|
|
|
|||
|
|
### 短期优化(1-2周)
|
|||
|
|
|
|||
|
|
1. **流式 TTS** - 边生成边播放,减少等待时间
|
|||
|
|
2. **对话历史** - 保存最近几轮对话,提供上下文
|
|||
|
|
3. **打断功能** - 用户可以打断 AI 的回复
|
|||
|
|
4. **情感识别** - 根据用户语气调整 AI 回复
|
|||
|
|
|
|||
|
|
### 中期优化(1-2月)
|
|||
|
|
|
|||
|
|
1. **多轮对话** - 完整的对话管理系统
|
|||
|
|
2. **个性化** - 根据用户习惯调整回复风格
|
|||
|
|
3. **语音克隆** - 支持用户自定义音色
|
|||
|
|
4. **实时对话** - WebSocket 实时双向通信
|
|||
|
|
|
|||
|
|
### 长期优化(3-6月)
|
|||
|
|
|
|||
|
|
1. **多模态** - 结合图像、视频的对话
|
|||
|
|
2. **情感分析** - 深度理解用户情绪
|
|||
|
|
3. **主动对话** - AI 主动发起话题
|
|||
|
|
4. **场景化** - 不同场景的专属对话模式
|
|||
|
|
|
|||
|
|
## 📝 测试清单
|
|||
|
|
|
|||
|
|
### 功能测试
|
|||
|
|
|
|||
|
|
- [ ] 录音功能正常
|
|||
|
|
- [ ] ASR 识别准确
|
|||
|
|
- [ ] LLM 回复合理
|
|||
|
|
- [ ] TTS 语音自然
|
|||
|
|
- [ ] 播放功能正常
|
|||
|
|
- [ ] 错误处理完善
|
|||
|
|
|
|||
|
|
### 性能测试
|
|||
|
|
|
|||
|
|
- [ ] 响应时间 < 15秒
|
|||
|
|
- [ ] 音频质量良好
|
|||
|
|
- [ ] 内存占用合理
|
|||
|
|
- [ ] 网络流量可控
|
|||
|
|
|
|||
|
|
### 兼容性测试
|
|||
|
|
|
|||
|
|
- [ ] iOS 设备
|
|||
|
|
- [ ] Android 设备
|
|||
|
|
- [ ] 不同网络环境
|
|||
|
|
- [ ] 不同音色
|
|||
|
|
|
|||
|
|
## 📞 技术支持
|
|||
|
|
|
|||
|
|
如遇问题,请提供:
|
|||
|
|
1. 完整的前端控制台日志
|
|||
|
|
2. 后端服务器日志
|
|||
|
|
3. 录音文件信息
|
|||
|
|
4. 网络环境
|
|||
|
|
5. 设备信息
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**最后更新**: 2026-03-05
|
|||
|
|
**版本**: v2.0 - 完整语音对话
|