Files
CamTalk/docs/04-技术选型.md
hhs dca37f3e48 docs: 同步文档与代码实现状态
- 02-系统架构: Redis/PostgreSQL 标注已实现,模块表新增 Auth/Store/Migrations,更新表设计和前端组件
- 03-接口文档: config 新增 scenario 字段,Manager 接口补全 UpdateTitle/ListByUser,配置结构体同步,扩展接口替换为实际 Repository
- 04-技术选型: 持久化层标注已实现
- 06-语音交互: TTS Voice 更正为 mimo_default
- 11-持久化与用户系统设计: 所有 Phase 标记完成
- PLAN_BACKEND/PLAN_USER_MODULE: 标记完成状态
- README: 新增实现状态总览,补充文档索引
2026-06-19 14:58:43 +08:00

303 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 技术选型
## 概述
本文档记录项目中各项技术的**选型过程、替代方案对比和决策理由**。技术选型没有"绝对正确",只有"更适合"。
**定位**本文档记录各项技术的选型过程和决策理由。AI 服务栈、持久化层、认证系统均已实现并通过配置灵活切换。前端边缘处理已确定技术栈。
```
技术选型
├── AI 服务栈(✅ 已实现)
│ ├── STT: Deepgram默认 / MiMo ASR
│ ├── LLM: GPT-4o默认 / 通义千问等 OpenAI 兼容模型
│ └── TTS: OpenAI TTS默认 / MiMo TTS
├── 持久化层(✅ 已实现)
│ ├── 数据库: PostgreSQLpgx/v5手写 SQL
│ ├── 迁移: 嵌入式 SQL 文件,自动执行
│ └── 存储模式: Memory默认+ Write-Through 到 PG / Redis可切换
├── 认证与用户系统(✅ 已实现)
│ ├── 认证方案: JWT (HS256), access 15min + refresh 7day
│ ├── JWT 库: golang-jwt/jwt/v5
│ ├── 密码哈希: bcrypt
│ ├── 数据库驱动: pgx/v5手写 SQL不用 ORM
│ └── 前端 Token 存储: localStorage
└── 前端边缘处理层(✅ 已实现)
├── 关键帧检测: Canvas 像素比较160x120 降采样)
├── 语音检测: @ricky0123/vad-web
└── 媒体采集: MediaDevices API
```
---
## 一、AI 服务栈选型
### STT语音识别
| 方案 | 延迟 | 成本 | 特点 |
|------|------|------|------|
| **Deepgram**(默认) | <500ms | 按分钟计费 | 流式识别延迟极低WebSocket 接口 |
| **MiMo ASR**(小米) | ~1s | 按量计费 | 国产替代,兼容 OpenAI chat/completions 格式HTTP 非流式 |
| Whisper API | 1-3s | 按分钟计费 | 准确率高,支持多语言 |
| FunASR | <500ms | 自部署免费 | 阿里开源,中文优化 |
当前默认使用 Deepgram nova-2可通过 `ai.stt.provider` 配置切换到 MiMo ASR。
### LLM多模态大模型
| 方案 | 成本 | 特点 |
|------|------|------|
| **GPT-4o**(默认) | $2.5/1M tokens | 视觉理解能力强API 成熟,流式推理 |
| 通义千问 qwen3-vl-plus | 按量计费 | 阿里云,通过 OpenAI 兼容接口调用 |
| Claude Sonnet | $3/1M tokens | Anthropic长上下文能力强 |
代码通过 OpenAI 兼容接口调用,可灵活切换到任何兼容服务商。配置 `ai.llm.provider``ai.llm.model``ai.llm.endpoint` 即可。
### TTS语音合成
| 方案 | 成本 | 特点 |
|------|------|------|
| **OpenAI TTS**(默认) | $15/1M 字符 | 音质自然,支持流式,默认模型 tts-1语音 alloy |
| MiMo TTS小米 | 按量计费 | 国产替代,通过配置切换 |
当前默认使用 OpenAI TTStts-1, alloy可通过 `ai.tts.provider` 配置切换。
---
## 二、持久化层选型(已实现)
### 数据特征分析
| 数据 | 结构特征 | 读写模式 | 数据量级 |
|------|---------|---------|---------|
| 对话消息 | 强结构化 | 写多读少,按会话聚合读取 | 中(每用户日均 ~100 条) |
| 会话元信息 | 强结构化 | 写少读少 | 低 |
| 对话上下文 | 半结构化 JSON | 高频读写TTL 过期 | 低(仅当前窗口) |
| 用量统计 | 强结构化 | 写多,定期聚合读 | 低(日粒度汇总后很小) |
| 用户偏好 | 强结构化 KV | 写极少读少 | 极低 |
| 关键帧图像 | 非结构化二进制 | 写少,按需读 | 大(单张 100KB~1MB |
核心数据(对话、会话、统计)都是**强结构化**的,关系型数据库天然适配。"对话上下文"是半结构化 JSON需要数据库对 JSON 有良好支持。
### 候选方案对比
| 维度 | PostgreSQL | MySQL | SQLite | MongoDB | TiDB |
|------|-----------|-------|--------|---------|------|
| 数据模型 | 关系型 + JSONB | 关系型 | 关系型(嵌入式) | 文档型BSON | 关系型(分布式) |
| JSON 支持 | JSONB 原生索引 | JSON 类型,索引弱 | 无原生支持 | 天生擅长 | 兼容 MySQL JSON |
| 关联查询 | 强 | 强 | 强 | 弱(需 $lookup | 强 |
| 聚合统计 | 窗口函数/CTE | 基础聚合 | 基础聚合 | 聚合管道 | 强 |
| 并发能力 | 高MVCC | 中 | 低(单写锁) | 高 | 极高(分布式) |
| Go 生态 | pgx / GORM | go-sql-driver | go-sqlite3 | mongo-go-driver | 兼容 MySQL 驱动 |
### 淘汰理由
**SQLite** — 写锁是全局的,并发写入会频繁锁等待。多个 Go Gateway 实例无法共享同一 SQLite 文件。适合单机桌面应用,不适合 Web 服务。
**MySQL** — JSON 类型索引能力弱,无法对 JSON 内部字段高效查询。缺少 `gen_random_uuid()` 等原生函数。如果团队只熟悉 MySQLMVP 阶段完全可用,后续复杂查询会比 PostgreSQL 麻烦。
**MongoDB**`messages` 需按 `session_id` 关联 `sessions`MongoDB 中要用 `$lookup`,写法复杂且性能不如 SQL JOIN。用量统计的"按天聚合"用 SQL 一句话搞定MongoDB 聚合管道代码量多 3-5 倍。
**TiDB** — 部署复杂(至少 3 PD + 3 TiKV + 2 TiDB单机 PostgreSQL 完全够用,过度设计。
### 选择 PostgreSQL 的理由
| 项目需求 | PostgreSQL 匹配点 |
|---------|------------------|
| 强结构化数据 | 原生关系型SQL 标准完备 |
| JSON 半结构化 | JSONB 支持索引、路径查询、部分更新 |
| messages ↔ sessions 关联 | 完整的 FK 约束 + JOIN |
| 用量按天/周/月聚合 | 窗口函数、CTE、`DATE_TRUNC` |
| Go 后端对接 | pgx 驱动性能优秀GORM/Ent 支持成熟 |
| 未来全文搜索 | 内置 `tsvector`,无需额外引入 ES |
### Go 集成示例
```go
import "github.com/jackc/pgx/v5/pgxpool"
pool, _ := pgxpool.New(ctx, "postgres://user:pass@localhost:5432/vision_ai")
func SaveMessage(ctx context.Context, pool *pgxpool.Pool, msg *Message) error {
_, err := pool.Exec(ctx,
`INSERT INTO messages (session_id, role, content, image_url, tokens_used)
VALUES ($1, $2, $3, $4, $5)`,
msg.SessionID, msg.Role, msg.Content, msg.ImageURL, msg.TokensUsed,
)
return err
}
func GetWeeklyUsage(ctx context.Context, pool *pgxpool.Pool, userID string) ([]UsageRow, error) {
rows, _ := pool.Query(ctx,
`SELECT date, llm_tokens, estimated_cost
FROM usage_daily
WHERE user_id = $1 AND date >= CURRENT_DATE - INTERVAL '7 days'
ORDER BY date`, userID)
defer rows.Close()
// ... scan rows
}
```
JSONB 包容查询:
```sql
SELECT id, content, created_at
FROM messages
WHERE role = 'user'
AND content @> '{"text": "花"}'
ORDER BY created_at DESC
LIMIT 20;
```
### 冷热分离架构
```
Go Gateway
├── 写入路径 → Redis实时会话状态
│ → PostgreSQL对话历史 + 用量)
└── 读取路径 → Redis当前上下文
→ PostgreSQL历史记录
```
建议异步写入——实时消息先写 Redis异步批量刷入 PostgreSQL不影响对话体验。
### 决策流程
```
需要持久化?
├── 否 → 继续用 Redis
└── 是 → 数据强结构化?
├── 否, 高度嵌套 → 考虑 MongoDB
└── 是 → 数据量级?
├── < 100GB, 单机可扛 → PostgreSQL
├── 海量, 需水平扩展 → TiDB / CockroachDB
└── 极小, 单文件即可 → SQLite
```
---
## 二、前端边缘处理层选型
### 总览
| 能力 | 当前选型 | 选择理由 |
|------|---------|---------|
| 边缘推理 | ONNX Runtime Web | 通用推理引擎模型无关WASM 加速 |
| 语音检测 | @ricky0123/vad-web | 包装原生 WebRTC VAD零延迟体积极小 |
| 媒体采集 | MediaDevices API | 浏览器原生接口,无中间层,零依赖 |
### 边缘推理ONNX Runtime Web
| 方案 | 特点 | 适用场景 |
|------|------|---------|
| **ONNX Runtime Web** | 通用推理引擎,支持任意 ONNX 模型WASM 加速 | 自定义模型 pipeline |
| TensorFlow.js | Google 生态WebGL/WebGPU 加速 | 模型本身就是 TF 格式 |
| MediaPipe | 开箱即用 CV 任务 | 只需常见 CV 任务,不需自定义模型 |
| Transformers.js | HuggingFace 生态 | 快速集成预训练模型 |
项目需要同时跑 VAD 和关键帧检测两种自定义模型。ONNX 是跨框架通用格式,核心优势是**模型无关**。
### 语音检测:@ricky0123/vad-web
| 方案 | 特点 | 适用场景 |
|------|------|---------|
| **@ricky0123/vad-web** | 基于 WebRTC VAD~100KB 含 WASM纯前端零延迟 | "有没有人说话"二分类 |
| Web Audio API + 能量检测 | AnalyserNode 计算 RMS | 极简但不抗噪 |
| Silero VAD (ONNX) | 神经网络级 VAD | 嘈杂环境需更精准 |
| Picovoice Porcupine | 商业级唤醒词引擎 | 需要唤醒词功能 |
vad-web 是"够用且最轻"的平衡点——直接包装浏览器原生 WebRTC VAD 算法。
### 媒体采集MediaDevices API
`navigator.mediaDevices.getUserMedia()` 是所有浏览器音视频采集的**唯一标准入口**。所有上层封装库底层都是调这个 API。项目需要原始 MediaStream用封装库反而要多一层解包。
### 选型共同逻辑
三个技术选择的共同决策模式——**选择最薄的抽象层**
| 技术 | "最薄"体现在 |
|------|------------|
| ONNX Runtime Web | 不绑定特定框架,模型格式通用 |
| @ricky0123/vad-web | 包装原生 WebRTC VAD没有多余的模型加载 |
| MediaDevices API | 直接用浏览器原生接口,不加封装层 |
与"前端做轻量预处理"原则一致:前端层只需采集和判断"有没有值得发给后端的数据"。
---
## 四、认证与用户系统选型
### 总览
| 能力 | 选型 | 选择理由 |
|------|------|---------|
| 认证方案 | JWT (HS256) | 无状态,分布式友好,实现简单 |
| JWT 库 | golang-jwt/jwt/v5 | 社区主流v5 活跃维护 |
| 密码哈希 | bcrypt | Go 标准库直接可用,安全性足够 |
| 数据库驱动 | pgx/v5 | Go 生态性能最优的 PostgreSQL 驱动 |
| 数据库迁移 | 手写 SQL | MVP 阶段足够,后续可引入 golang-migrate |
### 认证方案JWT
| 方案 | 特点 | 适用场景 |
|------|------|---------|
| **JWT (HS256)** | 无状态 token服务端不存 session水平扩展友好 | 分布式部署、前后端分离 |
| Session + Cookie | 有状态,服务端存 session通常 Redis | 传统 Web 应用、需要服务端控制会话 |
| OAuth2 | 第三方登录授权 | 需要接入微信/GitHub 等第三方登录 |
选择 JWT 的核心理由:项目架构是前后端分离 + WebSocket 长连接JWT 无需服务端维护 session 状态天然适配。HS256 对称签名足以满足安全需求,实现比 RS256 简单。
Token 策略采用 **access (15min) + refresh (7day) 双 token**access_token 短生命周期降低泄露风险refresh_token 支持无感续期。
### JWT 库golang-jwt/jwt/v5
| 方案 | 状态 | 特点 |
|------|------|------|
| **golang-jwt/jwt/v5** | 活跃维护 | dgrijalva/jwt-go 的官方继任,社区主流 |
| dgrijalva/jwt-go | 已停维护 | 原始库,不再更新 |
| lestrrat-go/jwx | 活跃 | 功能更全JWE/JWS但项目只需签名过度引入 |
v5 是 Go 生态中 JWT 的事实标准API 简洁,文档完善。
### 密码哈希bcrypt
| 方案 | 特点 | 选择理由 |
|------|------|---------|
| **bcrypt** | 自适应 cost factor抗暴力破解 | Go 标准库 `golang.org/x/crypto/bcrypt` 直接可用 |
| argon2 | 2015 年密码哈希竞赛冠军,抗 GPU/ASIC | 安全性更高,但 Go 生态库不如 bcrypt 成熟 |
| scrypt | 内存硬哈希 | 参数调优复杂bcrypt 已足够 |
bcrypt 的 `cost` 参数可随硬件升级调大,当前默认 cost=10 足够安全。
### 数据库驱动pgx/v5
| 方案 | 特点 | 适用场景 |
|------|------|---------|
| **pgx/v5** | 原生 PostgreSQL 协议实现,连接池 pgxpool性能最优 | 需要高性能、直接写 SQL |
| GORM | 全功能 ORM自动迁移、关联预加载 | 快速开发、不想写 SQL |
| Ent | Facebook 出品,类型安全的 ORM | 大型项目、强类型需求 |
| database/sql + lib/pq | 标准接口,但 lib/pq 已停维护 | 简单场景 |
项目规模不大4 张表),手写 SQL 更可控,避免 ORM 的抽象泄漏和性能黑盒。pgx 原生支持 `pgxpool` 连接池,无需额外引入。
### 数据库迁移:手写 SQL
| 方案 | 特点 | 适用场景 |
|------|------|---------|
| **手写 SQL** | 零依赖,完全可控 | 表少(<10 张)、团队小 |
| golang-migrate | CLI + 库双模式,支持版本回滚 | 表多、需要严格版本管理 |
| Atlas | 声明式迁移HCL 定义 schema | 大型项目、多环境管理 |
MVP 阶段 4 张表,手写 `schema.sql` 即可。后续表结构复杂后可引入 golang-migrate。
### 前端 Token 存储
| 方案 | 特点 | 选择理由 |
|------|------|---------|
| **localStorage** | 持久化存储刷新不丢失JS 可直接读写 | 简单直接SPA 应用标准做法 |
| httpOnly Cookie | 防 XSS 读取,但需防 CSRF | 传统 Web 应用,需额外 CSRF 防护 |
| sessionStorage | 仅当前标签页有效 | 关闭标签页需重新登录,体验差 |
JWT 存 localStorage配合请求拦截器统一附加 `Authorization: Bearer <token>` header。refresh_token 同样存 localStorage401 时自动触发刷新流程。