# 语音对话接口交互文档 本文档详细描述了前端 `webUI` 与后端 `server` 之间关于语音对话功能(`/voice-chat`)的交互流程、参数传递及返回值结构。 ## 1. 概述 语音对话功能允许用户录制一段语音,前端将其上传至后端。后端依次执行以下操作: 1. **STT (Speech-to-Text)**:将语音转换为文本。 2. **LLM (Large Language Model)**:将识别出的文本作为输入,获取 AI 的文本回复。 3. **TTS (Text-to-Speech)**:将 AI 的文本回复转换为语音。 最终,后端将用户识别文本、AI 回复文本及合成的语音数据一次性返回给前端。 ## 2. 前端调用 (WebUI) 前端主要涉及的文件为 `webUI/src/components/ChatBox.vue` 和 `webUI/src/utils/api.js`。 ### 2.1 调用方式 在 `ChatBox.vue` 中,当用户完成录音后,调用 `handleVoiceModeMessage` 方法,进而调用 `voiceAPI.voiceChat`。 底层通过 `uni.uploadFile` 发起 `multipart/form-data` 类型的 POST 请求。 ### 2.2 请求参数 前端向后端发送的请求包含 **文件** 和 **表单数据 (FormData)**。 * **URL**: `/api/chat/voice-chat` (由 `config.js` 中的 `VOICE_CHAT` 常量定义) * **Method**: `POST` * **Header**: `Authorization: Bearer ` * **文件部分**: * `name`: `"audio"` * `filePath`: 录音文件的本地路径 (e.g., `.aac` 或 `.wav`) * **表单数据 (FormData)**: | 参数名 | 类型 | 必填 | 说明 | 来源 | | :--- | :--- | :--- | :--- | :--- | | `sessionId` | String | 否 | 会话 ID,用于保持上下文 | `conversationId.value` | | `modelId` | Integer | 否 | 模型 ID | `characterConfig.modelId` | | `templateId` | Integer | 否 | 模板 ID | `characterConfig.templateId` | | `voiceStyle` | String | 否 | 语音风格 (用于 TTS) | `options.voiceStyle` | | `ttsConfigId` | Integer | 否 | TTS 配置 ID | `aiConfig.ttsId` | | `sttConfigId` | Integer | 否 | STT 配置 ID | `aiConfig.sttId` | | `useFunctionCall` | Boolean | 否 | 是否使用函数调用 | 默认为 `false` | ### 2.3 响应处理 前端接收到后端返回的 JSON 数据后,进行如下解析: 1. **用户文本**: 从 `sttResult.text` 获取,显示在聊天界面右侧。 2. **AI 回复**: 从 `llmResult.response` 获取,显示在聊天界面左侧。 3. **语音播放**: 优先使用 `ttsResult.audioBase64` (Base64 编码音频),如果没有则使用 `ttsResult.audioPath` (音频 URL) 进行播放。 --- ## 3. 后端处理 (Server) 后端入口为 `server/src/main/java/com/xiaozhi/controller/ChatController.java`,核心逻辑在 `ChatSessionServiceImpl.java`。 ### 3.1 接口定义 * **Controller**: `ChatController` * **Path**: `/api/chat/voice-chat` * **Consumes**: `multipart/form-data` ### 3.2 处理流程 1. **接收文件**: 后端支持字段名为 `audioFile`, `file`, 或 `audio` 的文件上传 (前端使用 `audio`)。 2. **参数解析**: 解析 `sessionId`, `modelId` 等参数。 3. **文件验证**: 检查文件大小 (1KB - 50MB)、格式 (支持 mp3, wav, m4a, aac 等) 和 MIME 类型。 4. **音频处理**: * 将上传的音频文件保存为临时文件。 * 使用 `AudioUtils` 将音频转换为 **PCM 16k 单声道** 格式(适配 STT 引擎)。 5. **业务逻辑 (`ChatSessionService.voiceChat`)**: * **STT**: 调用配置的 STT 服务识别语音,得到 `recognizedText`。 * **LLM**: 如果识别到文本,调用 `syncChat` 获取 AI 回复 `chatResponse`。 * **TTS**: 调用 TTS 服务将 AI 回复转换为语音,生成音频文件并读取为 Base64。 6. **结果封装**: 将 STT、LLM、TTS 的结果封装到 Map 中返回。 --- ## 4. 接口规范 ### 4.1 请求结构 **POST** `/api/chat/voice-chat` **Content-Type**: `multipart/form-data` **Body**: * `audio`: [二进制文件数据] * `sessionId`: "session_12345" * `modelId`: 10 * `templateId`: 6 * ... ### 4.2 响应结构 **Content-Type**: `application/json` ```json { "code": 200, "message": "语音对话成功", "data": { "sessionId": "session_12345", "timestamp": 1717660000000, // 1. STT 结果 "sttResult": { "text": "你好,请介绍一下你自己。", // 用户语音识别结果 "audioSize": 32000, "sttProvider": "vosk" }, // 2. LLM 结果 "llmResult": { "response": "你好!我是蔚AI,很高兴为你服务。", // AI 回复文本 "inputText": "你好,请介绍一下你自己。" }, // 3. TTS 结果 "ttsResult": { "audioBase64": "UklGRi...", // Base64 编码的音频数据 (用于直接播放) "audioPath": "audio/output/...", // 服务器音频文件路径 "timestamp": 1717660005000 }, // 性能统计 (耗时: ms) "sttDuration": 500, "llmDuration": 1200, "ttsDuration": 800, // 文件元数据 "originalFileName": "temp_audio.aac", "fileSize": 15000, "contentType": "audio/aac", "description": null } } ``` ### 4.3 错误响应 ```json { "code": 400, // 或 500 "message": "请求参数错误: 音频文件不能为空", "data": null } ``` ## 5. 总结 * **交互模式**: 同步一次性交互。前端发送音频,等待后端完成所有处理(识别+对话+合成)后,一次性接收所有数据。 * **音频格式**: 前端通常录制 `aac` 或 `wav`,后端统一转码为 `pcm` 进行处理。 * **回退机制**: 如果后端处理失败,前端 `ChatBox.vue` 会捕获异常并提示用户,或使用本地模拟回复(在未登录等特定情况下)。