# WebSocket 智能语义分析服务 - 长期记忆增强版

## 项目概述

这是一个基于 WebSocket 和 Agent-to-Agent (A2A) 架构的智能语音识别服务，具备说话人识别和长期记忆功能。系统能够在同一场会议中，通过学习每个说话人的发言特征，持续提高说话人识别的准确率。

## 核心特性

### 1. 实时语音识别处理
- 接收前端音频流，转发到 ASR 引擎
- 支持双通道模式（2pass）和 PCM 音频格式
- 实时推送识别结果到前端

### 2. 多 Agent 协作架构
- **AuditorAgent**: 判断说话人是否完成发言
- **ProfilerAgent**: 识别说话人身份（支持长期记忆）

### 3. 长期记忆功能 ⭐
- 为每个说话人建立特征档案
- 持续学习说话人的发言风格和用词习惯
- 利用会议历史上下文提高识别准确率

### 4. 完善的日志系统 📝
- 自动创建日志目录，分级记录运行信息
- 控制台输出：INFO 级别（运行状态）
- 完整日志文件：DEBUG 级别（详细调试信息）
- 错误日志文件：ERROR 级别（错误和异常）
- 所有日志包含时间戳和上下文信息

---

## 日志系统说明

### 日志文件位置

程序运行时会在项目根目录下创建 `logs` 文件夹，包含以下日志文件：

```
logs/
├── a2a_wss.log          # 完整日志（DEBUG 级别）
└── a2a_wss_error.log   # 错误日志（ERROR 级别）
```

### 日志级别说明

| 级别 | 说明 | 使用场景 | 输出位置 |
|------|------|----------|----------|
| **DEBUG** | 调试信息 | 详细的程序执行流程、变量值等 | 仅日志文件 |
| **INFO** | 一般信息 | 关键业务流程、状态变更 | 控制台 + 日志文件 |
| **ERROR** | 错误信息 | 异常、错误堆栈 | 控制台 + 日志文件 + 错误日志 |

### 日志内容示例

```
2025-01-07 14:30:15 - root - INFO - 启动 WebSocket 智能语义分析服务: ws://0.0.0.0:10095
2025-01-07 14:30:15 - root - INFO - Agent 模型: Qwen3-32B
2025-01-07 14:30:15 - root - INFO - ASR 服务地址: ws://localhost:59805/ws/asr
2025-01-07 14:30:15 - root - INFO - Redis 地址: redis://localhost:6379/0
2025-01-07 14:30:20 - root - INFO - 客户端已连接
2025-01-07 14:30:20 - root - INFO - 已连接到 ASR 服务: ws://localhost:59805/ws/asr
2025-01-07 14:30:22 - root - DEBUG - 收到 ASR 识别结果: 各位好，今天我们召开月度工作总结会议...
2025-01-07 14:30:23 - root - INFO - [Auditor] 发言完成检测通过 - 会话ID: room_101
2025-01-07 14:30:23 - root - DEBUG - 获取说话人特征库 - 会话ID: room_101, 说话人数量: 0
2025-01-07 14:30:24 - root - INFO - [Profiler] 识别说话人: 主持人 - 会话ID: room_101
```

### 日志配置

日志系统在代码中自动配置（无需手动配置），包含：

1. **日志格式**：`时间 - 记录器名称 - 级别 - 消息`
2. **编码**：UTF-8（支持中文）
3. **自动创建目录**：首次运行时自动创建 `logs` 文件夹
4. **错误追踪**：ERROR 级别日志包含完整的异常堆栈信息

### 日志查看建议

- **实时监控**：`tail -f logs/a2a_wss.log`
- **查看错误**：`cat logs/a2a_wss_error.log`
- **搜索关键词**：`grep "识别说话人" logs/a2a_wss.log`
- **统计分析**：`grep "ERROR" logs/a2a_wss_error.log | wc -l`

---

## 长期记忆功能详解

### 功能说明

长期记忆功能使得系统能够在同一场会议中，记住每个说话人的发言特征，并利用这些历史信息来辅助后续的说话人识别。

### 工作流程

