- 修复心跳 Bug:应用层 ping 不更新 lastPong,60 秒后连接会被错误断开 - 03-接口文档:audio/mpeg→audio/mp3、STTConfig/TTSConfig 补充 Model 字段、 APP_ENV 环境变量名修正、配置搜索路径补充、.env 加载说明、Vite proxy 说明修正 - 02-系统架构:补充 ConfigPanel/Toast 组件、Model Router/Rate Limiter 标注规划中、 补充 Gin 框架、MVP 存储改为 Memory、AI 服务 provider 更新、Orchestrator 伪代码对齐 - 04-技术选型:新增 AI 服务栈选型章节(STT/LLM/TTS)、PostgreSQL 标注规划中 - 06-语音交互:VAD 参数名修正、STT 改为一次性识别描述、音频编码格式补充 - 07-视觉理解:关键帧检测代码改为 TypeScript、分辨率修正、阈值逻辑统一 - 08-成本控制:变量名修正、未实现功能标注规划中、对话历史裁剪策略补充 - CLAUDE.md:同步更新技术栈、模块结构、存储策略描述
217 lines
9.2 KiB
Markdown
217 lines
9.2 KiB
Markdown
# 技术选型
|
||
|
||
## 概述
|
||
|
||
本文档记录项目中各项技术的**选型过程、替代方案对比和决策理由**。技术选型没有"绝对正确",只有"更适合"。
|
||
|
||
**定位**:持久化部分是拓展选型,不阻塞 MVP(MVP 用内存存储即可)。前端边缘处理部分是 MVP 阶段就需要确定的技术栈。AI 服务栈(STT/LLM/TTS)已确定默认选型,可通过配置灵活切换。
|
||
|
||
```
|
||
技术选型
|
||
├── AI 服务栈
|
||
│ ├── STT: Deepgram(默认) / MiMo ASR
|
||
│ ├── LLM: GPT-4o(默认) / 通义千问等 OpenAI 兼容模型
|
||
│ └── TTS: OpenAI TTS(默认) / MiMo TTS
|
||
├── 持久化层 → 数据库选型: PostgreSQL(规划中,MVP 阶段使用内存存储)
|
||
└── 前端边缘处理层
|
||
├── 边缘推理: ONNX Runtime Web(规划中,MVP 使用 Canvas 像素比较)
|
||
├── 语音检测: @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 TTS(tts-1, alloy),可通过 `ai.tts.provider` 配置切换。
|
||
|
||
---
|
||
|
||
## 二、持久化层选型(规划中,MVP 阶段使用内存存储)
|
||
|
||
### 数据特征分析
|
||
|
||
| 数据 | 结构特征 | 读写模式 | 数据量级 |
|
||
|------|---------|---------|---------|
|
||
| 对话消息 | 强结构化 | 写多读少,按会话聚合读取 | 中(每用户日均 ~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()` 等原生函数。如果团队只熟悉 MySQL,MVP 阶段完全可用,后续复杂查询会比 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 | 直接用浏览器原生接口,不加封装层 |
|
||
|
||
与"前端做轻量预处理"原则一致:前端层只需采集和判断"有没有值得发给后端的数据"。
|