# 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模块文档
