diff --git a/docs/01-架构设计.md b/docs/01-架构设计.md new file mode 100644 index 0000000..f770b43 --- /dev/null +++ b/docs/01-架构设计.md @@ -0,0 +1,389 @@ +# 架构设计 + +## 项目概述 + +CamTalk 是一款**多模态实时 AI 视觉对话助手**。用户通过摄像头和麦克风与 AI 交互,AI 理解视觉场景和语音输入后,以文字和语音形式给出自然回应。 + +核心挑战在于三个维度之间的张力: + +| 维度 | 关键问题 | +|------|---------| +| 视觉理解 | 如何准确理解摄像头画面中的人物、物体、场景? | +| 语音交互 | 如何让对话像真人交流一样自然、低延迟? | +| 成本控制 | 实时视频流 + LLM 推理,如何避免账单爆炸? | + +## 系统架构 + +三层架构:**前端做轻量预处理,后端做智能编排,云端 AI 服务按需调用**。 + +```mermaid +graph TB + subgraph Browser["浏览器客户端"] + UI["UI 渲染层
React 18 + TypeScript"] + Edge["边缘预处理层
VAD / 关键帧检测"] + Media["媒体采集层
Camera / Microphone"] + end + + subgraph Gateway["Go 网关"] + WS["WebSocket Handler
连接管理 / 消息分发"] + Session["Session Manager
会话状态 / 对话历史"] + Orch["AI Orchestrator
STT→LLM→TTS 流式并行"] + Auth["Auth 模块
JWT / bcrypt"] + REST["REST API
健康检查 / 对话管理"] + Store["Store 层
Repository 接口"] + end + + subgraph AI["云端 AI 服务"] + STT["STT
Deepgram / MiMo ASR"] + LLM["LLM
GPT-4o / 通义千问"] + TTS["TTS
OpenAI TTS / MiMo TTS"] + end + + subgraph Storage["存储层"] + Mem["Memory
进程内缓存"] + Redis["Redis
会话状态"] + PG["PostgreSQL
持久化存储"] + 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
依赖注入 / 启动"] + end + + subgraph Transport["传输层"] + WSH["WebSocket Handler
连接管理 / 认证"] + APH["REST API Handlers
Auth / Conversation / Health"] + end + + subgraph Business["业务层"] + SM["Session Manager
会话生命周期"] + ORCH["Orchestrator
STT→LLM→TTS 编排"] + AS["Auth Service
注册/登录/刷新/登出"] + end + + subgraph AI_Layer["AI 服务层"] + STT_S["STT Service
Deepgram / MiMo"] + LLM_S["LLM Service
OpenAI 兼容"] + TTS_S["TTS Service
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=&conversation_id=`。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,无需硬编码端口。 diff --git a/docs/01-项目概述.md b/docs/01-项目概述.md deleted file mode 100644 index 8a5a2c9..0000000 --- a/docs/01-项目概述.md +++ /dev/null @@ -1,28 +0,0 @@ -# 项目概述 - -## 概述 - -开发一款**多模态实时对话应用**——通过摄像头与麦克风捕获用户的视觉场景与语音输入,由 AI 理解并给出自然、流畅的回应。 - -核心挑战在于三个维度之间的张力: - -| 维度 | 关键问题 | 详见 | -|------|---------|------| -| 视觉理解 | 如何准确理解摄像头画面中的人物、物体、场景? | `07-视觉理解.md` | -| 语音交互 | 如何让对话像真人交流一样自然、低延迟? | `06-语音交互.md` | -| 成本控制 | 实时视频流 + LLM 推理,如何避免账单爆炸? | `08-成本控制.md` | - -> 提升视觉精度意味着更高分辨率和更频繁的采样,但这会直接推高带宽和推理成本。架构设计需要在三者之间做好取舍。 - -## 项目目标 - -1. **用户故事规划**:明确"AI 能看、能听、能说"需要覆盖哪些场景 → `05-用户故事.md` -2. **成本控制策略**:从架构设计层面融入运营成本意识 → `08-成本控制.md` - -## 交付物 - -- 可运行的应用程序(摄像头 + 麦克风 → AI 回应) -- 设计文档,覆盖: - - 计划实现 vs 最终实现的用户故事 - - 成本控制技巧的构思 vs 实际采用的方案 - - 项目架构设计与技术选型 diff --git a/docs/03-接口文档.md b/docs/02-接口文档.md similarity index 61% rename from docs/03-接口文档.md rename to docs/02-接口文档.md index 1efe171..071a75e 100644 --- a/docs/03-接口文档.md +++ b/docs/02-接口文档.md @@ -2,11 +2,11 @@ ## 概述 -前后端通信接口定义。以 WebSocket 承载实时对话,REST 端点支撑基础运维。持久化已通过 PostgreSQL 实现,MemoryManager 支持 Write-Through 模式。 +前后端通信接口定义。以 WebSocket 承载实时对话,REST 端点支撑基础运维。持久化通过 PostgreSQL 实现,MemoryManager 支持 Write-Through 模式。 **设计原则**: - WebSocket 为主:所有对话数据走 WebSocket -- REST 为辅:仅用于健康检查、会话管理等低频操作 +- REST 为辅:仅用于健康检查、认证、对话管理等低频操作 - 接口先行:先定义契约,再填充实现——前后端可并行开发 ## 接口全景 @@ -20,7 +20,6 @@ /api/conversations/* HTTP Client <--> GET /api/conversations/:id (历史消息) /messages - HTTP Client ~~> POST/DELETE /api/sessions (已废弃,保留兼容) ``` --- @@ -34,8 +33,6 @@ | `token` | 是 | JWT access_token,缺失或无效时返回 401 | | `conversation_id` | 否 | 恢复已有对话;省略则创建新对话 | -> 详见"REST API → WebSocket 认证变更"章节。 - ### 消息格式约定 所有 WebSocket 消息均为 JSON 文本帧,统一结构: @@ -79,7 +76,7 @@ interface ConfigMessage { tts_enabled?: boolean; // 是否开启语音合成,默认 true detail_level?: "low" | "high"; // 图像精度,默认 "low" language?: string; // 交互语言,默认 "zh-CN" - scenario?: string; // 场景模式,可选值:free_chat / interviewer / english_teacher / debate / interpreter + scenario?: string; // 场景模式:free_chat / interviewer / english_teacher / debate / interpreter }; } ``` @@ -175,7 +172,7 @@ interface TTSAudioMessage { | 属性 | 值 | 说明 | |------|------|------| -| 编码 | `audio/mp3`(MP3) | 浏览器 `