Files
CamTalk/docs/03-技术选型.md
hhs 032de796c8 docs: 重构文档结构,规范编号并整合冗余内容
## 主要变更

### 文档重构(减少 1199 行,-23%)
- 01-架构设计.md: 503→369 行 (-27%),删除 DDL/配置示例,精简鉴权/存储描述
- 02-接口文档.md: 1313→570 行 (-57%),删除 Go 接口/Orchestrator 实现/配置管理
- 07-成本控制.md: 65→59 行 (-9%),代码块替换为文件引用

### 文档编号规范化
- 08-功能创意.md → 删除(内容整合到 README.md "功能扩展方向")
- 10-Eino框架与编排设计.md → 08-Eino框架与编排设计.md
- 情景切换.md → 09-情景切换.md
- 12-鉴权体系.md → 10-鉴权体系.md
- 13-令牌桶限流.md → 11-令牌桶限流.md

### 交叉引用更新
- 01-架构设计.md: 更新对鉴权体系/令牌桶限流的引用为新编号
- README.md: 更新文档索引表、推荐阅读顺序、新增功能扩展方向

### 删除过时文档
- 09-技术名词解释.md(内容已整合到 03-技术选型.md)
- 10-Eino重构方案.md(历史记录,已完成)
- 11-Eino框架技术文档.md(已合并到 08)
- 情景切换功能完整文档.md(已规范化为 09)

## 重构原则
- 架构文档聚焦系统结构,移除实现细节
- 接口文档保留纯契约,删除内部实现
- 编号连续(01-11),语义清晰
- 通过交叉引用连接相关文档,避免重复
2026-06-21 14:48:03 +08:00

19 KiB
Raw Permalink Blame History

技术选型

概述

本文档记录项目中各项技术的选型过程、替代方案对比和决策理由。技术选型没有"绝对正确",只有"更适合"。

各技术选型章节包含关键术语解释,帮助快速理解技术概念。

后端核心技术栈

名词 解释
Go (Golang) 高并发后端语言Google 开发,杀手锏是 goroutine——极轻量协程一个程序可轻松开几万个每个只占几 KB 内存,适合管理大量 WebSocket 长连接
gorilla/websocket Go WebSocket 库Go 标准库无内置 WebSocket 支持,此库是社区最成熟的选择,处理了协议握手、帧解析等底层细节
Viper Go 配置管理库,读取 JSON/YAML/TOML 配置,支持环境变量覆盖,方便开发/测试/生产环境用不同配置
Zap Go 结构化日志库Uber 开源,输出 JSON 格式日志,方便工具搜索分析,性能远超标准库 log
技术选型
├── AI 编排框架
│   └── CloudWeGo Eino Graph声明式 DAG 编排,替代手写 goroutine 管道)
├── AI 服务栈
│   ├── STT: MiMo ASR默认 / Deepgram
│   ├── LLM: DashScope qwen3-vl-plus默认 / GPT-4o 等 OpenAI 兼容模型
│   └── TTS: MiMo TTS默认 / OpenAI TTS
├── 持久化层
│   ├── 数据库: PostgreSQLpgx/v5手写 SQL
│   ├── 迁移: 嵌入式 SQL 文件,自动执行
│   └── 存储模式: 三级存储 TieredManagerL1 Memory → L2 Redis → L3 PostgreSQL
├── 认证与用户系统
│   ├── 认证方案: 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 编排框架选型

关键术语

名词 解释
Eino 字节跳动开源的 Go AI 应用开发框架CloudWeGo Eino提供 Graph DAG 编排、组件抽象ChatModel/Tool 等)、流式处理和 Callback AOP 机制
compose.Graph Eino 的 DAG 编排器,声明式有向无环图,节点可以是 Lambda、ChatModel、ToolsNode 等,边定义数据流向
Lambda Graph 中的可组合函数单元四种模式InvokableLambda同步、StreamableLambda流式输出、CollectableLambda流式输入、TransformableLambda双向流式
StreamReader Eino 的流式数据抽象 schema.StreamReader[T],类似 io.Reader 的语义,Recv() 读取一帧,io.EOF 表示流结束
Callback Eino 的 AOP 机制类似中间件的钩子支持节点生命周期回调OnStart/OnEnd/OnError/OnEndWithStreamOutput

更多 Eino 相关概念详见 10-Eino框架与编排设计.md

候选方案对比

框架 语言 特点 CamTalk 适用性
CloudWeGo Eino Go Go 原生、类型安全、流式原生、Graph DAG 编排 完美匹配
LangChain Go Go 生态丰富但较重,抽象层多 过度抽象
自研编排 Go 完全可控 维护成本高

选择 Eino 的理由

维度 手写 goroutine旧方案 Eino Graph新方案
编排方式 手动 go func() + sync.WaitGroup 声明式 DAG类型安全
流式处理 自定义 chan 传递 StreamReader + Pipe,自动转换
错误处理 各节点独立处理,不一致 Graph 级别统一错误传播
回调/AOP 日志散落各处 callbacks.Handler 统一注入
配置灵活性 Pipeline 创建时固定 每请求 Option 动态注入
可测试性 需启动 goroutine Graph.Invoke() 直接测试
扩展性 修改 Pipeline 代码 添加节点 + 边,无侵入

核心依赖

github.com/cloudwego/eino v0.9.9                              # 核心框架
github.com/cloudwego/eino-ext/components/model/openai v0.1.13  # OpenAI 兼容 ChatModel

