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),语义清晰
- 通过交叉引用连接相关文档,避免重复
This commit is contained in:
hhs
2026-06-21 14:48:03 +08:00
parent 9e5f691056
commit 032de796c8
17 changed files with 2790 additions and 3989 deletions

311
CLAUDE.md
View File

@@ -1,294 +1,73 @@
# CLAUDE.md
本文件为 Claude Code (claude.ai/code) 在本仓库中工作时提供指引。
CamTalk — 多模态实时 AI 视觉对话助手(摄像头 + 麦克风 + 视觉 + 语音 AI
## 项目概述
CamTalk 是一款多模态实时 AI 视觉对话助手。用户通过摄像头和麦克风与 AI 交互AI 理解视觉场景和语音输入后,以文字和语音形式给出自然回应。
> **文档优先原则:** 执行任何开发任务前,先读取 `docs/` 下的相关设计文档(架构、接口、技术选型等),以文档为最高依据。代码实现应与文档一致;若有偏差,优先更新文档(尤其是接口文档)。`docs/Eino/` 下有完整的 Eino 框架文档(~75 个 markdown 文件),可作为参考。
**核心设计文档:**
| 文档 | 内容 |
|------|------|
| `docs/01-架构设计.md` | 三层架构、技术栈、数据库设计、部署方案 |
| `docs/02-接口文档.md` | WebSocket 协议、REST API、AI 服务层、编排器、配置管理 |
| `docs/03-技术选型.md` | AI 服务栈、持久化层、前端边缘处理选型 |
| `docs/04-用户故事.md` | 用户场景与优先级 |
| `docs/05-语音交互.md` | VAD → STT → LLM → TTS 全链路 |
| `docs/06-视觉理解.md` | 帧采样、关键帧检测、多模态输入 |
| `docs/07-成本控制.md` | 采样策略、端云协同、模型分级 |
| `docs/08-功能创意.md` | 功能创意与规划 |
| `docs/09-技术名词解释.md` | 术语定义VAD/STT/TTS/Token/JWT 等) |
| `docs/10-Eino重构方案.md` | Eino Graph 迁移方案与决策记录 |
| `docs/11-Eino框架技术文档.md` | Eino 框架使用指南 |
| `docs/12-鉴权体系设计.md` | JWT 双 token 轮转详细设计 |
| `docs/13-令牌桶限流设计.md` | 令牌桶限流详细设计(需同步实现) |
> **文档优先原则:** 开发前先读 `docs/` 设计文档,以文档为准;若代码与文档不一致,优先更新文档(尤其接口文档)。详细设计见 `docs/01-13` 系列文档。**注意**`docs/Eino/` 框架文档内容庞大(~75 个文件),仅在需要了解 Eino Graph/节点/Callback 等框架细节时才读取。
## 架构
三层系统:
三层系统:前端React + Vite→ Go 网关Gin + WebSocket + Eino Graph AI 编排)→ AI 服务DashScope LLM, MiMo STT/TTS
1. **浏览器客户端**React 18 + TypeScript, Vite—— 媒体采集、边缘预处理VAD 通过 `@ricky0123/vad-web`、关键帧检测通过 Canvas 像素比较、UI 渲染。核心 Hook`useVisionSession()`
2. **Go 网关**Gin, gorilla/websocket, Viper, Zap—— WebSocket 服务器、会话管理、AI 编排(基于 CloudWeGo Eino Graph。每个 WebSocket 连接一个 goroutine。
3. **云端 AI 服务** —— 通过 OpenAI 兼容接口可灵活切换。默认DashScope qwen3-vl-plusLLM、MiMo ASRSTT、MiMo TTSTTS。仅通过 Go 网关访问,浏览器不直连。
**AI 编排流水线**Eino Graph 7 节点 DAG`STT → History → ChatModel → Msg2Str → Splitter → TTS → Done`。LLM token 通过 Callback 实时推送TTS 逐句并行合成。
**关键模式**AI 编排基于 Eino Graph 声明式 DAG6 节点线性流水线:`START → STT → History → ChatModel → Msg2Str → Splitter → TTS → Done → END`LLM token 通过 Callback 实时推送TTS 逐句合成并行推送,最小化感知延迟
**会话存储**TieredManagerL1 Memory → L2 Redis → L3 PostgreSQL 三级存储30 分钟 TTLRedis 故障自动降级
### Eino Graph 节点详解
| 节点 | 类型 | 文件 | 职责 |
|------|------|------|------|
| STT | `InvokableLambda` | `backend/internal/eino/nodes_stt.go` | 语音识别或文本直通text-only 跳过 STT |
| History | `InvokableLambda` | `backend/internal/eino/nodes_history.go` | 构建 System Prompt + 对话历史 + 用户输入 + 图像 |
| ChatModel | ChatModel 节点 | `backend/internal/eino/graph.go` | 调用 DashScope qwen3-vl-plusOpenAI 兼容协议) |
| Msg2Str | `TransformableLambda` | `backend/internal/eino/nodes_splitter.go` | 将 ChatModel 流式 Message 转为字符串流 |
| Splitter | `TransformableLambda` | `backend/internal/eino/nodes_splitter.go` | 按句子分隔符(`。!?\n.!?`)拆分文本流 |
| TTS | `TransformableLambda` | `backend/internal/eino/nodes_tts.go` | 逐句合成语音并推送 `tts_audio` |
| Done | `InvokableLambda` | `backend/internal/eino/nodes_done.go` | 发送 `llm_done`、收集最终输出 |
**跨节点状态**`PipelineState``backend/internal/eino/state.go`),通过 `context.WithValue` 在节点间传递 FullResponse、TranscribedText、TokenUsage、SessionID、RequestID。
**Callback**`BuildCallbackHandler``backend/internal/eino/callback.go`)挂载到 ChatModel 的 `OnEndWithStreamOutput`,每收到一个 LLM token 立即通过 `sender.SendLLMChunk()` 推送到客户端。
**适配器**`EinoOrchestrator``backend/internal/eino/adapter.go`)包装 Graph实现 `orchestrator.Orchestrator` 接口,负责解码 Base64 图像/音频、构建输入、注入上下文、运行流式推理、持久化消息。
### 会话存储TieredManager
三级存储:**L1 Memory → L2 Redis → L3 PostgreSQL**`backend/internal/session/tiered.go`
- **读路径**L1 命中直接返回;未命中尝试 L2 Redis → 回填 L1L3 通过 L1 的 `FindByID` 降级读取
- **写路径**L1 同步写入 → L2 同步写(失败 soft-warn→ L3 异步 goroutine 写(使用 `context.Background()` 防止请求取消丢失)
- **降级**:后台协程每 30 秒 ping RedisRedis 不可用时自动跳过 L2 操作;恢复后自动重新启用
- **TTL**Session 默认 30 分钟MaxHistory 20 条L1 后台协程每分钟清理过期 session
Repository 接口模式:`UserRepository``MessageRepository``SessionRepository`,均有 PostgreSQL 和内存双实现。
### 鉴权
JWT 双 token 轮转认证HMAC-SHA256
- Access Token默认 120 分钟 TTLBearer header 传递
- Refresh Token默认 7 天 TTL带 jtiUUIDHash 存储在 Redis/PostgreSQL
- 轮转Refresh 时旧 token hash 删除,新 pair 生成;若 JWT 有效但 DB hash 缺失 → 判定为重放攻击 → 吊销该用户所有 refresh token
- `CachedUserRepository``backend/internal/store/cached_user.go`装饰器模式Redis 缓存 refresh token hashRead-Through / Write-ThroughRedis 故障软降级
**鉴权**JWT 双 token 轮转Access 120min + Refresh 7d重放攻击检测DB hash 校验Redis 缓存装饰器。
## 技术栈
| 层级 | 技术 |
|------|------|
| 前端 | React 18, TypeScript, Vite, @ricky0123/vad-web, onnxruntime-web |
| 后端 | Go 1.25+ (go.mod 最低要求; Dockerfile 构建用 golang:1.26-alpine), Gin, gorilla/websocket, Viper, Zap |
| AI 编排 | CloudWeGo Eino Graph声明式 DAG 编排) |
| LLM | DashScope qwen3-vl-plus默认通过 eino-ext OpenAI ChatModel 接入) |
| STT | MiMo ASR默认 / Deepgram |
| TTS | MiMo TTS默认 / OpenAI TTS |
| 存储 | PostgreSQL 15 + Redis 7通过 TieredManager 三级存储) |
前端React 18 + TypeScript + ViteVAD@ricky0123/vad-webONNX Runtime
后端Go 1.25+, Gin, WebSocket, Viper, Zap, CloudWeGo Eino Graph
AIDashScope qwen3-vl-plus, MiMo ASR/TTS可切换 Deepgram/OpenAI TTS
存储PostgreSQL 15 + Redis 7
## 构建与运行命令
## 快速启动
```bash
# 前端
cd frontend && npm install
npm run dev # Vite 开发服务器(含 /ws、/api 代理到 localhost:8080
npm run build # 生产构建tsc -b && vite build
npm run lint # ESLint 检查flat config, TypeScript strict
# 后端
cd backend && go mod download
go run ./cmd/server # 启动网关,监听 :8080
go build -o bin/camtalk ./cmd/server
go test ./... # 运行所有测试
go test -run TestName ./path # 运行单个测试
go vet ./... # 静态分析
# Docker 部署(生产环境)
./deploy.sh build # 构建所有镜像
./deploy.sh up # 启动 4 个服务
./deploy.sh restart # down + up
./deploy.sh logs [service] # 查看日志
./deploy.sh status # 查看服务状态
# 前端npm run devVite代理 /ws 和 /api 到 :8080
# 后端go run ./cmd/server监听 :8080
# 生产:./deploy.sh up4 容器frontend/backend/postgres/redis
```
> **注意**:前端目前没有测试基础设施(无 vitest 配置、无测试文件)。后端使用 `testing` + `testify`assert/require/mock测试编译期接口检查 `var _ Interface = (*Impl)(nil)`。
核心环境变量(`.env.example``CAMTALK_AI_LLM_API_KEY`, `CAMTALK_AI_STT_API_KEY`, `CAMTALK_AUTH_JWT_SECRET`, `CAMTALK_STORAGE_DSN`
## 配置环境切换
配置优先级:环境变量 > `config.{APP_ENV}.yaml` > `config.yaml`
环境切换:`APP_ENV=dev|prod`dev 默认prod 启用限流 + 严格 CORS
项目通过 `APP_ENV` 环境变量控制配置文件加载:
## 协议与 API
- **本地开发**(默认):`APP_ENV=dev` → 加载 `config/config.dev.yaml`
- Debug 日志、关闭限流、允许所有 CORS
- 使用 `backend/.env` 中的远程 Redis/PostgreSQL 地址
**WebSocket**`ws://localhost:8080/ws?token=<jwt>&conversation_id=<uuid>`
- 客户端:`query`(图像/音频 Base64, `config`, `interrupt`, `ping`
- 服务端:`connected`, `stt_result`, `llm_chunk`, `llm_done`, `tts_audio`, `error`, `pong`
- 心跳:客户端 30s ping服务端 60s 超时断连;重连:指数退避 1s→30s
- 实现:`CamTalkWebSocket` 单例(`frontend/src/lib/websocket.ts`),订阅模式,自动重连
- **生产部署**`APP_ENV=prod` → 加载 `config/config.prod.yaml`
- Info/JSON 日志、启用限流、严格 CORS 白名单
- Docker Compose 自动设置,使用容器内网地址
**REST API**`/api/auth/*`(注册/登录/刷新/登出),`/api/conversations/*`CRUD + 消息分页),`/api/health`
**配置优先级**环境变量 > config.{env}.yaml > config.yaml > 默认值
**错误码**`INVALID_MESSAGE`, `SESSION_NOT_FOUND`, `RATE_LIMITED`, `IMAGE_TOO_LARGE`, `LLM_TIMEOUT`, `STT/TTS/LLM_ERROR`, `INVALID_TOKEN`, 等
**手动切换环境**(测试用):
```bash
cd backend
APP_ENV=prod go run ./cmd/server # 本地测试生产配置
APP_ENV=dev go run ./cmd/server # 显式指定开发配置
```
## 关键文件路径
## 配置系统
**后端核心**
- `backend/internal/eino/` — Graph 定义、节点、Callback、Adapter、State
- `backend/internal/session/tiered.go` — 三级会话存储
- `backend/internal/store/` — Repository 实现PG + 内存 + Redis 缓存)
- `backend/internal/ws/handler.go` — WebSocket 连接管理
- `backend/migrations/` — SQL 迁移文件
配置文件:`backend/config/config.yaml`(基础配置),可被 `config/config.{env}.yaml` 覆盖。
**优先级(从低到高)**:默认值 → `config.yaml``config.{env}.yaml`(由 `APP_ENV` 环境变量决定加载哪个 env 特定文件)→ `.env` 文件 → 环境变量
**主要 `CAMTALK_` 环境变量**(模板见 `backend/.env.example`
| 变量 | 用途 |
|------|------|
| `APP_ENV` | 运行环境dev/prod决定加载 `config.{env}.yaml` |
| `CAMTALK_AI_STT_API_KEY` | STT API Key |
| `CAMTALK_AI_LLM_API_KEY` | LLM API Key |
| `CAMTALK_AI_TTS_API_KEY` | TTS API Key |
| `CAMTALK_AUTH_JWT_SECRET` | JWT 签名密钥 |
| `CAMTALK_STORAGE_DSN` | PostgreSQL 连接串 |
| `CAMTALK_STORAGE_REDIS_ENABLED` | 启用 Redistrue/false |
| `CAMTALK_STORAGE_PERSISTENCE_ENABLED` | 启用 PostgreSQLtrue/false |
| `CAMTALK_REDIS_ADDR` | Redis 地址 |
| `CAMTALK_REDIS_PASSWORD` | Redis 密码 |
**最小启动**(至少需要一个 AI 服务的 API Key
```bash
CAMTALK_AI_LLM_API_KEY=sk-xxx CAMTALK_AI_STT_API_KEY=xxx go run ./cmd/server
```
## Docker 部署
`docker-compose.yml` 定义 4 个服务(`camtalk-net` 桥接网络):
| 服务 | 镜像/构建 | 端口 | 说明 |
|------|----------|------|------|
| `frontend` | 构建 `./frontend/Dockerfile`node:22-alpine → nginx:stable-alpine | 9000:80 | React SPA反向代理 /api 和 /ws 到 backend |
| `backend` | 构建 `./backend/Dockerfile`golang:1.26-alpine → alpine:3.20 | 内部 8080 | Go 网关,静态链接二进制 `-ldflags="-s -w"` |
| `postgres` | `postgres:15-alpine` | 内部 5432 | 数据库 `camtalk`,挂载 `./backend/migrations/` 到 initdb |
| `redis` | `redis:7-alpine` | 内部 6379 | 会话缓存AOF 持久化 |
后端容器依赖 postgres + redis 健康检查通过后启动。所有服务 `restart: unless-stopped`。密钥通过 `--env-file /opt/camtalk/.env` 注入。
**前端 nginx 特殊配置**:设置 `Cross-Origin-Opener-Policy``Cross-Origin-Embedder-Policy` 头(`SharedArrayBuffer` 需要ONNX WASM 推理依赖)。
## CI/CD
使用 **Gitea Actions**`.gitea/workflows/deploy.yml`),自托管 runner标签 `aliyun`)。
触发条件push 到 `main``v2` 分支。流程rsync 代码到 `/root/camtalk`,执行 `deploy.sh build``deploy.sh restart`
## 数据库迁移
嵌入式 SQL 迁移系统(`backend/internal/store/migrate.go`SQL 文件在 `backend/migrations/`
| 迁移 | 内容 |
|------|------|
| `001_users` | `users`UUID PK+ `refresh_tokens`FK → users |
| `002_messages` | `messages`BIGSERIAL PK, session_id UUID, 游标分页索引) |
| `003_sessions` | `sessions`UUID PK, user_id UUID, config JSONB, 时间排序索引) |
迁移文件通过 Go 1.16+ `//go:embed` 嵌入二进制,启动时自动执行。通过 `schema_migrations` 表追踪版本,已应用的迁移跳过。同时挂载到 PostgreSQL 容器的 `/docker-entrypoint-initdb.d` 作为备用初始化路径。
## WebSocket 协议
端点:`ws://localhost:8080/ws?token=<access_token>&conversation_id=<uuid>`
所有消息为 JSON 文本帧,统一信封格式 `{type, request_id?, timestamp?}`。完整契约见 `docs/02-接口文档.md`
**认证**WebSocket 连接通过 query param `token`Access Token认证不走 HTTP `Authorization` header。服务端在升级时校验 JWT失败返回 401。
**客户端 → 服务端**`query`(图像 Base64 + 音频 Base64`config``interrupt``ping`
**服务端 → 客户端**`connected``stt_result``llm_chunk``llm_done``tts_audio``error``pong`
**心跳**:客户端每 30 秒 ping服务端 60 秒无 ping 断开连接。
**重连**:指数退避 + 抖动 —— 1s, 2s, 4s, 8s… 最大 30s。
**前端 WebSocket 实现**`CamTalkWebSocket` 单例类(`frontend/src/lib/websocket.ts`),基于订阅模式(`onMessage`/`onStatusChange` 返回取消订阅函数),自动处理心跳和重连。
## REST API辅助
- `GET /api/health` — 健康检查(版本、运行时间、活跃会话数)
- `POST /api/auth/register` — 注册
- `POST /api/auth/login` — 登录
- `POST /api/auth/refresh` — 刷新 Token
- `POST /api/auth/logout` — 登出
- `GET /api/conversations` — 对话列表
- `POST /api/conversations` — 创建对话
- `GET/PATCH/DELETE /api/conversations/:id` — 对话详情/改标题/删除
- `GET /api/conversations/:id/messages` — 获取对话消息(游标分页)
- `POST/DELETE /api/sessions` — 会话管理
## 错误码
`INVALID_MESSAGE``SESSION_NOT_FOUND``RATE_LIMITED``IMAGE_TOO_LARGE``AUDIO_TOO_SHORT``LLM_TIMEOUT``LLM_ERROR``STT_ERROR``TTS_ERROR``INTERNAL_ERROR``USERNAME_TAKEN``INVALID_CREDENTIALS``INVALID_TOKEN``INVALID_INPUT`
## 前端组件结构
| 组件 | 职责 |
|------|------|
| `LandingPage` | 未登录时的着陆页,内嵌 LoginModal 登录/注册弹窗 |
| `AuthPage` | 登录/注册表单(备用) |
| `CameraManager` | 摄像头流采集(`useCamera` hook640x480, facingMode: environment |
| `MicManager` | 麦克风音频采集(`useMicrophone` hook16kHz 单声道) |
| `EdgeProcessor` | VAD`useVAD` hook@ricky0123/vad-web+ 关键帧检测Canvas 像素比较160x120 降采样) |
| `WebSocketManager` | WebSocket 连接生命周期管理(桥接 `wsClient` 单例到 React 状态) |
| `ChatPanel` | 消息展示、流式回复、文本输入、场景选择5 种场景卡片) |
| `VideoPreview` | 摄像头画面预览forwardRef `<video>` |
| `SessionSidebar` | 左侧抽屉式对话列表(搜索、重命名、删除、时间分组) |
| `ConfigPanel` | 右侧抽屉式配置面板主题、TTS、语言、场景、登出 |
| `Toast` | 轻量通知提示3 秒自动消失) |
核心 Hook
- `useVisionSession()` — 封装一次完整的视觉对话会话(~500 行管理摄像头、麦克风、VAD、WebSocket、消息状态、TTS 播放、两种模式dialogue / observation
- `useSessionList()` — 对话列表 CRUD通过 REST API乐观更新
- `useObservationMode()` — 定期帧差异检测5 秒间隔),相似度 < 0.85 时触发回调
关键 lib 文件:
- `frontend/src/lib/websocket.ts``CamTalkWebSocket` 单例,心跳 + 指数退避重连
- `frontend/src/lib/auth.tsx``AuthProvider` 上下文JWT 解码 + 自动刷新调度exp 前 60 秒)
- `frontend/src/lib/api.ts` — REST 客户端,自动 Bearer header并发安全 401 拦截 + token 刷新 + 重试
- `frontend/src/lib/ttsPlayer.ts``TTSPlayer` 类,流式 TTS 音频逐句排队播放
- `frontend/src/lib/scenarios.ts` — 5 种对话场景定义free_chat, interviewer, english_teacher, debate, interpreter
- `frontend/src/lib/i18n/` — 国际化3 种语言zh-CN 默认/fallback, en-US, ja-JP扁平常量 map
- `frontend/src/lib/storage.ts` — localStorage 封装config, tokens, user info
- `frontend/src/lib/audio.ts` — 音频编码工具(浏览器采集 → Base64 PCM
- `frontend/src/lib/sampling.ts` — 混合采样策略(定时低频 + 事件高频,实现见 `docs/07-成本控制.md`
- `frontend/src/lib/errors.ts` — 错误码到用户友好文案的映射
- `frontend/src/lib/toast.ts` — Toast 全局状态管理error/warning/info3 秒自动消失)
- `frontend/src/types/index.ts` — 所有 TypeScript 类型定义WebSocket 消息可辨识联合类型、场景、配置等)
## Vite 构建细节
`frontend/vite.config.ts` 包含:
- 自定义 `serve-vad-assets` 插件,在开发/构建时自动从 `node_modules` 复制 VAD 模型文件(`silero_vad_legacy.onnx``silero_vad_v5.onnx``vad.worklet.bundle.min.js`)和 ONNX Runtime WASM 文件到 `public/`。开发服务器中间件确保 `.wasm``.mjs` 文件返回正确的 MIME type。
- 开发代理:`/ws``ws://localhost:8080``/api``http://localhost:8080`,前端开发时无需配置额外环境变量。
## 后端模块结构
| 模块 | 职责 |
|------|------|
| WebSocket Handler | 连接管理、JWT 认证query param token、消息分发query/config/interrupt/ping |
| Session Manager | 会话状态、对话历史三级存储Memory/Redis/PostgreSQL30 分钟 TTL |
| Eino 编排层 | 基于 Eino Graph 的声明式 AI 编排6 节点线性 DAG + Callback |
| AI Orchestrator | `EinoOrchestrator` 适配器,包装 Graph 实现 `Orchestrator` 接口 |
| AI Service Layer | AI 服务抽象层STT/TTS 多 provider 接口LLM 通过 eino-ext ChatModel |
| Auth | JWT 双 token 轮转认证bcrypt 密码哈希Gin 中间件 |
| Store | 持久化存储层UserRepository/MessageRepository/SessionRepositoryPG + 内存 + Redis 缓存装饰器) |
| REST API | 健康检查、认证、对话管理Gin 路由组) |
| Models | 数据模型 + WebSocket 消息类型定义 |
| Migrations | 嵌入式 SQL 版本化迁移 |
| Rate Limiter | 按用户的令牌桶速率限制(`docs/13-令牌桶限流设计.md``backend/internal/ratelimit/`,实现中) |
测试模式:后端使用 `testing` + `testify``assert``require``mock`。mock 模式包括:`mockSender`(实现 `orchestrator.Sender` 接口)、`httptest.Server`(模拟 AI 服务 HTTP API`MockOrchestrator`模拟完整编排管道。WebSocket 集成测试使用 `httptest.Server` + `gorilla/websocket.Dialer`
**前端核心**
- `frontend/src/hooks/useVisionSession.ts` — 核心会话 Hook~500 行)
- `frontend/src/lib/websocket.ts` — WebSocket 客户端单例
- `frontend/src/lib/auth.tsx` — JWT 自动刷新 + AuthProvider
- `frontend/src/lib/api.ts` — REST 客户端401 拦截 + token 刷新)
- `frontend/src/lib/ttsPlayer.ts` — 流式 TTS 音频播放队列
- `frontend/vite.config.ts` — VAD 模型文件自动复制 + 代理配置
## 编码规范
- **Go**遵循标准 Go 规范。所有 AI 调用使用 `context.Context` 做取消/超时。并发 map 访问使用 `sync.RWMutex`。结构体标签用 `json:"snake_case"`编译期接口检查 `var _ Interface = (*Impl)(nil)`
- **TypeScript**:严格模式`strict: true`)。所有数据模型用接口定义。WebSocket 消息类型用可辨识联合类型(`type` 字段)`verbatimModuleSyntax: true`(强制 `import type`)。未使用变量以 `_` 前缀忽略。
- **CORS 处理**:禁止后端代码配置文件`config/*.yaml`)中进行任何 CORS 配置。跨域由代理层统一处理开发环境通过 `frontend/vite.config.ts` 中的 proxy 配置(`/ws``/api` 代理到 `localhost:8080`,生产环境通过 Nginx 反向代理(`frontend/nginx.conf`)。
- **提交信息**Conventional Commits 格式,描述用中文。示例:`feat: 添加 WebSocket 连接管理``fix: 修复心跳超时判断``docs: 更新接口文档`
- **禁止自动 push**:除非用户明确要求
- **文档优先**实现功能前先读 `docs/` 下的相关设计文档。实现与文档不一致时优先更新 `docs/` 下的接口文档
- **Go**标准规范,`context.Context` 超时控制,`sync.RWMutex` 并发保护,`json:"snake_case"` 标签,编译期接口检查 `var _ Interface = (*Impl)(nil)`
- **TypeScript**:严格模式,接口定义数据模型,WebSocket 消息用可辨识联合类型(`type` 字段区分
- **CORS**:禁止后端代码/配置文件配置 CORS统一由代理层处理开发环境 Vite proxy,生产环境 Nginx
- **提交信息**Conventional Commits,中文描述(如 `feat: 添加 WebSocket 心跳`
- **禁止自动 push**:除非用户明确要求
- **文档优先**开发前先读 `docs/` 设计文档,代码与文档不一致时优先更新文档