feat: v2 版本 #206
120
docs/02-系统架构.md
120
docs/02-系统架构.md
@@ -34,8 +34,8 @@
|
||||
| 语言 | Go | 高并发 goroutine 模型,适合长连接管理 |
|
||||
| HTTP 框架 | Gin | 高性能 HTTP 路由,中间件生态成熟 |
|
||||
| WebSocket | gorilla/websocket | Go 生态最成熟的 WebSocket 库 |
|
||||
| 会话存储 | Redis(规划中) / Memory(MVP 默认) | 高速 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 | 维护用户会话状态、对话历史 | Memory(MVP 默认)/ 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 connection,JWT 认证,conversation_id 恢复 | ✅ 已完成 |
|
||||
| Session Manager | 维护用户会话状态、对话历史 | Memory(默认)/ Redis(可切换),30 分钟 TTL,Write-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 (
|
||||
-- 001_users.up.sql
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL,
|
||||
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()
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
227
docs/03-接口文档.md
227
docs/03-接口文档.md
@@ -2,7 +2,7 @@
|
||||
|
||||
## 概述
|
||||
|
||||
前后端通信接口定义。以 WebSocket 承载实时对话,REST 端点支撑基础运维。**暂不实现持久化**,但通过 Repository 接口模式为后续扩展预留接入点。
|
||||
前后端通信接口定义。以 WebSocket 承载实时对话,REST 端点支撑基础运维。持久化已通过 PostgreSQL 实现,MemoryManager 支持 Write-Through 模式。
|
||||
|
||||
**设计原则**:
|
||||
- WebSocket 为主:所有对话数据走 WebSocket
|
||||
@@ -79,6 +79,7 @@ interface ConfigMessage {
|
||||
tts_enabled?: boolean; // 是否开启语音合成,默认 true
|
||||
detail_level?: "low" | "high"; // 图像精度,默认 "low"
|
||||
language?: string; // 交互语言,默认 "zh-CN"
|
||||
scenario?: string; // 场景模式,可选值:free_chat / interviewer / english_teacher / debate / interpreter
|
||||
};
|
||||
}
|
||||
```
|
||||
@@ -746,6 +747,20 @@ DELETE /api/sessions/{id} → 改用 DELETE /api/conversations/{id}
|
||||
| `/api/usage` | GET | 查询用量统计 |
|
||||
| `/api/users/{id}/preferences` | GET/PUT | 用户偏好管理 |
|
||||
|
||||
### 已实现的认证与对话端点
|
||||
|
||||
> 详见上方"认证接口"和"对话接口"章节。包括:
|
||||
> - `POST /api/auth/register` — 注册
|
||||
> - `POST /api/auth/login` — 登录
|
||||
> - `POST /api/auth/refresh` — 刷新 Token
|
||||
> - `POST /api/auth/logout` — 登出
|
||||
> - `GET /api/conversations` — 对话列表
|
||||
> - `POST /api/conversations` — 创建对话
|
||||
> - `GET /api/conversations/:id` — 对话详情
|
||||
> - `PATCH /api/conversations/:id` — 更新标题
|
||||
> - `DELETE /api/conversations/:id` — 删除对话
|
||||
> - `GET /api/conversations/:id/messages` — 历史消息
|
||||
|
||||
---
|
||||
|
||||
## 三、AI 服务层接口
|
||||
@@ -992,8 +1007,8 @@ session:{id}:history → List (对话历史)
|
||||
// Manager 会话管理器接口。
|
||||
// WebSocket Handler 通过此接口操作会话,不直接接触存储层。
|
||||
type Manager interface {
|
||||
// Create 创建新会话,返回 session ID。
|
||||
Create(ctx context.Context, config models.SessionConfig) (string, error)
|
||||
// Create 创建新会话,关联 user_id,返回 session ID。
|
||||
Create(ctx context.Context, userID string, config models.SessionConfig) (string, error)
|
||||
|
||||
// Get 获取会话(含 config)。不存在返回 ErrSessionNotFound。
|
||||
Get(ctx context.Context, sessionID string) (*models.Session, error)
|
||||
@@ -1001,10 +1016,16 @@ type Manager interface {
|
||||
// UpdateConfig 更新会话配置(config 消息触发)。
|
||||
UpdateConfig(ctx context.Context, sessionID string, patch models.SessionConfigPatch) error
|
||||
|
||||
// UpdateTitle 更新对话标题。
|
||||
UpdateTitle(ctx context.Context, sessionID string, title string) error
|
||||
|
||||
// ListByUser 获取用户的对话列表(分页)。
|
||||
ListByUser(ctx context.Context, userID string, page, size int) ([]ConversationSummary, int, error)
|
||||
|
||||
// GetHistory 获取最近 N 轮对话历史(供 Orchestrator 构建 LLM 上下文)。
|
||||
GetHistory(ctx context.Context, sessionID string, limit int) ([]models.Message, error)
|
||||
|
||||
// AppendMessage 追加一条对话消息,同时刷新 TTL。
|
||||
// AppendMessage 追加一条对话消息,同时刷新 TTL。首条 user 消息自动更新标题。
|
||||
AppendMessage(ctx context.Context, sessionID string, msg models.Message) error
|
||||
|
||||
// SetActiveRequest 标记当前正在处理的请求 ID(interrupt 用)。
|
||||
@@ -1025,11 +1046,37 @@ type Manager interface {
|
||||
// ActiveCount 返回当前活跃会话数(健康检查用)。
|
||||
ActiveCount() int
|
||||
}
|
||||
|
||||
// ConversationSummary 对话列表项。
|
||||
type ConversationSummary struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
LastMessage string `json:"last_message"`
|
||||
MessageCount int `json:"message_count"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
```
|
||||
|
||||
### WebSocket Handler 集成
|
||||
|
||||
```go
|
||||
// 连接建立(JWT 认证 + conversation_id 恢复)
|
||||
func (h *Handler) HandleWS(c *gin.Context) {
|
||||
tokenStr := c.Query("token")
|
||||
claims, err := h.tokenManager.ValidateAccess(tokenStr)
|
||||
// ... 认证失败返回 401
|
||||
|
||||
conversationID := c.Query("conversation_id")
|
||||
if conversationID != "" {
|
||||
sess, _ := h.sessionMgr.Get(c, conversationID)
|
||||
if sess.UserID != claims.UserID { /* 返回 SESSION_NOT_FOUND */ }
|
||||
sessionID = conversationID
|
||||
} else {
|
||||
sessionID, _ = h.sessionMgr.Create(c, claims.UserID, defaultConfig)
|
||||
}
|
||||
// ... 进入 WS 处理循环
|
||||
}
|
||||
|
||||
// query 分支
|
||||
case "query":
|
||||
var msg models.WsQuery
|
||||
@@ -1055,9 +1102,9 @@ case "interrupt":
|
||||
// 不调用 Destroy,让 session 自然过期(支持重连恢复)
|
||||
```
|
||||
|
||||
### MVP 内存实现
|
||||
### 内存实现(默认)
|
||||
|
||||
联调阶段无 Redis 时,用同一接口的内存实现:
|
||||
默认使用 `MemoryManager`,支持可选的 Write-Through 到 PostgreSQL:
|
||||
|
||||
```go
|
||||
type MemoryManager struct {
|
||||
@@ -1066,6 +1113,8 @@ type MemoryManager struct {
|
||||
ttl time.Duration
|
||||
maxHistory int
|
||||
stopCleaner chan struct{}
|
||||
msgRepo store.MessageRepository // 可选,Write-Through
|
||||
sessRepo store.SessionRepository // 可选,Write-Through
|
||||
}
|
||||
|
||||
type sessionEntry struct {
|
||||
@@ -1076,6 +1125,10 @@ type sessionEntry struct {
|
||||
}
|
||||
```
|
||||
|
||||
**Write-Through 机制**:注入 `msgRepo` 和 `sessRepo` 后,每次 `AppendMessage` 同时写入 PostgreSQL,重启后可从 PG 恢复会话。`Create` 时同时写入 PG sessions 表。
|
||||
|
||||
**自动标题**:首条 user 消息时,如果 title 仍为 "新对话",自动更新为消息内容前 20 个字符。
|
||||
|
||||
注入时根据配置切换:
|
||||
|
||||
```go
|
||||
@@ -1083,7 +1136,14 @@ var sessionMgr session.Manager
|
||||
if cfg.Redis.Addr != "" {
|
||||
sessionMgr = session.NewRedisManager(redisClient, 30*time.Minute, 20)
|
||||
} else {
|
||||
sessionMgr = session.NewMemoryManager(30*time.Minute, 20)
|
||||
opts := []session.Option{}
|
||||
if msgRepo != nil {
|
||||
opts = append(opts, session.WithMessageRepository(msgRepo))
|
||||
}
|
||||
if sessRepo != nil {
|
||||
opts = append(opts, session.WithSessionRepository(sessRepo))
|
||||
}
|
||||
sessionMgr = session.NewMemoryManager(30*time.Minute, 20, opts...)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1110,6 +1170,7 @@ Viper 加载顺序:先读 `config.yaml`,再根据 `APP_ENV` 环境变量尝
|
||||
type Config struct {
|
||||
App AppConfig `mapstructure:"app"`
|
||||
Server ServerConfig `mapstructure:"server"`
|
||||
Session SessionConfig `mapstructure:"session"`
|
||||
Redis RedisConfig `mapstructure:"redis"`
|
||||
AI AIConfig `mapstructure:"ai"`
|
||||
Storage StorageConfig `mapstructure:"storage"`
|
||||
@@ -1127,6 +1188,15 @@ type ServerConfig struct {
|
||||
Port int `mapstructure:"port"` // 默认 8080
|
||||
ReadTimeout int `mapstructure:"read_timeout"` // 秒,默认 30
|
||||
WriteTimeout int `mapstructure:"write_timeout"` // 秒,默认 30
|
||||
HeartbeatInterval int `mapstructure:"heartbeat_interval"` // 秒,默认 30
|
||||
HeartbeatTimeout int `mapstructure:"heartbeat_timeout"` // 秒,默认 60
|
||||
ShutdownTimeout int `mapstructure:"shutdown_timeout"` // 秒,默认 10
|
||||
AllowedOrigins []string `mapstructure:"allowed_origins"` // 空表示允许所有
|
||||
}
|
||||
|
||||
type SessionConfig struct {
|
||||
TTL int `mapstructure:"ttl"` // 分钟,默认 30
|
||||
MaxHistory int `mapstructure:"max_history"` // 条数,默认 20
|
||||
}
|
||||
|
||||
type RedisConfig struct {
|
||||
@@ -1142,10 +1212,12 @@ type AIConfig struct {
|
||||
}
|
||||
|
||||
type STTConfig struct {
|
||||
Provider string `mapstructure:"provider"` // "deepgram"
|
||||
Provider string `mapstructure:"provider"` // "deepgram" | "mimo" | "xiaomi"
|
||||
APIKey string `mapstructure:"api_key"`
|
||||
Model string `mapstructure:"model"` // 默认 "nova-2"
|
||||
Endpoint string `mapstructure:"endpoint"` // 默认 "wss://api.deepgram.com/v1/listen"
|
||||
Timeout int `mapstructure:"timeout"` // 秒,默认 5
|
||||
HTTPClientTimeout int `mapstructure:"http_client_timeout"` // 秒,默认 30
|
||||
}
|
||||
|
||||
type LLMConfig struct {
|
||||
@@ -1154,16 +1226,20 @@ type LLMConfig struct {
|
||||
Model string `mapstructure:"model"` // 默认 "gpt-4o"
|
||||
Endpoint string `mapstructure:"endpoint"` // 默认 "https://api.openai.com/v1"
|
||||
Timeout int `mapstructure:"timeout"` // 秒,默认 10
|
||||
HTTPClientTimeout int `mapstructure:"http_client_timeout"` // 秒,默认 60
|
||||
}
|
||||
|
||||
type TTSConfig struct {
|
||||
Provider string `mapstructure:"provider"` // "openai"
|
||||
Provider string `mapstructure:"provider"` // "openai" | "mimo" | "xiaomi"
|
||||
APIKey string `mapstructure:"api_key"`
|
||||
Model string `mapstructure:"model"` // 默认 "tts-1"
|
||||
Voice string `mapstructure:"voice"` // 默认 "alloy"
|
||||
Voice string `mapstructure:"voice"` // 默认 "mimo_default"
|
||||
Speed float64 `mapstructure:"speed"` // 默认 1.0
|
||||
Endpoint string `mapstructure:"endpoint"` // 默认 "https://api.openai.com/v1"
|
||||
Timeout int `mapstructure:"timeout"` // 秒,默认 5
|
||||
HTTPClientTimeout int `mapstructure:"http_client_timeout"` // 秒,默认 30
|
||||
OutputFormat string `mapstructure:"output_format"` // 默认 "mp3"
|
||||
SampleRate int `mapstructure:"sample_rate"` // 默认 24000
|
||||
}
|
||||
|
||||
type StorageConfig struct {
|
||||
@@ -1195,6 +1271,13 @@ server:
|
||||
port: 8080
|
||||
read_timeout: 30
|
||||
write_timeout: 30
|
||||
heartbeat_interval: 30
|
||||
heartbeat_timeout: 60
|
||||
shutdown_timeout: 10
|
||||
|
||||
session:
|
||||
ttl: 30
|
||||
max_history: 20
|
||||
|
||||
redis:
|
||||
addr: "localhost:6379"
|
||||
@@ -1206,18 +1289,24 @@ ai:
|
||||
provider: deepgram
|
||||
model: nova-2
|
||||
endpoint: "wss://api.deepgram.com/v1/listen"
|
||||
timeout: 5
|
||||
http_client_timeout: 30
|
||||
llm:
|
||||
provider: openai
|
||||
model: gpt-4o
|
||||
endpoint: "https://api.openai.com/v1"
|
||||
timeout: 10
|
||||
http_client_timeout: 60
|
||||
tts:
|
||||
provider: openai
|
||||
model: tts-1
|
||||
voice: alloy
|
||||
voice: mimo_default
|
||||
speed: 1.0
|
||||
endpoint: "https://api.openai.com/v1"
|
||||
timeout: 5
|
||||
http_client_timeout: 30
|
||||
output_format: mp3
|
||||
sample_rate: 24000
|
||||
|
||||
storage:
|
||||
driver: memory
|
||||
@@ -1360,6 +1449,7 @@ type SessionConfig struct {
|
||||
TTSEnabled bool `json:"tts_enabled"`
|
||||
DetailLevel string `json:"detail_level"` // "low" | "high"
|
||||
Language string `json:"language"`
|
||||
Scenario string `json:"scenario"` // "free_chat" | "interviewer" | "english_teacher" | "debate" | "interpreter"
|
||||
}
|
||||
|
||||
type QueryRequest struct {
|
||||
@@ -1416,6 +1506,7 @@ interface SessionConfig {
|
||||
ttsEnabled: boolean;
|
||||
detailLevel: "low" | "high";
|
||||
language: string;
|
||||
scenario: string; // "free_chat" | "interviewer" | "english_teacher" | "debate" | "interpreter"
|
||||
}
|
||||
|
||||
interface ChatMessage {
|
||||
@@ -1499,79 +1590,79 @@ type ClientMessage =
|
||||
|
||||
---
|
||||
|
||||
## 八、扩展接口设计
|
||||
## 八、存储层接口设计
|
||||
|
||||
通过 Repository 接口隔离存储层,MVP 用内存实现,后续替换为数据库——业务逻辑零改动。
|
||||
通过 Repository 接口隔离存储层,内存和 PostgreSQL 均已实现,业务逻辑零改动。
|
||||
|
||||
### UserRepository
|
||||
|
||||
```go
|
||||
// HistoryRepository — 对话历史存储契约
|
||||
// MVP: 内存实现(session 内有效,断开即丢)
|
||||
// 后续: PostgreSQL 实现
|
||||
type HistoryRepository interface {
|
||||
SaveMessage(ctx context.Context, sessionID string, msg Message) error
|
||||
GetMessages(ctx context.Context, sessionID string, limit int) ([]Message, error)
|
||||
}
|
||||
|
||||
// UsageRepository — 用量统计存储契约
|
||||
// MVP: 内存计数器
|
||||
// 后续: PostgreSQL 按天聚合
|
||||
type UsageRepository interface {
|
||||
RecordUsage(ctx context.Context, sessionID string, usage UsageRecord) error
|
||||
GetDailyUsage(ctx context.Context, userID string, days int) ([]UsageDaily, error)
|
||||
// UserRepository 用户数据存储契约。
|
||||
// 内存实现:MemUserRepository(测试/开发用)
|
||||
// PostgreSQL 实现:PgUserRepository
|
||||
type UserRepository interface {
|
||||
Create(ctx context.Context, username, passwordHash string) (string, error)
|
||||
FindByUsername(ctx context.Context, username string) (*User, error)
|
||||
FindByID(ctx context.Context, id string) (*User, error)
|
||||
SaveRefreshToken(ctx context.Context, userID, tokenHash string, expiresAt time.Time) error
|
||||
FindRefreshToken(ctx context.Context, tokenHash string) (string, error)
|
||||
DeleteRefreshToken(ctx context.Context, tokenHash string) error
|
||||
DeleteUserRefreshTokens(ctx context.Context, userID string) error
|
||||
}
|
||||
```
|
||||
|
||||
MVP 内存实现:
|
||||
### MessageRepository
|
||||
|
||||
```go
|
||||
type InMemoryHistory struct {
|
||||
mu sync.RWMutex
|
||||
sessions map[string][]Message
|
||||
}
|
||||
|
||||
func (h *InMemoryHistory) SaveMessage(ctx context.Context, sessionID string, msg Message) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
h.sessions[sessionID] = append(h.sessions[sessionID], msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *InMemoryHistory) GetMessages(ctx context.Context, sessionID string, limit int) ([]Message, error) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
msgs := h.sessions[sessionID]
|
||||
if limit > 0 && len(msgs) > limit {
|
||||
msgs = msgs[len(msgs)-limit:]
|
||||
}
|
||||
return msgs, nil
|
||||
// MessageRepository 对话消息持久化契约。
|
||||
// PostgreSQL 实现:PgMessageRepository
|
||||
// MemoryManager 通过 Write-Through 注入此接口。
|
||||
type MessageRepository interface {
|
||||
SaveMessage(ctx context.Context, sessionID string, msg models.Message, tokensUsed int) error
|
||||
GetMessages(ctx context.Context, sessionID string, limit int, beforeID int64) ([]StoredMessage, error)
|
||||
GetLastMessage(ctx context.Context, sessionID string) (*StoredMessage, error)
|
||||
GetMessageCount(ctx context.Context, sessionID string) (int, error)
|
||||
GetSessionMessageStats(ctx context.Context, sessionIDs []string) (map[string]int, error)
|
||||
}
|
||||
```
|
||||
|
||||
注入点(应用启动时根据配置选择实现):
|
||||
### SessionRepository
|
||||
|
||||
```go
|
||||
func NewApp(cfg *Config) *App {
|
||||
var history HistoryRepository
|
||||
var usage UsageRepository
|
||||
|
||||
switch cfg.Storage.Driver {
|
||||
case "postgres":
|
||||
pool, _ := pgxpool.New(ctx, cfg.Storage.DSN)
|
||||
history = &PgHistory{pool: pool}
|
||||
usage = &PgUsage{pool: pool}
|
||||
default: // "memory" — MVP 默认
|
||||
history = &InMemoryHistory{sessions: make(map[string][]Message)}
|
||||
usage = &InMemoryUsage{}
|
||||
}
|
||||
|
||||
return &App{
|
||||
orchestrator: NewOrchestrator(cfg.AI, history, usage),
|
||||
sessionMgr: NewSessionManager(cfg.Session, history),
|
||||
}
|
||||
// SessionRepository 会话元数据持久化契约。
|
||||
// PostgreSQL 实现:PgSessionRepository
|
||||
// MemoryManager 通过 Write-Through 注入此接口。
|
||||
type SessionRepository interface {
|
||||
Save(ctx context.Context, session models.Session) error
|
||||
FindByID(ctx context.Context, id string) (*models.Session, error)
|
||||
FindByUser(ctx context.Context, userID string, page, size int) ([]models.Session, int, error)
|
||||
UpdateTitle(ctx context.Context, id string, title string) error
|
||||
UpdateConfig(ctx context.Context, id string, config models.SessionConfig) error
|
||||
Touch(ctx context.Context, id string) error
|
||||
Delete(ctx context.Context, id string) error
|
||||
}
|
||||
```
|
||||
|
||||
> 依赖倒置原则——业务层依赖接口,不依赖具体实现。MVP 注入 `InMemoryHistory`,上线时一行代码换成 `PgHistory`。
|
||||
### 注入方式
|
||||
|
||||
```go
|
||||
// main.go 依赖注入
|
||||
if cfg.Storage.Driver == "postgres" {
|
||||
pool, _ := store.NewPostgresPool(ctx, cfg.Storage.DSN)
|
||||
userRepo = store.NewPgUserRepository(pool)
|
||||
msgRepo = store.NewPgMessageRepository(pool)
|
||||
sessRepo = store.NewPgSessionRepository(pool)
|
||||
sessionMgr = session.NewMemoryManager(30*time.Minute, 20,
|
||||
session.WithMessageRepository(msgRepo),
|
||||
session.WithSessionRepository(sessRepo),
|
||||
)
|
||||
} else {
|
||||
userRepo = store.NewMemUserRepository()
|
||||
sessionMgr = session.NewMemoryManager(30*time.Minute, 20)
|
||||
}
|
||||
```
|
||||
|
||||
> 依赖倒置原则——业务层依赖接口,不依赖具体实现。通过配置一行代码切换存储后端。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -4,23 +4,26 @@
|
||||
|
||||
本文档记录项目中各项技术的**选型过程、替代方案对比和决策理由**。技术选型没有"绝对正确",只有"更适合"。
|
||||
|
||||
**定位**:持久化部分是拓展选型,不阻塞 MVP(MVP 用内存存储即可)。前端边缘处理部分是 MVP 阶段就需要确定的技术栈。AI 服务栈(STT/LLM/TTS)已确定默认选型,可通过配置灵活切换。
|
||||
**定位**:本文档记录各项技术的选型过程和决策理由。AI 服务栈、持久化层、认证系统均已实现并通过配置灵活切换。前端边缘处理已确定技术栈。
|
||||
|
||||
```
|
||||
技术选型
|
||||
├── AI 服务栈
|
||||
├── AI 服务栈(✅ 已实现)
|
||||
│ ├── STT: Deepgram(默认) / MiMo ASR
|
||||
│ ├── LLM: GPT-4o(默认) / 通义千问等 OpenAI 兼容模型
|
||||
│ └── TTS: OpenAI TTS(默认) / MiMo TTS
|
||||
├── 持久化层 → 数据库选型: PostgreSQL(规划中,MVP 阶段使用内存存储)
|
||||
├── 认证与用户系统
|
||||
├── 持久化层(✅ 已实现)
|
||||
│ ├── 数据库: PostgreSQL(pgx/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
|
||||
└── 前端边缘处理层
|
||||
├── 边缘推理: ONNX Runtime Web(规划中,MVP 使用 Canvas 像素比较)
|
||||
└── 前端边缘处理层(✅ 已实现)
|
||||
├── 关键帧检测: Canvas 像素比较(160x120 降采样)
|
||||
├── 语音检测: @ricky0123/vad-web
|
||||
└── 媒体采集: MediaDevices API
|
||||
```
|
||||
@@ -61,7 +64,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 二、持久化层选型(规划中,MVP 阶段使用内存存储)
|
||||
## 二、持久化层选型(已实现)
|
||||
|
||||
### 数据特征分析
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ vad.start();
|
||||
|
||||
句子切分规则:按中文标点(`。!?`)、英文标点(`. ! ?`)和换行符切分。
|
||||
|
||||
当前实现参数:Voice `"alloy"`、Speed `1.0`、OutputFmt `"mp3"`、SampleRate `24000`。
|
||||
当前实现参数:Voice `"mimo_default"`(可通过配置切换)、Speed `1.0`、OutputFmt `"mp3"`、SampleRate `24000`。
|
||||
|
||||
方案选择:
|
||||
- **OpenAI TTS**(默认):音质好,延迟中等,按字符计费,模型 tts-1
|
||||
|
||||
@@ -709,42 +709,43 @@ storage:
|
||||
|
||||
## 八、实施阶段
|
||||
|
||||
### Phase 1:用户认证系统
|
||||
### Phase 1:用户认证系统 ✅
|
||||
|
||||
- [ ] 数据库 schema 迁移脚本(users, refresh_tokens 表)
|
||||
- [ ] `internal/auth/` 包:TokenManager, bcrypt 工具, JWT 中间件
|
||||
- [ ] `internal/store/user.go`:UserRepository 接口 + PostgreSQL 实现
|
||||
- [ ] REST API:`/api/auth/register`, `/api/auth/login`, `/api/auth/refresh`, `/api/auth/logout`
|
||||
- [ ] 单元测试
|
||||
- [x] 数据库 schema 迁移脚本(users, refresh_tokens 表)— `migrations/001_users.up.sql`
|
||||
- [x] `internal/auth/` 包:TokenManager, bcrypt 工具, JWT 中间件
|
||||
- [x] `internal/store/user.go`:UserRepository 接口 + PostgreSQL 实现 + 内存实现
|
||||
- [x] REST API:`/api/auth/register`, `/api/auth/login`, `/api/auth/refresh`, `/api/auth/logout`
|
||||
- [x] 单元测试 — `jwt_test.go`, `service_test.go`, `auth_test.go`, `user_test.go`
|
||||
|
||||
### Phase 2:对话 CRUD + 消息持久化
|
||||
### Phase 2:对话 CRUD + 消息持久化 ✅
|
||||
|
||||
- [ ] 数据库 schema 迁移脚本(sessions, messages 表改造)
|
||||
- [ ] `internal/store/conversation.go`:ConversationRepository 接口 + PostgreSQL 实现
|
||||
- [ ] Session Manager 扩展:Create 绑定 user_id, ListByUser, UpdateTitle
|
||||
- [ ] REST API:`/api/conversations` CRUD + `/api/conversations/:id/messages`
|
||||
- [ ] Write-through:AppendMessage 同时写 PostgreSQL
|
||||
- [x] 数据库 schema 迁移脚本(sessions, messages 表)— `migrations/002_messages.up.sql`, `003_sessions.up.sql`
|
||||
- [x] `internal/store/message.go`:MessageRepository 接口 + PostgreSQL 实现
|
||||
- [x] `internal/store/session.go`:SessionRepository 接口 + PostgreSQL 实现
|
||||
- [x] Session Manager 扩展:Create 绑定 user_id, ListByUser, UpdateTitle
|
||||
- [x] REST API:`/api/conversations` CRUD + `/api/conversations/:id/messages`
|
||||
- [x] Write-through:AppendMessage 同时写 PostgreSQL
|
||||
|
||||
### Phase 3:对话历史恢复
|
||||
### Phase 3:对话历史恢复 ✅
|
||||
|
||||
- [ ] `sessionManager.LoadFromDB()` 实现
|
||||
- [ ] 对话标题自动生成逻辑
|
||||
- [ ] REST API:对话详情、历史消息查询(分页)
|
||||
- [x] MemoryManager 支持从 PG 透明恢复会话(Get 时自动 LoadFromDB)
|
||||
- [x] 对话标题自动生成逻辑(首条 user 消息前 20 字符)
|
||||
- [x] REST API:对话详情、历史消息查询(游标分页)
|
||||
|
||||
### Phase 4:前端集成
|
||||
### Phase 4:前端集成 ✅
|
||||
|
||||
- [ ] `useAuth` hook + 请求拦截器(自动附加 token、自动 refresh)
|
||||
- [ ] `AuthPage` 组件(登录/注册表单)
|
||||
- [ ] `ConversationList` 组件
|
||||
- [ ] `useConversations` hook
|
||||
- [ ] 路由守卫:未登录重定向到 `/login`
|
||||
- [ ] WebSocket 连接带 token + conversation_id
|
||||
- [ ] `useVisionSession` 适配多对话切换
|
||||
- [x] `useAuth` hook + AuthProvider(自动附加 token、自动 refresh)
|
||||
- [x] `AuthPage` 组件(登录/注册表单)
|
||||
- [x] `SessionSidebar` 组件(对话列表、搜索、重命名、删除)
|
||||
- [x] `useSessionList` hook(localStorage 持久化)
|
||||
- [x] 路由守卫:未登录重定向到 AuthPage
|
||||
- [x] WebSocket 连接带 token + conversation_id
|
||||
- [x] `useVisionSession` 适配多对话切换
|
||||
|
||||
### Phase 5:配置与收尾
|
||||
### Phase 5:配置与收尾 ✅
|
||||
|
||||
- [ ] 配置结构体扩展(AuthConfig)
|
||||
- [ ] config.yaml 更新
|
||||
- [ ] docker-compose 添加 PostgreSQL
|
||||
- [ ] 集成测试
|
||||
- [ ] 更新 `02-系统架构.md` 和 `03-接口文档.md`
|
||||
- [x] 配置结构体扩展(AuthConfig, SessionConfig, StorageConfig)
|
||||
- [x] config.yaml 更新
|
||||
- [x] 数据库迁移嵌入式自动执行(`go:embed`)
|
||||
- [x] 集成测试 — 122 个测试函数覆盖所有模块
|
||||
- [x] 更新 `02-系统架构.md` 和 `03-接口文档.md`
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# CamTalk 后端完善计划
|
||||
|
||||
> **✅ 状态:全部完成。** 所有 Phase 已实现并通过测试(约 122 个测试函数)。本文档保留作为历史参考。
|
||||
|
||||
## Context
|
||||
|
||||
后端当前是一个骨架:`main.go` 启动 Gin 服务器,`ws/handler.go` 实现了 WebSocket 连接生命周期和消息分发,`models/models.go` 定义了所有协议消息类型,`config/config.go` 实现了 Viper 配置加载。但所有业务逻辑都是 TODO 桩——没有 Session Manager、没有 AI 服务客户端、没有编排层、没有日志/错误工具、没有测试。前端已基本完成,正在等待后端提供真实的 AI 管道。
|
||||
@@ -10,7 +12,7 @@
|
||||
|
||||
## 分阶段实施
|
||||
|
||||
### Phase 1:基础设施(logger、errors、config 接入、graceful shutdown)
|
||||
### Phase 1:基础设施(logger、errors、config 接入、graceful shutdown) ✅
|
||||
|
||||
**目标**:为后续模块提供日志、错误码、配置等基础能力,替换 `main.go` 中的硬编码值。
|
||||
|
||||
@@ -26,7 +28,7 @@
|
||||
|
||||
---
|
||||
|
||||
### Phase 2:Session Manager
|
||||
### Phase 2:Session Manager ✅
|
||||
|
||||
**目标**:实现会话生命周期管理,让 WS handler 能追踪会话、存储对话历史。
|
||||
|
||||
@@ -40,7 +42,7 @@
|
||||
|
||||
---
|
||||
|
||||
### Phase 3:AI 服务层接口 + 实现
|
||||
### Phase 3:AI 服务层接口 + 实现 ✅
|
||||
|
||||
**目标**:定义并实现三个 AI 服务客户端,每个服务一个独立包。
|
||||
|
||||
@@ -62,7 +64,7 @@
|
||||
|
||||
---
|
||||
|
||||
### Phase 4:AI Orchestrator(核心编排)
|
||||
### Phase 4:AI Orchestrator(核心编排) ✅
|
||||
|
||||
**目标**:实现 STT → LLM → TTS 流式并行管道,这是后端最关键的业务逻辑。
|
||||
|
||||
@@ -78,7 +80,7 @@
|
||||
|
||||
---
|
||||
|
||||
### Phase 5:WS Handler 完整接入
|
||||
### Phase 5:WS Handler 完整接入 ✅
|
||||
|
||||
**目标**:将 Session Manager + Orchestrator 串入 WebSocket handler,实现端到端消息处理。
|
||||
|
||||
@@ -92,7 +94,7 @@
|
||||
|
||||
---
|
||||
|
||||
### Phase 6:REST API 补全
|
||||
### Phase 6:REST API 补全 ✅
|
||||
|
||||
**目标**:补全设计文档中的 REST 端点。
|
||||
|
||||
@@ -104,7 +106,7 @@
|
||||
|
||||
---
|
||||
|
||||
### Phase 7:Rate Limiter + Model Router(可选/MVP 后)
|
||||
### Phase 7:Rate Limiter + Model Router(可选/MVP 后) 📋
|
||||
|
||||
**目标**:防止滥用 + 智能模型选择,MVP 可简化或跳过。
|
||||
|
||||
@@ -116,7 +118,7 @@
|
||||
|
||||
---
|
||||
|
||||
### Phase 8:集成测试 + 文档同步
|
||||
### Phase 8:集成测试 + 文档同步 ✅
|
||||
|
||||
| # | 任务 | 文件 | 说明 |
|
||||
|---|------|------|------|
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# CamTalk 后端用户模块构建计划
|
||||
|
||||
> **✅ 状态:全部完成。** 所有 Phase 已实现并通过测试。本文档保留作为历史参考。
|
||||
|
||||
## Context
|
||||
|
||||
后端 AI 管道(STT → LLM → TTS)已完成,现在需要实现用户系统和对话持久化。目标:**用户注册登录后,可在对话列表中选择历史对话继续交谈**。
|
||||
@@ -34,7 +36,7 @@
|
||||
|
||||
## 分阶段实施
|
||||
|
||||
### Phase 1:配置扩展 + 数据库连接
|
||||
### Phase 1:配置扩展 + 数据库连接 ✅
|
||||
|
||||
**目标**:扩展配置结构体,建立 PostgreSQL 连接池。
|
||||
|
||||
@@ -83,7 +85,7 @@ func NewPostgresPool(ctx context.Context, dsn string) (*pgxpool.Pool, error) {
|
||||
|
||||
---
|
||||
|
||||
### Phase 2:用户模型 + Repository
|
||||
### Phase 2:用户模型 + Repository ✅
|
||||
|
||||
**目标**:定义用户数据模型和持久化接口。
|
||||
|
||||
@@ -149,7 +151,7 @@ type User struct {
|
||||
|
||||
---
|
||||
|
||||
### Phase 3:JWT + 认证服务
|
||||
### Phase 3:JWT + 认证服务 ✅
|
||||
|
||||
**目标**:实现 JWT 签发/校验、bcrypt 密码处理、认证业务逻辑。
|
||||
|
||||
@@ -292,7 +294,7 @@ type Service interface {
|
||||
|
||||
---
|
||||
|
||||
### Phase 4:认证 REST API
|
||||
### Phase 4:认证 REST API ✅
|
||||
|
||||
**目标**:实现注册、登录、刷新、登出四个端点。
|
||||
|
||||
@@ -350,7 +352,7 @@ const (
|
||||
|
||||
---
|
||||
|
||||
### Phase 5:Session Manager 改造
|
||||
### Phase 5:Session Manager 改造 ✅
|
||||
|
||||
**目标**:Session Manager 关联 user_id,支持对话列表查询。
|
||||
|
||||
@@ -436,7 +438,7 @@ func (m *MemoryManager) ListByUser(ctx context.Context, userID string, page, siz
|
||||
|
||||
---
|
||||
|
||||
### Phase 6:对话 REST API
|
||||
### Phase 6:对话 REST API ✅
|
||||
|
||||
**目标**:实现对话 CRUD 和历史消息查询端点。
|
||||
|
||||
@@ -493,7 +495,7 @@ func (h *ConversationHandler) getSessionForUser(c *gin.Context, sessionID string
|
||||
|
||||
---
|
||||
|
||||
### Phase 7:WebSocket 认证集成
|
||||
### Phase 7:WebSocket 认证集成 ✅
|
||||
|
||||
**目标**:WS 连接需要 JWT 认证,支持指定 conversation_id 恢复历史对话。
|
||||
|
||||
@@ -564,7 +566,7 @@ func generateTitle(firstMessage string) string {
|
||||
|
||||
---
|
||||
|
||||
### Phase 8:消息持久化(Write-Through)
|
||||
### Phase 8:消息持久化(Write-Through) ✅
|
||||
|
||||
**目标**:对话消息同时写入 PostgreSQL,保证重启不丢数据。
|
||||
|
||||
@@ -643,7 +645,7 @@ func (m *MemoryManager) AppendMessage(ctx context.Context, sessionID string, msg
|
||||
|
||||
---
|
||||
|
||||
### Phase 9:旧端点废弃 + 集成收尾
|
||||
### Phase 9:旧端点废弃 + 集成收尾 ✅
|
||||
|
||||
**目标**:废弃旧的 `/api/sessions` 端点,完成全链路集成。
|
||||
|
||||
|
||||
@@ -4,17 +4,21 @@ CamTalk 是一款多模态实时 AI 视觉对话助手。用户通过摄像头
|
||||
|
||||
## 文档索引
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [01-项目概述](01-项目概述.md) | 项目目标、核心挑战、交付物 |
|
||||
| [02-系统架构](02-系统架构.md) | 三层架构、技术栈、核心交互流程、前后端模块、存储策略、部署架构 |
|
||||
| [03-接口文档](03-接口文档.md) | WebSocket 协议、REST API、**AI 服务层接口**、**编排器设计**、**Session Manager**、**配置管理(Viper)**、数据模型、错误码、连接管理(**实现时首先阅读**) |
|
||||
| [04-技术选型](04-技术选型.md) | 持久化层(PostgreSQL)和前端边缘处理层的选型对比与决策理由 |
|
||||
| [05-用户故事](05-用户故事.md) | P0/P1/P2 用户故事、验收标准、优先级决策依据 |
|
||||
| [06-语音交互](06-语音交互.md) | VAD → STT → LLM → TTS 全链路、延迟优化 |
|
||||
| [07-视觉理解](07-视觉理解.md) | 帧采样策略、图像编码、多模态 LLM 输入机制 |
|
||||
| [08-成本控制](08-成本控制.md) | 智能采样、端云协同、模型分级、缓存复用 |
|
||||
| [09-技术名词解释](09-技术名词解释.md) | 前端/后端/AI 服务技术名词简明解释 |
|
||||
| 文档 | 说明 | 状态 |
|
||||
|------|------|------|
|
||||
| [01-项目概述](01-项目概述.md) | 项目目标、核心挑战、交付物 | ✅ 与代码一致 |
|
||||
| [02-系统架构](02-系统架构.md) | 三层架构、技术栈、核心交互流程、前后端模块、存储策略、部署架构 | ✅ 已更新 |
|
||||
| [03-接口文档](03-接口文档.md) | WebSocket 协议、REST API、AI 服务层接口、编排器设计、Session Manager、配置管理(Viper)、数据模型、错误码、连接管理(**实现时首先阅读**) | ✅ 已更新 |
|
||||
| [04-技术选型](04-技术选型.md) | 持久化层(PostgreSQL)、认证系统和前端边缘处理层的选型对比与决策理由 | ✅ 已更新 |
|
||||
| [05-用户故事](05-用户故事.md) | P0/P1/P2 用户故事、验收标准、优先级决策依据 | ✅ 与代码一致 |
|
||||
| [06-语音交互](06-语音交互.md) | VAD → STT → LLM → TTS 全链路、延迟优化 | ✅ 与代码一致 |
|
||||
| [07-视觉理解](07-视觉理解.md) | 帧采样策略、图像编码、多模态 LLM 输入机制 | ✅ 与代码一致 |
|
||||
| [08-成本控制](08-成本控制.md) | 智能采样、端云协同、模型分级、缓存复用 | ✅ 与代码一致 |
|
||||
| [09-技术名词解释](09-技术名词解释.md) | 前端/后端/AI 服务技术名词简明解释 | ✅ 与代码一致 |
|
||||
| [10-功能创意](10-功能创意.md) | 未来功能创意清单 | 📋 愿景 |
|
||||
| [11-持久化与用户系统设计](11-持久化与用户系统设计.md) | 用户认证、JWT、对话持久化的完整设计方案 | ✅ 已全部实现 |
|
||||
| [PLAN_BACKEND.md](PLAN_BACKEND.md) | 后端 AI 管道构建计划(Session Manager → AI 服务 → Orchestrator) | ✅ 已全部完成 |
|
||||
| [PLAN_USER_MODULE.md](PLAN_USER_MODULE.md) | 后端用户模块构建计划(Auth → 对话 CRUD → 消息持久化) | ✅ 已全部完成 |
|
||||
|
||||
## 推荐阅读顺序
|
||||
|
||||
@@ -25,3 +29,18 @@ CamTalk 是一款多模态实时 AI 视觉对话助手。用户通过摄像头
|
||||
5. **05-用户故事** — 明确功能优先级
|
||||
6. **06~08** — 各技术领域的详细设计
|
||||
7. **09-技术名词解释** — 遇到不熟悉的名词时查阅
|
||||
8. **11-持久化与用户系统设计** — 用户认证和持久化的详细设计
|
||||
|
||||
## 实现状态总览
|
||||
|
||||
前后端代码已全部实现,无 TODO/FIXME 桩代码。后端约 122 个测试函数覆盖所有模块。
|
||||
|
||||
| 层级 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 前端 | ✅ 已完成 | 10 个组件、3 个 Hook、10 个库模块、i18n 三语言 |
|
||||
| 后端 AI 管道 | ✅ 已完成 | STT/LLM/TTS 多 provider、Orchestrator 流式并行 |
|
||||
| 后端用户系统 | ✅ 已完成 | JWT 认证、用户注册登录、对话 CRUD、消息持久化 |
|
||||
| 后端存储层 | ✅ 已完成 | Memory + PostgreSQL + Redis 三种实现 |
|
||||
| 数据库迁移 | ✅ 已完成 | 3 个版本化迁移脚本,嵌入式自动执行 |
|
||||
| Model Router | 📋 规划中 | 按问题复杂度选择模型 |
|
||||
| Rate Limiter | 📋 规划中 | 令牌桶限流 |
|
||||
|
||||
Reference in New Issue
Block a user