docs: 按功能模块重构文档结构
- 新建 01-架构设计.md:合并项目概述+系统架构+持久化设计,含 Mermaid 架构图、模块图、时序图、ER 图、部署图 - 新建 02-接口文档.md:合并接口文档+持久化 API+用户模块 API,统一格式去重 - 重编号 03~09,去掉状态标注,规划中功能标记为待实现 - 删除 PLAN_BACKEND.md、PLAN_USER_MODULE.md 及冗余文档
This commit is contained in:
389
docs/01-架构设计.md
Normal file
389
docs/01-架构设计.md
Normal file
@@ -0,0 +1,389 @@
|
||||
# 架构设计
|
||||
|
||||
## 项目概述
|
||||
|
||||
CamTalk 是一款**多模态实时 AI 视觉对话助手**。用户通过摄像头和麦克风与 AI 交互,AI 理解视觉场景和语音输入后,以文字和语音形式给出自然回应。
|
||||
|
||||
核心挑战在于三个维度之间的张力:
|
||||
|
||||
| 维度 | 关键问题 |
|
||||
|------|---------|
|
||||
| 视觉理解 | 如何准确理解摄像头画面中的人物、物体、场景? |
|
||||
| 语音交互 | 如何让对话像真人交流一样自然、低延迟? |
|
||||
| 成本控制 | 实时视频流 + LLM 推理,如何避免账单爆炸? |
|
||||
|
||||
## 系统架构
|
||||
|
||||
三层架构:**前端做轻量预处理,后端做智能编排,云端 AI 服务按需调用**。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph Browser["浏览器客户端"]
|
||||
UI["UI 渲染层<br/>React 18 + TypeScript"]
|
||||
Edge["边缘预处理层<br/>VAD / 关键帧检测"]
|
||||
Media["媒体采集层<br/>Camera / Microphone"]
|
||||
end
|
||||
|
||||
subgraph Gateway["Go 网关"]
|
||||
WS["WebSocket Handler<br/>连接管理 / 消息分发"]
|
||||
Session["Session Manager<br/>会话状态 / 对话历史"]
|
||||
Orch["AI Orchestrator<br/>STT→LLM→TTS 流式并行"]
|
||||
Auth["Auth 模块<br/>JWT / bcrypt"]
|
||||
REST["REST API<br/>健康检查 / 对话管理"]
|
||||
Store["Store 层<br/>Repository 接口"]
|
||||
end
|
||||
|
||||
subgraph AI["云端 AI 服务"]
|
||||
STT["STT<br/>Deepgram / MiMo ASR"]
|
||||
LLM["LLM<br/>GPT-4o / 通义千问"]
|
||||
TTS["TTS<br/>OpenAI TTS / MiMo TTS"]
|
||||
end
|
||||
|
||||
subgraph Storage["存储层"]
|
||||
Mem["Memory<br/>进程内缓存"]
|
||||
Redis["Redis<br/>会话状态"]
|
||||
PG["PostgreSQL<br/>持久化存储"]
|
||||
end
|
||||
|
||||
Media --> Edge
|
||||
Edge -->|"query (image+audio)"| WS
|
||||
UI <-->|"WebSocket"| WS
|
||||
WS --> Session
|
||||
WS --> Orch
|
||||
Orch --> STT
|
||||
Orch --> LLM
|
||||
Orch --> TTS
|
||||
Session --> Store
|
||||
Store --> Mem
|
||||
Store --> Redis
|
||||
Store --> PG
|
||||
REST --> Session
|
||||
WS --> Auth
|
||||
```
|
||||
|
||||
> 为什么单独加一层 Go 网关,而不是让前端直连 AI API?1)API Key 安全性;2)统一的速率限制和成本管控;3)多模型路由逻辑集中在一处便于维护。
|
||||
|
||||
## 核心交互流程
|
||||
|
||||
一次完整的"用户提问 → AI 回答"流程:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant B as 浏览器
|
||||
participant G as Go 网关
|
||||
participant S as STT
|
||||
participant L as LLM
|
||||
participant T as TTS
|
||||
|
||||
B->>B: VAD 检测到语音结束
|
||||
B->>G: query {image, audio}
|
||||
G->>S: 音频流
|
||||
S-->>G: 流式文本
|
||||
G-->>B: stt_result {text}
|
||||
|
||||
G->>L: [图像 + 文本 + 上下文]
|
||||
loop LLM 流式输出
|
||||
L-->>G: token delta
|
||||
G-->>B: llm_chunk {delta}
|
||||
end
|
||||
G-->>B: llm_done {full_text, tokens}
|
||||
|
||||
par LLM 输出的同时
|
||||
G->>G: 句子切分器检测到完整句子
|
||||
G->>T: 句子文本
|
||||
T-->>G: 音频 chunk
|
||||
G-->>B: tts_audio {audio}
|
||||
end
|
||||
G-->>B: tts_audio {final: true}
|
||||
```
|
||||
|
||||
**关键优化**:LLM 文本流和 TTS 音频流**并行推送**——客户端先逐 token 展示文字,同时 TTS 逐句子合成并推送音频,用户感知延迟大幅降低。
|
||||
|
||||
## 技术栈
|
||||
|
||||
### 前端
|
||||
|
||||
| 技术 | 选型 | 选择理由 |
|
||||
|------|------|---------|
|
||||
| 框架 | React 18 + TypeScript | 组件化开发,类型安全,生态成熟 |
|
||||
| 构建 | Vite | 开发热更新快,构建产物小 |
|
||||
| 实时通信 | WebSocket(原生 API) + 自封装连接管理 | 浏览器原生支持,封装心跳/重连/消息分发 |
|
||||
| 语音检测 | @ricky0123/vad-web | 基于 WebRTC VAD,纯前端零延迟 |
|
||||
| 媒体采集 | MediaDevices API | 浏览器原生摄像头/麦克风访问 |
|
||||
|
||||
### 后端
|
||||
|
||||
| 技术 | 选型 | 选择理由 |
|
||||
|------|------|---------|
|
||||
| 语言 | Go | 高并发 goroutine 模型,适合长连接管理 |
|
||||
| HTTP 框架 | Gin | 高性能 HTTP 路由,中间件生态成熟 |
|
||||
| WebSocket | gorilla/websocket | Go 生态最成熟的 WebSocket 库 |
|
||||
| 会话存储 | Memory(默认) / Redis | 进程内存零依赖,Redis 支持多实例部署 |
|
||||
| 持久化存储 | PostgreSQL | 对话历史、用户数据、会话元数据 |
|
||||
| 配置管理 | Viper + godotenv | 支持 YAML + .env + 环境变量覆盖 |
|
||||
| 日志 | Zap | 高性能结构化日志 |
|
||||
|
||||
### AI 服务
|
||||
|
||||
| 能力 | 默认方案 | 备选方案 |
|
||||
|------|---------|---------|
|
||||
| 多模态 LLM | GPT-4o | 通义千问等 OpenAI 兼容模型 |
|
||||
| 语音识别 STT | Deepgram | MiMo ASR(小米) |
|
||||
| 语音合成 TTS | OpenAI TTS | MiMo TTS(小米) |
|
||||
|
||||
> Go 网关的 AI 服务层统一封装不同服务商的调用接口,通过配置切换 provider。
|
||||
|
||||
## 后端模块
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph Entry["入口层"]
|
||||
Main["main.go<br/>依赖注入 / 启动"]
|
||||
end
|
||||
|
||||
subgraph Transport["传输层"]
|
||||
WSH["WebSocket Handler<br/>连接管理 / 认证"]
|
||||
APH["REST API Handlers<br/>Auth / Conversation / Health"]
|
||||
end
|
||||
|
||||
subgraph Business["业务层"]
|
||||
SM["Session Manager<br/>会话生命周期"]
|
||||
ORCH["Orchestrator<br/>STT→LLM→TTS 编排"]
|
||||
AS["Auth Service<br/>注册/登录/刷新/登出"]
|
||||
end
|
||||
|
||||
subgraph AI_Layer["AI 服务层"]
|
||||
STT_S["STT Service<br/>Deepgram / MiMo"]
|
||||
LLM_S["LLM Service<br/>OpenAI 兼容"]
|
||||
TTS_S["TTS Service<br/>OpenAI / MiMo"]
|
||||
end
|
||||
|
||||
subgraph Data["数据层"]
|
||||
UR["UserRepository"]
|
||||
MR["MessageRepository"]
|
||||
SR["SessionRepository"]
|
||||
end
|
||||
|
||||
Main --> WSH
|
||||
Main --> APH
|
||||
Main --> SM
|
||||
Main --> ORCH
|
||||
Main --> AS
|
||||
|
||||
WSH --> SM
|
||||
WSH --> ORCH
|
||||
APH --> SM
|
||||
APH --> AS
|
||||
ORCH --> STT_S
|
||||
ORCH --> LLM_S
|
||||
ORCH --> TTS_S
|
||||
SM --> MR
|
||||
SM --> SR
|
||||
AS --> UR
|
||||
```
|
||||
|
||||
| 模块 | 职责 |
|
||||
|------|------|
|
||||
| WebSocket Handler | 管理客户端连接生命周期,JWT 认证,conversation_id 恢复,单播消息推送 |
|
||||
| Session Manager | 维护用户会话状态、对话历史。Memory(默认)/ Redis(可切换),30 分钟 TTL,Write-Through 到 PG |
|
||||
| AI Orchestrator | 编排 STT→LLM→TTS 流式并行管道,context 取消 + 超时控制 + 句子切分 |
|
||||
| AI Service Layer | AI 服务抽象层,多 provider 支持(Deepgram/MiMo/OpenAI 等) |
|
||||
| Auth | 用户认证与授权。JWT (HS256) 双 token 轮转,bcrypt 密码哈希,Gin 中间件 |
|
||||
| Store | 持久化存储层。UserRepository / MessageRepository / SessionRepository,内存 + PostgreSQL 双实现 |
|
||||
| REST API | 健康检查、认证、对话管理端点 |
|
||||
| Logger | Zap 结构化日志 |
|
||||
| Models | 数据模型定义 |
|
||||
| Migrations | 数据库版本化迁移,嵌入式 SQL 文件自动执行 |
|
||||
| Model Router | 根据请求类型选择 AI 模型(待实现) |
|
||||
| Rate Limiter | 令牌桶限流(待实现) |
|
||||
|
||||
## 前端组件
|
||||
|
||||
| 组件 | 职责 |
|
||||
|------|------|
|
||||
| AuthPage | 登录/注册表单 |
|
||||
| CameraManager | 摄像头流采集 |
|
||||
| MicManager | 麦克风音频采集 |
|
||||
| EdgeProcessor | VAD + 关键帧检测(Canvas 像素比较) |
|
||||
| WebSocketManager | WS 连接生命周期管理 |
|
||||
| ChatPanel | 消息展示、流式回复、文本输入、场景选择 |
|
||||
| VideoPreview | 摄像头画面预览 |
|
||||
| SessionSidebar | 左侧抽屉式对话列表(搜索、重命名、删除) |
|
||||
| ConfigPanel | 右侧抽屉式配置面板(主题、TTS 开关、detail level、语言、场景、账户) |
|
||||
| Toast | 轻量通知提示(3 秒自动消失) |
|
||||
|
||||
核心 Hook:`useVisionSession()` 封装一次完整的视觉对话会话(摄像头、VAD、WebSocket、消息状态、认证、场景模式)。
|
||||
|
||||
## 数据库设计
|
||||
|
||||
### ER 关系
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
users ||--o{ sessions : "1:N"
|
||||
users ||--o{ refresh_tokens : "1:N"
|
||||
sessions ||--o{ messages : "1:N"
|
||||
|
||||
users {
|
||||
uuid id PK
|
||||
varchar username UK
|
||||
varchar password_hash
|
||||
timestamptz created_at
|
||||
timestamptz updated_at
|
||||
}
|
||||
|
||||
sessions {
|
||||
uuid id PK
|
||||
uuid user_id FK
|
||||
varchar title
|
||||
jsonb config
|
||||
timestamptz created_at
|
||||
timestamptz updated_at
|
||||
}
|
||||
|
||||
messages {
|
||||
bigserial id PK
|
||||
uuid session_id FK
|
||||
varchar role
|
||||
text content
|
||||
integer tokens_used
|
||||
timestamptz created_at
|
||||
}
|
||||
|
||||
refresh_tokens {
|
||||
bigserial id PK
|
||||
uuid user_id FK
|
||||
varchar token_hash UK
|
||||
timestamptz expires_at
|
||||
timestamptz created_at
|
||||
}
|
||||
```
|
||||
|
||||
### 表结构
|
||||
|
||||
```sql
|
||||
-- 用户表
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
username VARCHAR(64) NOT NULL UNIQUE,
|
||||
password_hash VARCHAR(256) NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
-- 会话表
|
||||
CREATE TABLE sessions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
title VARCHAR(128) DEFAULT '新对话',
|
||||
config JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
-- 消息表
|
||||
CREATE TABLE messages (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
||||
role VARCHAR(16) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
tokens_used INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
-- 刷新令牌表
|
||||
CREATE TABLE refresh_tokens (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
token_hash VARCHAR(256) NOT NULL UNIQUE,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
```
|
||||
|
||||
### 存储策略
|
||||
|
||||
| 场景 | 存储方案 | 说明 |
|
||||
|------|---------|------|
|
||||
| 默认 | Memory(进程内) | 零依赖,快速启动。MemoryManager 支持 Write-Through 到 PG |
|
||||
| 持久化 | Memory + PostgreSQL | 通过 `storage.driver: postgres` 启用,MemoryManager 注入 PG Repository |
|
||||
| 多实例 | Redis(独立) | 通过配置切换到 RedisManager,适合多实例部署 |
|
||||
|
||||
冷热分离:Redis/Memory 存"热数据"(当前对话上下文,微秒级读写),PostgreSQL 存"冷数据"(历史记录)。MemoryManager 的 Write-Through 机制确保每次 AppendMessage 同时写入 PG,重启后可从 PG 恢复会话。
|
||||
|
||||
## 认证设计
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as 客户端
|
||||
participant G as Go 网关
|
||||
participant DB as PostgreSQL
|
||||
|
||||
Note over C,DB: 注册流程
|
||||
C->>G: POST /api/auth/register {username, password}
|
||||
G->>G: bcrypt hash 密码
|
||||
G->>DB: INSERT users
|
||||
G->>G: 生成 access_token + refresh_token
|
||||
G->>DB: 存 SHA256(refresh_token)
|
||||
G-->>C: {user, access_token, refresh_token}
|
||||
|
||||
Note over C,DB: 登录流程
|
||||
C->>G: POST /api/auth/login {username, password}
|
||||
G->>DB: 查 users by username
|
||||
G->>G: bcrypt.CompareHashAndPassword
|
||||
G->>G: 生成 token pair
|
||||
G->>DB: 存 SHA256(refresh_token)
|
||||
G-->>C: {user, access_token, refresh_token}
|
||||
|
||||
Note over C,DB: Token 刷新(轮转)
|
||||
C->>G: POST /api/auth/refresh {refresh_token}
|
||||
G->>G: 校验签名和过期
|
||||
G->>DB: 验证 hash 存在
|
||||
G->>DB: 撤销旧 refresh_token
|
||||
G->>G: 生成新 token pair
|
||||
G->>DB: 存新 refresh_token hash
|
||||
G-->>C: {access_token, refresh_token}
|
||||
```
|
||||
|
||||
**Token 策略**:access_token 15 分钟有效,refresh_token 7 天有效。refresh 时旧 token 失效(轮转),防止重放攻击。
|
||||
|
||||
**WebSocket 认证**:连接地址 `ws://host/ws?token=<access_token>&conversation_id=<uuid>`。HTTP Upgrade 前校验 token,失败返回 401。
|
||||
|
||||
## 部署架构
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
User["用户浏览器"] --> Nginx
|
||||
|
||||
subgraph Nginx["Nginx 反向代理"]
|
||||
Static["/ → 前端静态资源"]
|
||||
API["/api/* → Go Gateway"]
|
||||
WS_Proxy["/ws → Go Gateway"]
|
||||
end
|
||||
|
||||
subgraph Gateway_Pool["Go Gateway 实例"]
|
||||
G1["Gateway-1"]
|
||||
G2["Gateway-2"]
|
||||
GN["Gateway-N"]
|
||||
end
|
||||
|
||||
Nginx --> G1
|
||||
Nginx --> G2
|
||||
Nginx --> GN
|
||||
|
||||
G1 --> Redis
|
||||
G2 --> Redis
|
||||
GN --> Redis
|
||||
|
||||
G1 --> PG_DB["PostgreSQL"]
|
||||
G2 --> PG_DB
|
||||
GN --> PG_DB
|
||||
|
||||
G1 --> AI_Services["AI Services(外部 API)"]
|
||||
G2 --> AI_Services
|
||||
GN --> AI_Services
|
||||
```
|
||||
|
||||
**跨域策略**:Nginx 将前端(`/`)、REST API(`/api/*`)、WebSocket(`/ws`)统一反代到同一域名,浏览器无跨域问题。
|
||||
|
||||
**开发环境**:前端 Vite :5173 通过 `server.proxy` 转发 `/ws` 和 `/api` 到后端 :8080,无需硬编码端口。
|
||||
Reference in New Issue
Block a user