--- tags: [AI, 架构设计, 技术栈, 端云协同, WebSocket, 微服务] create time: 2026-06-12 14:32 --- # 项目架构与技术栈 ## 概述 本文档设计 AI 视觉对话助手的**分层架构**与**技术选型**。核心设计原则:**前端做轻量预处理,后端做智能编排,云端 AI 服务按需调用**——在保证交互体验的同时控制成本。 ## 正文 ### 整体架构 ```mermaid graph TB subgraph Client["浏览器客户端"] UI["React UI"] CAM["摄像头/麦克风"] EDGE["边缘预处理"] WS_C["WebSocket Client"] end subgraph Gateway["Go 后端网关"] WS_S["WebSocket Server"] SESSION["会话管理"] ROUTER["模型路由"] ORCH["AI 编排器"] end subgraph AI["云端 AI 服务"] LLM["多模态 LLM"] STT["语音识别"] TTS["语音合成"] end CAM --> EDGE EDGE -->|"关键帧 + 语音片段"| WS_C WS_C <-->|"双向实时通信"| WS_S WS_S --> SESSION SESSION --> ROUTER ROUTER --> ORCH ORCH --> LLM ORCH --> STT ORCH --> TTS TTS -->|"音频流"| WS_S LLM -->|"文本流"| WS_S ``` 三层各司其职: | 层级 | 职责 | 关键约束 | |------|------|---------| | **客户端** | 媒体采集、边缘预处理、UI 渲染 | 浏览器资源有限,模型需轻量 | | **Go 网关** | 会话管理、模型路由、AI 服务编排 | 高并发、低延迟、状态管理 | | **AI 服务** | LLM 推理、语音识别、语音合成 | 按量计费,需控制调用频率 | > [!question] 思考 > 为什么要单独加一层 Go 网关,而不是让前端直连 AI API?原因有三:1)API Key 安全性;2)统一的速率限制和成本管控;3)多模型路由逻辑集中在一处便于维护。 ### 技术栈选型 #### 前端 | 技术 | 选型 | 选择理由 | |------|------|---------| | 框架 | **React 18 + TypeScript** | 组件化开发,类型安全,生态成熟 | | 构建 | **Vite** | 开发热更新快,构建产物小 | | 实时通信 | **WebSocket (原生 API)** | 浏览器原生支持,无需额外依赖 | | 边缘推理 | **ONNX Runtime Web** | 浏览器端跑轻量模型(VAD、关键帧检测) | | 语音检测 | **@ricky0123/vad-web** | 基于 WebRTC VAD,纯前端零延迟 | | 媒体采集 | **MediaDevices API** | 浏览器原生摄像头/麦克风访问 | #### 后端 | 技术 | 选型 | 选择理由 | |------|------|---------| | 语言 | **Go** | 高并发 goroutine 模型,适合长连接管理 | | WebSocket | **gorilla/websocket** | Go 生态最成熟的 WebSocket 库 | | 会话存储 | **Redis** | 高速 KV 存储,适合会话状态和上下文缓存 | | 配置管理 | **Viper** | 支持多格式配置,环境变量覆盖 | | 日志 | **Zap** | 高性能结构化日志 | #### AI 服务(按需选型) | 能力 | 主选方案 | 备选方案 | 选型考量 | |------|---------|---------|---------| | 多模态 LLM | **GPT-4o** | Claude Sonnet | 视觉理解能力强,API 成熟 | | 语音识别 STT | **Deepgram** | FunASR 自部署 | 流式识别延迟低(<500ms) | | 语音合成 TTS | **OpenAI TTS** | Edge TTS(免费) | 音质自然,支持流式 | | 轻量分类 | **GPT-4o-mini** | Haiku | 模型路由时的复杂度判断 | > [!tip] 混合策略 > 不必绑定单一厂商。Go 网关的模型路由层可以统一封装不同 AI 服务的调用接口,按场景动态切换。比如简单识别用 GPT-4o-mini,深度分析用 GPT-4o,TTS 用免费的 Edge TTS 降低成本。 ### 核心交互流程 一次完整的"用户提问 → AI 回答"流程: ```mermaid sequenceDiagram participant B as Browser participant G as Go Gateway participant S as STT Service participant L as LLM Service participant T as TTS Service B->>B: VAD 检测到语音开始 B->>B: 捕获当前摄像头帧 B->>G: WebSocket 发送 [音频流 + 图像帧] G->>S: 转发音频流 S-->>G: 流式返回识别文本 G->>L: 发送 [图像 + 识别文本 + 历史上下文] L-->>G: 流式返回回答文本 G-->>B: WebSocket 推送回答文本 G->>T: 发送回答文本 T-->>G: 流式返回音频 G-->>B: WebSocket 推送音频流 B->>B: 播放音频 + 渲染文字 ``` > [!info] 关键优化 > 注意 LLM 文本流和 TTS 音频流是**并行推送**的——客户端先展示文字,同时开始播放语音,用户感知延迟大幅降低。 ### 后端架构设计 Go 网关的核心模块: ```mermaid graph TD subgraph Server["Go Gateway"] WS["WebSocket Hub"] SM["Session Manager"] MR["Model Router"] AO["AI Orchestrator"] RL["Rate Limiter"] CACHE["Context Cache"] end WS --> SM SM --> MR MR --> AO SM --> RL SM --> CACHE ``` 各模块职责: | 模块 | 职责 | 关键实现 | |------|------|---------| | **WebSocket Hub** | 管理所有客户端连接,广播/定向推送 | goroutine per connection | | **Session Manager** | 维护用户会话状态、对话历史 | Redis + TTL 过期策略 | | **Model Router** | 根据请求类型选择 AI 模型 | 规则引擎 + 成本阈值 | | **AI Orchestrator** | 编排多路 AI 调用(并行/串行) | context 取消 + 超时控制 | | **Rate Limiter** | 防止单用户过度消耗 API 额度 | 令牌桶算法 | Go 后端核心代码结构: ```go // AI 编排器:并行调用 LLM 和 TTS func (o *Orchestrator) ProcessQuery(ctx context.Context, req *QueryRequest) (*QueryResponse, error) { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() // 并行:LLM 推理 + 准备 TTS llmCh := make(chan string, 1) go func() { resp, _ := o.llm.Chat(ctx, req.Image, req.Text, req.History) llmCh <- resp }() llmText := <-llmCh // LLM 返回后,流式推送给客户端,同时启动 TTS ttsCh := make(chan []byte, 1) go func() { audio, _ := o.tts.Synthesize(ctx, llmText) ttsCh <- audio }() return &QueryResponse{Text: llmText, Audio: <-ttsCh}, nil } ``` ### 前端架构设计 ```mermaid graph TD subgraph App["React App"] MAIN["App Root"] CAM_M["CameraManager"] MIC_M["MicManager"] EDGE_M["EdgeProcessor"] WS_M["WebSocketManager"] CHAT["ChatPanel"] VIDEO["VideoPreview"] end MAIN --> CAM_M MAIN --> MIC_M MAIN --> WS_M MAIN --> CHAT MAIN --> VIDEO CAM_M --> EDGE_M MIC_M --> EDGE_M EDGE_M --> WS_M ``` 核心 Hook 设计: ```typescript // useVisionSession —— 封装一次完整的视觉对话会话 function useVisionSession() { const [messages, setMessages] = useState([]); const wsRef = useWebSocket("ws://localhost:8080/ws"); // 摄像头管理 const videoRef = useRef(null); const { captureFrame } = useCamera(videoRef); // VAD 语音检测 const { isSpeaking } = useVAD({ onSpeechEnd: async (audio) => { const frame = captureFrame(); // 同时发送图像帧和语音片段 wsRef.current?.send(JSON.stringify({ type: "query", image: frame.toDataURL("image/jpeg", 0.7), audio: encodeAudio(audio) })); } }); // 接收 AI 回复(文本 + 音频) useEffect(() => { wsRef.current?.on("message", (data) => { const { text, audio } = JSON.parse(data); setMessages(prev => [...prev, { role: "assistant", text }]); if (audio) playAudio(audio); }); }, []); return { messages, videoRef, isSpeaking }; } ``` ### 部署架构 ```mermaid graph LR subgraph CDN["CDN"] STATIC["静态资源"] end subgraph LB["负载均衡"] NGINX["Nginx"] end subgraph App["应用层"] GW1["Gateway-1"] GW2["Gateway-2"] end subgraph Storage["存储层"] REDIS["Redis"] end USER["用户浏览器"] --> CDN CDN --> STATIC USER -->|"WebSocket"| NGINX NGINX --> GW1 NGINX --> GW2 GW1 --> REDIS GW2 --> REDIS GW1 -->|"API Calls"| AI["AI Services"] GW2 -->|"API Calls"| AI ``` > [!tip] WebSocket 与负载均衡 > WebSocket 是长连接,Nginx 需要配置 `proxy_set_header Upgrade` 和 `ip_hash` 或 sticky session,确保同一用户的请求始终路由到同一个 Gateway 实例。 ## 关联笔记 - [[视觉理解]] - [[语音交互]] - [[成本控制]] - [[用户故事]]