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: 新增实现状态总览,补充文档索引
This commit is contained in:
hhs
2026-06-19 14:58:43 +08:00
parent 16302af7d2
commit dca37f3e48
8 changed files with 333 additions and 213 deletions

View File

@@ -34,8 +34,8 @@
| 语言 | Go | 高并发 goroutine 模型,适合长连接管理 |
| HTTP 框架 | Gin | 高性能 HTTP 路由,中间件生态成熟 |
| WebSocket | gorilla/websocket | Go 生态最成熟的 WebSocket 库 |
| 会话存储 | Redis规划中 / MemoryMVP 默认) | 高速 KV 存储MVP 阶段使用进程内存,可通过配置切换到 Redis |
| 持久化存储 | PostgreSQL规划中 | 对话历史、用量统计、用户偏好MVP 阶段未实现) |
| 会话存储 | Redis已实现 / Memory默认 | 高速 KV 存储Memory 为默认实现Redis 已实现可通过配置切换 |
| 持久化存储 | PostgreSQL已实现 | 对话历史、用户数据、会话持久化。MemoryManager 支持 Write-Through 到 PG |
| 配置管理 | Viper + godotenv | 支持 YAML + .env + 环境变量覆盖,详见 `03-接口文档.md` 第六章 |
| 日志 | Zap | 高性能结构化日志 |
@@ -76,18 +76,21 @@ Browser Go Gateway STT LLM TTS
## 后端模块
| 模块 | 职责 | 关键实现 |
|------|------|---------|
| WebSocket Handler | 管理客户端连接生命周期,单播消息推送 | goroutine per connection |
| Session Manager | 维护用户会话状态、对话历史 | MemoryMVP 默认)/ Redis可切换30 分钟 TTL详见 `03-接口文档.md` 第五章) |
| AI Orchestrator | 编排 STT→LLM→TTS 流式并行管道 | context 取消 + 超时控制 + 句子切分 |
| AI Service Layer | AI 服务抽象层STT/LLM/TTS | 多 provider 支持Deepgram/MiMo/OpenAI 等) |
| REST API | 健康检查、会话管理端点 | Gin 路由 |
| Error Handler | 统一错误码定义与发送 | 错误码枚举 |
| Logger | 日志初始化封装 | Zap 结构化日志 |
| Models | 数据模型定义 | WebSocket 消息、会话、配置等 |
| Model Router | 根据请求类型选择 AI 模型(规划中) | 规则引擎 + 成本阈值 |
| Rate Limiter | 防止单用户过度消耗 API 额度(规划中) | 令牌桶算法 |
| 模块 | 职责 | 关键实现 | 状态 |
|------|------|---------|------|
| WebSocket Handler | 管理客户端连接生命周期,单播消息推送 | goroutine per connectionJWT 认证conversation_id 恢复 | ✅ 已完成 |
| Session Manager | 维护用户会话状态、对话历史 | Memory默认/ Redis可切换30 分钟 TTLWrite-Through 到 PG(详见 `03-接口文档.md` 第五章) | ✅ 已完成 |
| AI Orchestrator | 编排 STT→LLM→TTS 流式并行管道 | context 取消 + 超时控制 + 句子切分 | ✅ 已完成 |
| AI Service Layer | AI 服务抽象层STT/LLM/TTS | 多 provider 支持Deepgram/MiMo/OpenAI 等) | ✅ 已完成 |
| Auth | 用户认证与授权 | JWT (HS256) 双 token 轮转bcrypt 密码哈希Gin 中间件 | ✅ 已完成 |
| Store | 持久化存储层 | UserRepository / MessageRepository / SessionRepository内存 + PostgreSQL 双实现 | ✅ 已完成 |
| REST API | 健康检查、认证、对话管理端点 | Gin 路由,输入校验,权限校验 | ✅ 已完成 |
| Error Handler | 统一错误码定义与发送 | 错误码枚举 | ✅ 已完成 |
| Logger | 日志初始化封装 | Zap 结构化日志 | ✅ 已完成 |
| Models | 数据模型定义 | WebSocket 消息、会话、配置、用户等 | ✅ 已完成 |
| Migrations | 数据库版本化迁移 | 嵌入式 SQL 文件,自动执行,版本跟踪 | ✅ 已完成 |
| Model Router | 根据请求类型选择 AI 模型 | 规则引擎 + 成本阈值 | 📋 规划中 |
| Rate Limiter | 防止单用户过度消耗 API 额度 | 令牌桶算法 | 📋 规划中 |
AI Orchestrator 核心接口(`internal/orchestrator/orchestrator.go`
@@ -113,44 +116,31 @@ Pipeline 实现(`internal/orchestrator/pipeline.go`)流程:
| 组件 | 职责 |
|------|------|
| AuthPage | 登录/注册表单前端校验Tab 切换 |
| CameraManager | 摄像头流采集 |
| MicManager | 麦克风音频采集 |
| EdgeProcessor | VAD + 关键帧检测Canvas 像素比较) |
| WebSocketManager | WS 连接生命周期管理 |
| ChatPanel | 消息展示 |
| ChatPanel | 消息展示、流式回复、文本输入、场景选择 |
| VideoPreview | 摄像头画面预览 |
| ConfigPanel | 侧抽屉式配置面板主题、TTS 开关、detail level、语言 |
| SessionSidebar | 侧抽屉式对话列表(搜索、重命名、删除 |
| ConfigPanel | 右侧抽屉式配置面板主题、TTS 开关、detail level、语言、场景、账户 |
| Toast | 轻量通知提示3 秒自动消失) |
核心 Hook`useVisionSession()` 封装一次完整的视觉对话会话摄像头、VAD、WebSocket、消息状态
核心 Hook`useVisionSession()` 封装一次完整的视觉对话会话摄像头、VAD、WebSocket、消息状态、认证、场景模式)。
```typescript
// useVisionSession 核心职责(简化示意)
function useVisionSession() {
const [messages, setMessages] = useState<Message[]>([]);
const wsRef = useWebSocket(`${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}/ws`);
const videoRef = useRef<HTMLVideoElement>(null);
const { captureFrame } = useCamera(videoRef);
// 组合useCamera + useMicrophone + useVAD + useWebSocketManager + useObservationMode
// 管理:消息状态、流式回复、处理标志、配置、统计、模式
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)
}));
}
});
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 };
// VAD onSpeechEnd: 捕获帧 + 音频 → 发送 query 消息
// 服务端消息处理stt_result / llm_chunk / llm_done / tts_audio / error
// 文本输入sendTextMessage() 支持手动输入文字(跳过 STT
// 场景模式config 消息支持 scenario 字段free_chat / interviewer / english_teacher 等)
// 打断interrupt() 发送中断消息 + 停止 TTS + 保存部分回复
// 认证WebSocket 连接携带 JWT token支持 conversation_id 恢复历史对话
}
```
@@ -158,40 +148,52 @@ function useVisionSession() {
| 阶段 | 存储方案 | 持久化内容 | 理由 |
|------|---------|-----------|------|
| MVP | Memory进程内 | 无 | 快速验证核心功能重启丢数据可接受。Redis 实现已就绪,可通过 `storage.driver` 配置切换 |
| 上线 | Redis + PostgreSQL | 对话历史、用户偏好、用量统计 | 用户需要查看历史,运营需要成本数据 |
| 规模化 | Redis + PG + 对象存储 | 图像帧、音频片段归档 | 大文件不适合存关系库 |
| 当前默认 | Memory进程内 | 会话状态 + 对话历史 | 零依赖快速启动。MemoryManager 支持 Write-Through 到 PG |
| 已实现 | Memory + PostgreSQL | 用户数据、对话历史、会话元数据 | 通过 `storage.driver: postgres` 启用MemoryManager 注入 PG Repository |
| 已实现 | Redis(独立) | 会话状态 + 对话历史 | 通过配置切换到 RedisManager适合多实例部署 |
冷热分离Redis 存"热数据"当前对话上下文微秒级读写PostgreSQL 存"冷数据"(历史记录)。
冷热分离Redis/Memory 存"热数据"当前对话上下文微秒级读写PostgreSQL 存"冷数据"(历史记录)。MemoryManager 的 Write-Through 机制确保每次 AppendMessage 同时写入 PG重启后可从 PG 恢复会话。
### PostgreSQL 表设计
### PostgreSQL 表设计(已实现)
实际迁移文件位于 `backend/migrations/`,通过 `go:embed` 嵌入,启动时自动执行:
```sql
CREATE TABLE sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
-- 001_users.up.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 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()
);
-- 002_messages.up.sql
CREATE TABLE messages (
id BIGSERIAL PRIMARY KEY,
session_id UUID REFERENCES sessions(id),
role VARCHAR(16) NOT NULL, -- "user" | "assistant"
session_id UUID NOT NULL,
role VARCHAR(16) NOT NULL,
content TEXT NOT NULL,
image_url TEXT,
tokens_used INTEGER DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE usage_daily (
user_id UUID NOT NULL,
date DATE NOT NULL,
llm_tokens BIGINT DEFAULT 0,
stt_seconds REAL DEFAULT 0,
tts_chars INTEGER DEFAULT 0,
estimated_cost NUMERIC(10,4) DEFAULT 0,
PRIMARY KEY (user_id, date)
-- 003_sessions.up.sql
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()
);
```