# WebSocket服务器API文档 ## 概述 心镜Agent WebSocket服务器实现了2.1(异常状态触发对话)和2.3(双向音频流对话)两个核心接口,用于与K230设备进行实时双向通信。 ## 启动服务器 ```bash cd /Users/dsw/workspace/now/2025/wds/IntuitionX/agent python src/MainServices.py ``` 默认监听地址:`ws://0.0.0.0:8765` ## 接口详解 ### 2.1 异常状态触发对话 **用途**: K230检测到皮肤状态差或悲伤情绪时,触发Agent主动关怀对话 **流程**: 1. K230发送异常状态请求(带trigger_reason) 2. 后端返回确认响应 3. 拼接相应的提示词(针对poor_skin或sad_emotion) 4. 流式调用LLM生成文本回复 5. 流式调用TTS合成语音 6. 发送音频块到K230(base64编码) #### K230 → 后端 ```json { "type": "abnormal_trigger", "trigger_reason": "poor_skin | sad_emotion", "enable_streaming": true, "context_data": { "emotion": "sad", "skin_status": { "acne": true, "dark_circles": true }, "timestamp": "2024-01-01 12:30:45" } } ``` **字段说明**: - `type`: 固定值 "abnormal_trigger" - `trigger_reason`: - `"poor_skin"`: 皮肤状态差(痘痘、黑眼圈等) - `"sad_emotion"`: 悲伤情绪 - `enable_streaming`: 是否启用流式响应(推荐true) - `context_data`: 可选,提供给LLM的上下文信息 #### 后端 → K230(响应) **初始确认**: ```json { "type": "abnormal_trigger_response", "success": true } ``` **音频流(流式发送)**: ```json { "type": "audio_stream_download", "session_id": "abnormal_trigger", "data": "base64-encoded-audio", "is_final": false } ``` **字段说明**: - `data`: base64编码的PCM音频数据(24kHz采样率,16bit) - `is_final`: 是否为最后一个音频块 #### 提示词策略 **poor_skin(皮肤状态差)**: > 检测到用户皮肤状态不佳(可能有痘痘、黑眼圈等)。 > 请用温柔、关心的语气,简短地(1-2句话)询问用户最近是否休息不好,或提供简单的护肤建议。 > 不要说教,语气要像朋友般温暖。 **sad_emotion(悲伤情绪)**: > 检测到用户情绪低落或悲伤。 > 请用温暖、共情的语气,简短地(1-2句话)表达你察觉到了用户的情绪,询问是否遇到了困扰。 > 不要追问细节,语气要温柔、理解、不带评判。 --- ### 2.3 双向音频流对话 **用途**: K230和Agent后端通过同一WebSocket连接实现实时音频双向传输 **流程**: 1. K230发送初始化请求,进行握手 2. K230持续发送音频流 3. 后端使用VAD检测用户停止说话 4. 调用ASR识别音频 5. 调用LLM生成回复 6. 流式调用TTS合成音频 7. 发送音频到K230 8. 循环处理 #### 握手阶段 **K230 → 后端(初始化)**: ```json { "type": "audio_stream_init", "session_id": "unique-session-id", "audio_config": { "sample_rate": 16000, "bit_depth": 16, "channels": 1, "encoding": "pcm" }, "timestamp": "2024-01-01 12:30:45" } ``` **后端 → K230(响应)**: ```json { "type": "audio_stream_init_response", "success": true, "message": "音频流连接已建立", "timestamp": "2024-01-01 12:30:45" } ``` #### 音频上传阶段 **K230 → 后端(上行音频流)**: ```json { "type": "audio_stream_upload", "session_id": "unique-session-id", "data": "base64-encoded-audio", "timestamp": "2024-01-01 12:30:45", "sequence": 1 } ``` **字段说明**: - `data`: base64编码的PCM音频数据 - `sequence`: 音频块序列号(用于排序) #### 音频回复阶段 **后端 → K230(下行音频流)**: ```json { "type": "audio_stream_download", "session_id": "unique-session-id", "data": "base64-encoded-audio", "timestamp": "2024-01-01 12:30:46", "is_final": false } ``` #### 控制消息 **K230 → 后端(控制)**: ```json { "type": "audio_stream_control", "session_id": "unique-session-id", "action": "pause | resume | end", "reason": "optional reason", "timestamp": "2024-01-01 12:30:47" } ``` **action 说明**: - `"pause"`: 暂停音频处理 - `"resume"`: 恢复音频处理 - `"end"`: 结束会话,清理资源 --- ## 实现细节 ### 核心模块集成 | 模块 | 功能 | 集成方式 | |------|------|---------| | LLM | 流式文本生成 | StreamingLLM.chat() 返回生成器 | | TTS | 流式语音合成 | StreamingTTS.stream_from_generator() 支持双向流 | | ASR | 语音识别 | 需要临时WAV文件 | | VAD | 语音活动检测 | 实时检测语音开始/结束 | ### 异步架构 - **WebSocket层**: 完全异步(asyncio) - **核心模块**: 同步阻塞(asyncio.to_thread桥接) - **优点**: WebSocket能够及时处理连接事件,模块处理在线程池中执行 ### 音频处理 **采样率转换**: - K230发送: 16kHz PCM - LLM处理: 文本 - TTS输出: 24kHz PCM(自动转换,由TTS模块处理) - K230接收: 24kHz PCM **临时文件**: - ASR需要文件路径 - 使用 `tempfile.NamedTemporaryFile` 创建临时WAV - 自动清理(with语句管理) --- ## 文件结构 ``` src/ ├── MainServices.py # WebSocket服务器主入口 ├── handlers/ │ ├── __init__.py │ ├── abnormal_trigger.py # 2.1处理器 │ └── audio_stream.py # 2.3处理器 └── utils/ └── prompts.py # 提示词管理 ``` ### 关键类和函数 #### MainServices.py ```python class WebSocketServer: """WebSocket服务器主类""" def __init__(self, host="0.0.0.0", port=8765) async def handler(self, websocket) # 消息路由 async def start() # 启动服务器 ``` #### handlers/abnormal_trigger.py ```python async def handle_abnormal_trigger(websocket, data) """处理2.1异常状态触发对话""" async def send_audio_stream(websocket, system_prompt) """执行LLM→TTS双向流并发送音频""" ``` #### handlers/audio_stream.py ```python class AudioStreamHandler: """处理单个会话的双向音频流""" async def handle_audio_upload(data) """接收和处理音频上传""" async def process_user_speech() """完整的ASR→LLM→TTS处理流程""" async def generate_and_send_response(user_text) """生成响应并发送音频""" ``` #### utils/prompts.py ```python def get_trigger_prompt(trigger_reason: str) -> str """根据触发原因获取完整提示词""" ``` --- ## 性能考虑 ### 流式处理 - LLM支持流式生成,边生成边发送给TTS - TTS支持双向流,边接收文本边合成音频 - 实现真正的低延迟对话 ### 并发处理 - 每个会话独立的AudioStreamHandler - WebSocket支持多个并发连接 - 核心模块在线程池执行,避免阻塞主循环 ### 内存优化 - 流式处理避免一次性加载整个响应 - 临时文件自动清理 - 音频块逐个发送,不存储在内存中 --- ## 错误处理 ### WebSocket层 - JSON解析错误:返回错误消息,保持连接 - 会话不存在:返回404错误 - 连接断开:自动清理session资源 ### 模块层 - LLM错误:捕获异常,记录日志,返回错误消息 - ASR错误:记录日志,跳过处理 - TTS错误:捕获异常,返回错误消息 ### 日志 - 所有操作都有[标签]标记日志,便于调试 - 格式: `[标签] 消息` --- ## 测试 ### 运行测试 ```bash # 启动服务器 python src/MainServices.py # 在另一个终端运行测试 python test_ws.py ``` ### 测试覆盖 - 2.1 异常状态触发对话(poor_skin) - 2.3 双向音频流初始化和握手 - 音频流上传(虚拟数据) - 连接管理和清理 --- ## 常见问题 ### Q: 为什么2.3没有音频响应? A: 测试使用的是虚拟PCM数据(全零),VAD无法检测到有效的语音,所以不会触发ASR→LLM→TTS流程。使用真实音频数据(包含语音)即可获得响应。 ### Q: 音频格式要求? A: - **输入**: PCM格式,16bit采样,16kHz采样率,单声道 - **输出**: PCM格式,16bit采样,24kHz采样率,单声道 ### Q: 如何确保音频流的顺序? A: - 2.3使用sequence字段标记顺序 - VAD确保完整的语音段被累积后再处理 - TTS按顺序发送音频块 ### Q: 能否同时运行多个2.1会话? A: 可以,但需要顺序处理(一个接一个)。WebSocket支持并发连接,但每个连接的处理是顺序的。 ### Q: 临时文件会自动删除吗? A: 是的,使用pathlib.Path.unlink()在ASR完成后立即删除。 --- ## 部署建议 ### 生产环境 1. 使用反向代理(nginx)处理负载均衡 2. 配置HTTPS(WSS)加密连接 3. 监控日志和性能指标 4. 设置合理的超时和重连机制 ### 开发环境 1. 本地启动服务器 2. 使用test_ws.py进行功能测试 3. 查看[标签]日志输出调试 --- ## 相关文档 - README.md - 项目总体介绍 - src/Module/llm/llm.py - LLM模块文档 - src/Module/tts/tts.py - TTS模块文档 - src/Module/asr/asr.py - ASR模块文档 - src/Module/vad/vad.py - VAD模块文档