```
┌─────────────┐
│ 开始会议    │
└──────┬──────┘
       │
       ▼
┌─────────────────────┐
│ 接收语音片段        │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│ 累积到缓冲区        │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│ Auditor: 判断发言   │
│ 是否完成            │
└──────┬──────────────┘
       │
       ▼ (发言完成)
┌──────────────────────────────┐
│ 获取长期记忆                 │
│ (所有说话人的历史特征)       │
└──────┬───────────────────────┘
       │
       ▼
┌──────────────────────────────┐
│ Profiler: 识别说话人         │
│ - 分析会议历史记录           │
│ - 对比说话人特征库           │
│ - 判断对话流程和上下文       │
└──────┬───────────────────────┘
       │
       ▼
┌──────────────────────────────┐
│ 更新长期记忆                 │
│ (保存当前发言到特征库)       │
└──────┬───────────────────────┘
       │
       ▼
┌─────────────────────┐
│ 推送结果到前端       │
└─────────────────────┘
```

### 核心改进

#### 1. Redis 存储扩展

**新增方法**：

- `save_speaker_profile(sid, speaker, text, features)`: 保存说话人特征到长期记忆
  - 保留每个说话人最近 10 条发言记录
  - 存储发言文本片段（前 200 字符）
  - 记录时间戳和可选的特征向量

- `get_speaker_profiles(sid)`: 获取所有说话人的历史特征
  - 返回字典格式：`{说话人: [历史记录列表]}`
  - 用于 Agent 分析时参考

#### 2. ProfilerAgent 增强

**改进的识别逻辑**：

- **历史记录分析**：查看最近 15 条对话，理解会议上下文
- **对话流程分析**：判断当前发言是对之前谁的话题的回应
- **说话风格匹配**：对比每个角色的用词习惯、句式结构、语气
- **角色职责判断**：根据发言内容是否符合角色职责范围
- **话题连贯性**：当前发言与该角色历史话题的连贯性

**提示词优化**：

```python
# 增强版系统提示词包含4个分析维度：
1. 对话流程分析
2. 说话风格匹配
3. 角色职责判断
4. 话题连贯性
```

#### 3. A2AOrchestrator 集成

在 `process_asr_fragment()` 方法中集成长期记忆：

```python
# 1. 获取长期记忆（说话人特征库）
speaker_profiles = await self.redis.get_speaker_profiles(session_id)

# 2. 使用长期记忆识别说话人
speaker = await self.profiler.identify(buf, history, speaker_profiles)

# 3. 保存到长期记忆（持续学习）
await self.redis.save_speaker_profile(session_id, speaker, buf)
```

---

## 配置说明

### 环境变量

| 变量名 | 说明 | 默认值 |
|--------|------|--------|
| `TARGET_WS_URL` | ASR 服务的 WebSocket 地址 | `ws://localhost:59805/ws/asr` |
| `REDIS_URL` | Redis 连接地址 | `redis://localhost:6379/0` |
| `LLM_API_KEY` | 大语言模型 API Key | `sk-xxxx` |
| `LLM_BASE_URL` | 大语言模型服务地址 | `http://172.18.127.124:9997/v1` |
| `LLM_MODEL` | 使用的模型名称 | `Qwen3-32B` |

### 候选说话人列表

在代码中配置（`CANDIDATE_SPEAKERS`）：
```python
CANDIDATE_SPEAKERS = [
    "局长",
    "主持人",
    "商务专员",
    "市场专员",
    "控制要素席",
    "空中侦察席"
]
```

---

## Redis 数据结构

### 键命名规范

```
meeting:buffer:{session_id}           # 会话文本缓冲
meeting:history:{session_id}          # 会话历史记录
meeting:speaker_profile:{session_id}:{speaker}  # 说话人特征库
```

### 数据类型

- **缓冲区**: String (累积的未完成发言)
- **历史记录**: List (最多 20 条)
- **说话人特征**: List (每个说话人最多 10 条)

---

## 使用示例

### 安装依赖

首先确保已安装 Python 3.8+，然后安装所需依赖：

```bash
# 在项目根目录下执行
pip install -r requirements.txt
```

### 启动服务

```bash
# 在 src 目录下执行
python a2a_wss.py
```

服务将监听 `ws://0.0.0.0:10095`

### 连接和消息格式

**前端发送配置**：
```json
{
  "mode": "2pass",
  "chunk_size": [10, 10, 10],
  "wav_format": "pcm"
}
```

**前端发送音频**: 二进制 PCM 数据

**后端返回结果**：
```json
{
  "mode": "offline-speaker",
  "type": "asr_with_speaker",
  "segments": [{
    "id": 0,
    "speaker": "局长",
    "embedding": null,
    "seek": 0,
    "full_text": "今天我们讨论一下市场推广方案"
  }],
  "speaker": "局长"
}
```