核心理由

  1. Go 原生,泛型支持,编译时类型检查
  2. 原生流式处理(StreamReader),适合 LLM token 级推送
  3. Graph 支持分支、并行、循环,满足当前和未来需求
  4. Callback 机制实现 AOP日志、指标、消息推送
  5. eino-ext 提供 OpenAI ChatModel 实现,直接对接 DashScope

详细的 Eino 框架使用文档见 11-Eino框架技术文档,重构方案见 10-Eino重构方案,实施记录见 12-Eino重构实施记录


二、AI 服务栈选型

关键术语

名词 解释
多模态 LLM 能读文字又能看图片的大语言模型,如 GPT-4oOpenAI、Claude SonnetAnthropic给照片+问题能"看懂"照片再回答
STT Speech-to-Text语音转文字。流式识别延迟可低于 500ms
TTS Text-to-Speech文字转语音。支持流式——边生成边读不必等全部生成完

STT语音识别

方案 延迟 成本 特点
MiMo ASR(默认) ~1s 按量计费 国产替代,兼容 OpenAI chat/completions 格式HTTP 非流式
Deepgram <500ms 按分钟计费 流式识别延迟极低WebSocket 接口
Whisper API 1-3s 按分钟计费 准确率高,支持多语言
FunASR <500ms 自部署免费 阿里开源,中文优化

当前默认使用 MiMo ASRmimo-v2.5-asr可通过 ai.stt.provider 配置切换到 Deepgram。

LLM多模态大模型

方案 成本 特点
DashScope qwen3-vl-plus(默认) 按量计费 阿里云,通过 OpenAI 兼容接口调用,视觉理解能力强
GPT-4o $2.5/1M tokens OpenAIAPI 成熟,流式推理
Claude Sonnet $3/1M tokens Anthropic长上下文能力强

LLM 通过 Eino 框架的 eino-ext/components/model/openai ChatModel 组件接入,支持任何 OpenAI 兼容接口。配置 ai.llm.providerai.llm.modelai.llm.endpoint 即可切换。

TTS语音合成

方案 成本 特点
MiMo TTS(默认) 按量计费 国产替代,通过配置切换,模型 mimo-v2.5-tts
OpenAI TTS $15/1M 字符 音质自然,支持流式,默认模型 tts-1语音 alloy

当前默认使用 MiMo TTSmimo-v2.5-tts可通过 ai.tts.provider 配置切换到 OpenAI TTS。


三、持久化层选型

关键术语

名词 解释
PostgreSQL 关系型数据库,支持 JSONBJSON 二进制格式可建索引、窗口函数、CTE 等高级特性
Redis 内存 KV 数据库,数据放在内存里,读写微秒级。支持 TTL 过期自动清理
MVCC Multi-Version Concurrency Control多版本并发控制PostgreSQL 用此实现高并发读写而不阻塞

数据特征分析

数据 结构特征 读写模式 数据量级
对话消息 强结构化 写多读少,按会话聚合读取 中(每用户日均 ~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 麻烦。

MongoDBmessages 需按 session_id 关联 sessionsMongoDB 中要用 $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 集成示例

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 包容查询:

SELECT id, content, created_at
FROM messages
WHERE role = 'user'
  AND content @> '{"text": "花"}'
ORDER BY created_at DESC
LIMIT 20;

冷热分离架构(三级存储)

Go Gateway (TieredManager)
  ├── L1: Memory进程内缓存微秒级
  ├── L2: Redis分布式缓存毫秒级
  └── L3: PostgreSQL持久化存储冷数据

读取路径L1 → L2 → L3逐级回源命中后向上回填
写入路径L1 → L2同步 → L3异步

TieredManager 自动管理三级存储,后台 goroutine 每 30 秒 ping Redis 健康状态Redis 故障时自动降级为 L1+L3 模式。

决策流程

需要持久化?
  ├── 否 → 继续用 Redis
  └── 是 → 数据强结构化?
        ├── 否, 高度嵌套 → 考虑 MongoDB
        └── 是 → 数据量级?
              ├── < 100GB, 单机可扛 → PostgreSQL
              ├── 海量, 需水平扩展 → TiDB / CockroachDB
              └── 极小, 单文件即可 → SQLite

四、前端边缘处理层选型

关键术语

名词 解释
React 18 组件化 UI 框架Facebook 开源把页面拆成组件搭积木拼装。18 版本支持并发渲染
TypeScript 带类型的 JavaScript在 JS 基础上增加类型声明,编译阶段就能发现类型错误
Vite 前端构建工具,利用浏览器原生 ES Module开发时毫秒级热更新HMR构建产物小
WebSocket 浏览器与服务器的双向通道。HTTP 是"一问一答"WebSocket 像打电话——接通后双方随时互发消息,适合实时对话场景
ONNX Runtime Web 浏览器端 AI 推理引擎,微软定义的通用模型格式 ONNX 的运行引擎,可在浏览器中用 WASM 加速跑轻量模型(如 VAD、关键帧检测零延迟、不耗服务器资源
VAD Voice Activity Detection语音活动检测检测"人有没有在说话"。WebRTC 内置了高效的 VAD 算法
MediaDevices API 浏览器摄像头/麦克风接口,navigator.mediaDevices.getUserMedia() 是浏览器音视频采集的唯一标准入口,无需插件

总览

能力 当前选型 选择理由
边缘推理 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 JSON Web Token无状态 token服务端不存 session分布式友好
HS256 HMAC-SHA256JWT 对称签名算法,用同一密钥签名和验证
bcrypt 密码哈希算法,自适应 cost factor抗暴力破解
pgx Go 生态性能最优的 PostgreSQL 驱动,原生协议实现,内置连接池 pgxpool

总览

能力 选型 选择理由
认证方案 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) 双 tokenaccess_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 时自动触发刷新流程。