# Unique9-SE **Repository Path**: software-engineering-pre-project/unique9-se ## Basic Information - **Project Name**: Unique9-SE - **Description**: Unique9-SEproject - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-05 - **Last Updated**: 2025-11-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: Python ## README # ASR 语音识别服务 API 文档 基于 FunASR 的实时语音识别服务,支持流式识别和标点添加。 ## 快速开始 ### 启动服务 ```bash # 克隆项目 git clone cd unique9-se # 使用 uv 安装依赖 uv sync # 启动 ASR 服务 python -m ASR.main ``` 服务运行在 `http://localhost:30703`,访问 `/docs` 查看 API 文档。 --- ## API 接口 ### 📡 SSE 流式识别 **URL**: `GET /stream/recognize/{session_id}` **说明**: 建立 SSE 连接接收实时识别结果 #### 事件格式(与 llm_chat 统一) | 事件 | 数据格式 | 说明 | |------|---------|------| | `start` | `{"status": "connected", "timestamp": str, "session_id": str}` | 连接建立 | | `message` | `{"type": "partial/final", "text": str, "timestamp": str, "session_id": str}` | 识别结果 | | `ping` | `{"status": "ping", "timestamp": str, "session_id": str}` | 心跳(5秒) | | `end` | `{"status": "completed", "timestamp": str, "session_id": str}` | 会话结束 | | `error` | `{"code": int, "message": str, "timestamp": str, "session_id": str}` | 错误 | #### ⚠️ message 事件类型(重要) - **`type: "partial"`**: 实时识别中的部分结果(无标点) - 用于实时显示给用户 - **不要发送给 llm_chat** - **`type: "final"`**: 最终识别结果(含标点)✅ - 用户说完一句话后的完整结果 - **这个才是要发送给 llm_chat 的内容** --- ### 🎤 录音控制 #### 开始录音 ```http POST /recording/start/{session_id} ``` #### 停止录音 ```http POST /recording/stop ``` --- ## 前端调用流程 ### 1. 初始化阶段 ``` 1. 生成唯一 session_id(使用 crypto.randomUUID() 或 uuid 库) 2. 建立 SSE 连接:GET /stream/recognize/{session_id} 3. 监听 start 事件确认连接成功 ``` ### 2. 录音阶段 ``` 用户按下按钮 ↓ 调用 POST /recording/start/{session_id} ↓ 用户说话(实时识别中) ↓ 收到 partial 事件 → 实时显示文本(灰色/提示状态) ↓ 用户松开按钮 ↓ 调用 POST /recording/stop ↓ 收到 final 事件 → 显示最终结果(黑色/确认状态) ``` ### 3. 调用 llm_chat 阶段 ``` 收到 final 事件 ↓ 提取 data.text ↓ 调用 llm_chat 的 POST /chat/stream body: { "message": data.text, ← ASR 的 final 结果 "thread_id": session_id ← 使用同一个 ID } ↓ 接收 llm_chat 的 SSE 流 → 显示 AI 回复 ``` --- ## 关键要点 ### ✅ DO(应该做的) 1. **只发送 final 结果给 llm_chat** ```javascript if (data.type === 'final') { sendToLLM(data.text); // 只有这个时候才调用 } ``` 2. **使用同一个 session_id** - ASR 的 `session_id` = llm_chat 的 `thread_id` - 方便追踪完整对话流程 3. **区分显示状态** - `partial`: 灰色/虚化显示(实时反馈) - `final`: 黑色/加粗显示(确认状态) ### ❌ DON'T(不应该做的) 1. **不要发送 partial 给 llm_chat** ```javascript // ❌ 错误示范 if (data.type === 'partial') { sendToLLM(data.text); // 不要这样做! } ``` 2. **不要忽略 final 事件** - partial 只是过程,final 才是结果 - 必须等到 final 才能确定用户完整输入 --- ## 数据流向图 ``` 用户语音 ↓ ASR 服务 ↓ partial 事件(实时识别,无标点) → 前端显示(灰色) ↓ final 事件(最终结果,有标点) → 前端显示(黑色) ↓ ↓ 提取 final.text ↓ llm_chat 服务 ↓ AI 回复(流式) ``` --- ## 事件处理伪代码 ```javascript // 1. 建立 SSE 连接 eventSource = new EventSource('/stream/recognize/{session_id}') // 2. 监听 message 事件 eventSource.on('message', (event) => { data = JSON.parse(event.data) if (data.type === 'partial') { // 实时显示(灰色) showRealtimeText(data.text) } else if (data.type === 'final') { // 显示最终结果(黑色) showFinalText(data.text) // ✅ 调用 llm_chat callLLMChat({ message: data.text, thread_id: session_id }) } }) // 3. 录音控制 onMouseDown() { POST('/recording/start/{session_id}') } onMouseUp() { POST('/recording/stop') } ``` --- ## 与 llm_chat 的对接 ### ASR 的输出(final) ```json { "type": "final", "text": "今天天气怎么样?", "timestamp": "2024-01-01T12:00:00", "session_id": "abc-123" } ``` ### 发送给 llm_chat ```http POST /chat/stream Content-Type: application/json { "message": "今天天气怎么样?", // ← 来自 ASR 的 final.text "thread_id": "abc-123" // ← 同一个 session_id } ``` --- ## 测试 ### 后端测试 ```bash # 使用测试客户端 uv run python -m ASR.client ``` ### 前端测试流程 1. 按住按钮说话 2. 观察实时文本变化(partial) 3. 松开按钮 4. 看到完整结果(final) 5. 确认 llm_chat 收到了 final.text --- ## 常见问题 **Q: 什么时候调用 llm_chat?** A: 收到 `type: "final"` 的 message 事件时。 **Q: partial 有什么用?** A: 给用户实时反馈,让用户知道系统在识别。不发送给 llm_chat。 **Q: 一次对话多次录音怎么处理?** A: - 第一次录音 → final1 → 发给 llm_chat(thread_id: xxx) - 第二次录音 → final2 → 发给 llm_chat(thread_id: xxx) - 使用同一个 thread_id,llm_chat 会记住上下文 **Q: 如何判断用户说完了?** A: 收到 `type: "final"` 事件就代表这句话说完了。 --- ## 技术支持 - ASR 模块问题:联系后端同学 - llm_chat 集成:参考 llm_chat 的 README - 问题反馈:提 Issue 或联系开发团队