---

## 长期记忆的工作示例

### 会议初期（无记忆）

```
历史记录: [暂无]
说话人特征库: [暂无]

当前发言: "各位好，今天我们召开月度工作总结会议"

识别结果: 主持人 (基于内容特征)
```

### 会议中期（有部分记忆）

```
历史记录:
[1] 主持人: 各位好，今天我们召开月度工作总结会议
[2] 局长: 本月工作总体进展顺利
[3] 商务专员: 我们完成了三个重要合同的签署
[4] 市场专员: 市场推广活动覆盖了五个新城市

说话人特征库:
- 【主持人】(发言1次): 各位好，今天我们召开月度工作总结会议
- 【局长】(发言1次): 本月工作总体进展顺利
- 【商务专员】(发言1次): 我们完成了三个重要合同的签署
- 【市场专员】(发言1次): 市场推广活动覆盖了五个新城市

当前发言: "关于下个月的计划，我们需要重点关注客户反馈"

识别结果: 市场专员
分析:
- 延续了市场专员之前的话题（市场推广）
- 符合市场专员的职责范围（客户反馈）
- 用词风格与历史记录匹配
```

### 会议后期（丰富记忆）

```
历史记录: [15条完整对话记录]

说话人特征库:
- 【局长】(发言4次): 本月工作总体进展顺利; 很好，各部门配合默契;
  这次会议很有成效; 最后强调一下安全问题
- 【主持人】(发言3次): 各位好，今天我们召开月度工作总结会议;
  下面请商务专员汇报; 时间关系，今天的会议就到这里
- 【商务专员】(发言5次): 我们完成了三个重要合同的签署;
  这三个合同总金额达500万; 客户对我们的方案很满意;
  下季度重点跟进大客户; 需要技术部门的支持
...

当前发言: "技术部门会全力支持商务工作，确保项目交付质量"

识别结果: (未出现在候选列表中，或需要进一步判断)
分析:
- 提到了"技术部门"，可能是技术相关角色
- 对之前商务专员的话题做出回应
- 风式偏向技术人员的特点（强调"质量"、"交付"）
```

---

## 性能优化

### 1. 数据限制
- 历史记录：最多保留 20 条
- 每个说话人特征：最多保留 10 条
- LLM 上下文：使用最近 15 条历史记录

### 2. 异步处理
- Agent 处理使用后台任务，不阻塞主循环
- 连接关闭时等待任务完成（最多 5 秒）

### 3. Redis 连接池
- 使用连接池提高并发性能
- 自动编码/解码 JSON 数据

---

## 技术栈

- **Python 3.8+**
- **websockets**: WebSocket 通信
- **redis**: 会话状态和长期记忆存储
- **openai**: 大语言模型调用
- **logging**: 日志记录和系统监控
- **asyncio**: 异步编程

---

## 代码结构

```
a2a_wss.py
├── 基础配置 (1-48行)
├── RedisManager 类 (52-199行)
│   ├── 会话缓冲管理
│   ├── 历史记录管理
│   └── 说话人特征库管理 ⭐新增
├── Agent 基类 (154-208行)
├── AuditorAgent 类 (211-235行)
├── ProfilerAgent 类 (238-369行) ⭐增强
│   ├── 长期记忆提示词
│   ├── 会议历史分析
│   └── 说话人特征匹配
├── A2AOrchestrator 类 (372-343行) ⭐集成
│   └── process_asr_fragment() 方法
└── WebSocket 处理 (352-450行)
```

---

## 注意事项

1. **Redis 依赖**: 确保 Redis 服务正常运行
2. **LLM 可用性**: 确保大语言模型服务可访问
3. **内存管理**: 长时间运行的会议会累积数据，建议定期清理
4. **识别准确率**: 长期记忆随时间累积，会议初期识别准确率可能较低

---

## 未来改进方向

1. **跨会议记忆**: 支持多场会议之间的说话人特征迁移
2. **特征工程**: 提取更多说话人特征（语速、停顿模式等）
3. **置信度评分**: 为每次识别添加置信度分数
4. **自适应学习**: 根据识别准确率动态调整特征权重
5. **多模态融合**: 结合声学特征提高识别准确率

---

## 作者和许可

本项目为智能语音识别系统的核心组件，采用 Agent-to-Agent 编排模式。

最后更新: 2025-01-07
