Update code

This commit is contained in:
User
2026-03-12 12:47:56 +08:00
parent 92e7fc5bda
commit 9dab61345c
9383 changed files with 1463454 additions and 1 deletions

440
test2/ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,440 @@
# BigWo 智能语音对话系统 — 系统架构文档
> 版本1.0 | 更新日期2026-03-09
---
## 1. 系统概述
BigWo 是一个**企业级智能客服对话系统**,支持**语音通话**和**文字对话**两种交互模式,可无缝切换且保持上下文连续。
### 核心能力
| 能力 | 说明 |
|------|------|
| 实时语音对话 | 火山引擎 RTC + S2S 端到端语音大模型,混合编排模式 |
| 知识库问答 | Function Calling → 方舟私域知识库 → 本地知识库 |
| 文字对话 | Coze v3 Chat API支持 SSE 流式输出 |
| 语音↔文字切换 | 同一 sessionId 贯穿MySQL 持久化完整历史 |
| 工具调用 | 知识库检索、天气、订单、时间、计算 |
### 技术栈
- **前端**React 18 + Vite 5 + TailwindCSS 4 + Lucide Icons
- **后端**Node.js + Express 4 (port 3012)
- **语音**@volcengine/rtc SDK + S2S 端到端 + 方舟 LLM (API v2024-12-01)
- **文字**Coze v3 Chat API流式 SSE
- **知识库**:方舟 Chat Completions API + knowledge_base metadata
- **数据库**MySQL 8 (mysql2/promise)
- **部署**PM2 (cluster) + Nginx 反向代理 + 宝塔面板
---
## 2. 系统架构图
```
┌─────────────────────────────────────────────────────────┐
│ 浏览器客户端 │
│ ┌────────────┐ ┌───────────┐ ┌──────────────────┐ │
│ │ VoicePanel │ │ ChatPanel │ │ SettingsPanel │ │
│ └─────┬──────┘ └─────┬─────┘ └──────────────────┘ │
│ │ │ │
│ ┌─────▼──────┐ ┌─────▼─────┐ ┌────────────────┐ │
│ │useVoiceChat│ │ chatApi │ │ voiceApi │ │
│ │ (Hook) │ │ (HTTP/SSE)│ │ (HTTP 封装) │ │
│ └─────┬──────┘ └─────┬─────┘ └────────────────┘ │
│ │ │ │
│ ┌─────▼──────┐ │ │
│ │ rtcService │ │ │
│ │(WebRTC SDK)│ │ │
│ └─────┬──────┘ │ │
└────────┼───────────────┼────────────────────────────────┘
│ WebRTC │ HTTPS
│ 音频流 │ REST/SSE
▼ ▼
┌────────────────┐ ┌─────────────────────────────────────┐
│ 火山引擎 RTC │ │ Express 后端 (port 3012) │
│ 云端服务 │ │ │
│ ┌────────────┐ │ │ routes/voice.js — 语音全生命周期 │
│ │S2S 端到端 │ │ │ routes/chat.js — 文字对话 │
│ │语音大模型 │ │ │ routes/session.js — 会话管理 │
│ ├────────────┤ │ │ │
│ │方舟 LLM │ │ │ services/volcengine.js — OpenAPI │
│ │(工具决策) │ │ │ services/toolExecutor.js — 工具 │
│ └─────┬──────┘ │ │ services/cozeChatService.js — Coze │
│ │FC 回调 │ │ services/arkChatService.js — 方舟 │
│ │(HTTP) │ │ config/voiceChatConfig.js — 配置 │
│ ▼ │ │ db/index.js — MySQL │
│ fc_callback ──►│ │ │
│ ◄── Update ────│ └─────────────────────────────────────┘
└────────────────┘ │
┌─────────────────┐ ┌──────────────┐
│ MySQL 8 │ │ 方舟知识库 │
│ sessions 表 │ │ (远程 API) │
│ messages 表 │ └──────────────┘
└─────────────────┘
```
---
## 3. 目录结构
```
test2/
├── client/ # 前端React + Vite
│ ├── src/
│ │ ├── App.jsx # 主应用,语音/文字模式切换
│ │ ├── components/
│ │ │ ├── VoicePanel.jsx # 语音通话界面
│ │ │ ├── ChatPanel.jsx # 文字对话界面SSE 流式)
│ │ │ ├── SettingsPanel.jsx # 语音参数设置面板
│ │ │ └── SubtitleDisplay.jsx# 实时字幕展示
│ │ ├── hooks/
│ │ │ └── useVoiceChat.js # 语音通话核心 Hook
│ │ └── services/
│ │ ├── rtcService.js # RTC SDK 封装WebRTC
│ │ ├── voiceApi.js # 语音 HTTP 请求
│ │ └── chatApi.js # 文字 HTTP/SSE 请求
│ └── vite.config.js
├── server/ # 后端Express
│ ├── app.js # 入口 + FC 回调 raw body 解析
│ ├── routes/
│ │ ├── voice.js # 语音全生命周期 + FC 回调(核心)
│ │ ├── chat.js # 文字对话Coze
│ │ └── session.js # 会话历史 & 模式切换
│ ├── services/
│ │ ├── volcengine.js # 火山引擎 OpenAPI 签名调用
│ │ ├── toolExecutor.js # 工具执行器
│ │ ├── arkChatService.js # 方舟 LLM文字场景备选
│ │ └── cozeChatService.js # Coze Chat API文字主服务
│ ├── config/
│ │ ├── voiceChatConfig.js # StartVoiceChat 配置构建器
│ │ └── tools.js # FC 工具定义5 个工具)
│ ├── db/index.js # MySQL CRUD
│ ├── lib/token.js # RTC Token 生成
│ └── .env # 环境变量
└── ecosystem.config.js # PM2 部署配置
```
---
## 4. 语音通话模块
### 4.1 混合编排模式OutputMode=1
S2S 端到端模型处理普通闲聊(低延迟 ~300-800ms方舟 LLM 同时决策是否需要调用工具。两者并行运行。
### 4.2 会话生命周期
```
POST /prepare → 创建房间 + 生成 RTC Token + 分配 TaskId
客户端 joinRoom() → 用户进入 RTC 房间、开启麦克风
POST /start → 构建配置 → StartVoiceChat API → AI Bot 进房
实时语音对话S2S 直接回复 + LLM 工具决策)
POST /stop → StopVoiceChat API → 返回字幕 → 可切换文字模式
```
### 4.3 语音 API 端点voice.js
| 端点 | 方法 | 说明 |
|------|------|------|
| `/api/voice/config` | GET | 获取模型、音色列表 |
| `/api/voice/prepare` | POST | 创建房间、生成 Token |
| `/api/voice/start` | POST | 启动 AI 语音对话 |
| `/api/voice/stop` | POST | 停止对话、返回字幕 |
| `/api/voice/fc_callback` | POST | FC 回调RTC 服务端→服务端) |
| `/api/voice/subtitle` | POST | 客户端转发确认字幕 |
| `/api/voice/room_message` | POST | 客户端转发 RTC 房间消息 |
### 4.4 内存数据映射
voice.js 维护以下 Map 用于会话状态关联:
| Map 名称 | Key | Value | 用途 |
|----------|-----|-------|------|
| `activeSessions` | sessionId | 完整会话对象 | 会话生命周期管理 |
| `roomToBotUserId` | roomId | botUserId | FC 回调→UpdateVoiceChat |
| `roomToHumanUserId` | roomId | userId | 日志追踪 |
| `roomToSessionId` | roomId | sessionId | DB 写入关联 |
| `roomToTaskId` | roomId | taskId | **UpdateVoiceChat 必须用此 TaskId** |
| `latestUserSpeech` | roomId | {text, timestamp} | FC 参数解析兜底 |
| `toolCallBuffers` | TaskID | buffer 对象 | FC chunk 收集 |
---
## 5. Function Calling 回调处理(核心)
### 5.1 数据流
```
RTC 服务 (LLM 触发 tool_call)
│ HTTP POST无 Content-Typebody 为 JSON
app.js 手动读取 raw body → JSON.parse → 分配 _seq 序列号
voice.js fc_callback 路由
├─ FormatA: Type="tool_calls" → OpenAI 格式数组,流式 chunk
├─ FormatB: Type="information" → RTC 原生格式
└─ FormatC: 会话状态回调 → 记录日志
FormatA 为主)
chunk 缓冲收集toolCallBuffers Map1s 超时触发)
▼ 1s 后
参数解析尝试链:
① JSON.parse(拼接 chunks)
② latestUserSpeechASR 用户语音兜底)
③ extractReadableText从 chunks 提取中文字符)
发送 interrupt 打断 S2S 直接回复
│ UpdateVoiceChat({ Command: "interrupt", TaskId: sessionTaskId })
执行工具toolExecutor.js
│ search_knowledge: 方舟 KB(30s) → 本地 KB
回传结果
│ UpdateVoiceChat({
│ Command: "function",
│ TaskId: sessionTaskId, ← 必须是 StartVoiceChat 的 TaskId
│ Message: JSON.stringify({ ToolCallID, Content })
│ })
AI 用知识库内容语音回复
```
### 5.2 关键设计决策
| 问题 | 解决方案 |
|------|----------|
| FC 回调无 Content-Type | app.js 在 express.json() 之前手动读取 raw body |
| Chunk 乱序且不完整 | 1s 定时器收集全部 chunks 后拼接 |
| JSON.parse 失败 | 用 ASR 用户语音文本作为查询参数方案B |
| S2S 直接回复覆盖 FC 结果 | 先发 interrupt 打断,再发 function 结果 |
| TaskId 不匹配 | roomToTaskId 存储 StartVoiceChat 的 TaskId |
| HTTP 响应超时 | 立即返回 200异步执行工具 |
### 5.3 用户语音文本获取方案B
FC 回调的 arguments 经常乱序无法解析,因此需要从其他途径获取用户的原始问题:
```
客户端 RTC SDK
│ onSubtitleMessageReceived / onRoomBinaryMessageReceived
useVoiceChat.js
│ 转发 definite=true 的用户字幕
POST /api/voice/subtitle → latestUserSpeech.set(roomId, text)
POST /api/voice/room_message → 解析二进制消息中的字幕数据
```
---
## 6. 文字对话模块
### 6.1 架构
通过 **Coze v3 Chat API** 实现Coze Bot 内置知识库插件。
```
用户输入 → POST /api/chat/send-stream
cozeChatService.chatStream()
│ 首次对话注入语音历史作为上下文
│ Coze 自动管理 conversation_id
SSE 流式返回 → 前端逐字展示
```
### 6.2 文字 API 端点chat.js
| 端点 | 方法 | 说明 |
|------|------|------|
| `/api/chat/start` | POST | 创建会话,注入语音历史上下文 |
| `/api/chat/send` | POST | 非流式发送 |
| `/api/chat/send-stream` | POST | SSE 流式发送 |
| `/api/chat/history/:id` | GET | 获取会话状态 |
| `/api/chat/:id` | DELETE | 删除会话 |
---
## 7. 会话管理与模式切换
### 7.1 统一 sessionId
同一个 sessionId 贯穿语音和文字模式,所有消息持久化到 MySQL messages 表。
### 7.2 消息来源标记
| source 值 | 说明 |
|-----------|------|
| `voice_asr` | 语音 ASR 识别的用户文本 |
| `voice_bot` | AI 语音回复字幕 |
| `voice_tool` | 语音场景工具调用结果 |
| `chat_user` | 文字对话用户输入 |
| `chat_bot` | 文字对话 AI 回复 |
### 7.3 模式切换 APIsession.js
| 端点 | 方法 | 说明 |
|------|------|------|
| `/api/session/:id/history` | GET | 获取完整历史(支持 llm/full 格式) |
| `/api/session/:id/switch` | POST | 切换模式,返回上下文历史 |
### 7.4 数据库表
**sessions**`id(PK)`, `user_id`, `mode(voice/chat)`, `created_at`, `updated_at`
**messages**`id(AI PK)`, `session_id`, `role(user/assistant/tool/system)`, `content`, `source`, `tool_name`, `created_at`
---
## 8. 客户端组件
### 8.1 组件树
```
App.jsx # 模式切换 + 全局设置
├── VoicePanel.jsx # 语音通话 UI开始/结束/静音/时长)
│ └── SubtitleDisplay # 实时字幕definite/interim 区分)
├── ChatPanel.jsx # 文字对话 UI消息列表 + SSE 流式显示)
└── SettingsPanel.jsx # 设置面板(模型/音色/系统角色/VAD
```
### 8.2 useVoiceChat Hook
管理语音通话完整生命周期:
- **start(options)**prepare → joinRoom → startVoiceChat
- **stop()**leaveRoom → stopVoiceChat → 返回字幕
- **toggleMute()**:静音/取消静音
- **状态**isActive, isMuted, isConnecting, subtitles, duration, error
### 8.3 rtcService.js
封装 @volcengine/rtc SDK
- **init(appId)**:创建引擎、注册事件监听
- **joinRoom()**:入房 + 开始音频采集 + 启用字幕
- **事件监听**:字幕(onSubtitleMessageReceived)、房间消息(binary/text)、诊断(音量/流)
- **方案B 消息转发**:所有房间消息 → onRoomMessage 回调 → useVoiceChat → 后端
---
## 9. 环境变量
### 必需
| 变量 | 说明 |
|------|------|
| `VOLC_RTC_APP_ID` | RTC 应用 ID |
| `VOLC_RTC_APP_KEY` | RTC 应用密钥(生成 Token |
| `VOLC_ACCESS_KEY_ID` | 火山引擎 AKAPI 签名) |
| `VOLC_SECRET_ACCESS_KEY` | 火山引擎 SK |
| `VOLC_S2S_APP_ID` | S2S 端到端语音 AppID |
| `VOLC_S2S_TOKEN` | S2S Token |
| `VOLC_ARK_ENDPOINT_ID` | 方舟 LLM 推理接入点 ID |
### 可选
| 变量 | 说明 |
|------|------|
| `COZE_API_TOKEN` | Coze 智能体 Token文字对话 |
| `COZE_BOT_ID` | Coze Bot ID |
| `VOLC_ARK_KNOWLEDGE_BASE_IDS` | 方舟私域知识库数据集 ID逗号分隔 |
| `VOLC_ARK_API_KEY` | 方舟 API Key |
| `VOLC_WEBSEARCH_API_KEY` | 联网搜索 Key |
| `FC_SERVER_URL` | FC 回调地址 |
| `FC_SIGNATURE` | FC 回调签名 |
| `MYSQL_HOST/PORT/USER/PASSWORD/DATABASE` | MySQL 配置 |
---
## 10. 部署架构
```
互联网用户
▼ HTTPS (443)
┌──────────────┐
│ Nginx │ ← 宝塔面板管理
│ (反向代理) │
│ SSL 终止 │
└──────┬───────┘
│ http://localhost:3012
┌──────────────┐
│ PM2 │ ← ecosystem.config.js
│ bigwo-server│
│ (Node.js) │
└──────┬───────┘
├── MySQL 8 (localhost:3306)
├── 火山引擎 RTC API (rtc.volcengineapi.com)
├── 方舟 LLM API (ark.cn-beijing.volces.com)
└── Coze API (api.coze.cn)
```
### PM2 配置
- 进程名:`bigwo-server`
- 工作目录:`/www/wwwroot/demo.tensorgrove.com.cn/server`
- 日志路径:`/var/log/bigwo/server-out.log``server-error.log`
- 内存限制512M 自动重启
---
## 11. 工具定义tools.js
系统定义了 5 个 Function Calling 工具:
| 工具 | 参数 | 说明 |
|------|------|------|
| `search_knowledge` | query: string | **核心工具**,强制优先调用 |
| `query_weather` | city: string | 天气查询Mock |
| `query_order` | order_id: string | 订单查询Mock |
| `get_current_time` | 无 | 当前时间 |
| `calculate` | expression: string | 数学计算 |
### search_knowledge 查询链
```
方舟私域知识库 (30s 超时)
│ POST https://ark.cn-beijing.volces.com/api/v3/chat/completions
│ metadata.knowledge_base: { dataset_ids, top_k: 3, threshold: 0.5 }
│ 失败 ↓
本地知识库 (即时,关键词匹配)
│ 覆盖:退货、退款、配送、保修、会员
```
---
## 12. 安全与限制
- **API 签名**:所有火山引擎 API 调用使用 AK/SK HMAC 签名
- **FC 回调签名**ServerSignature 校验(当前信任模式)
- **计算工具防注入**:仅允许 `0-9 + - * / ( ) . %` 字符
- **CORS**:已开启(`cors()` 中间件)
- **Body 限制**1MB
- **会话过期**:文字对话 30 分钟自动清理
- **环境变量**:敏感信息存于 `.env`,启动时校验
---
## 13. 已知问题与优化方向
1. **FC 响应延迟**:用户提问到 AI 用知识库回答约需 12-15sLLM 决策 ~8s + KB 查询 ~5s期间有静默
2. **Chunk 乱序**RTC FC 回调的 tool_call arguments 被拆成单字符 chunk 且乱序,只能靠 ASR 文本兜底
3. **S2S 与 LLM 并行冲突**S2S 会先给出直接回复,需 interrupt 打断后再发 FC 结果
4. **Mock 工具**:天气和订单工具目前为 Mock 数据,可接入真实 API
5. **知识库冷启动**:方舟 KB 首次查询较慢(~10s后续查询 ~3-5s