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 - 完整语音对话
|