docs: 文档与代码一致性检查与修复
- 修复心跳 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:同步更新技术栈、模块结构、存储策略描述
This commit is contained in:
@@ -73,7 +73,7 @@ interface ConfigMessage {
|
||||
```typescript
|
||||
interface InterruptMessage {
|
||||
type: "interrupt";
|
||||
request_id?: string; // 可选,指定打断哪次请求
|
||||
request_id?: string; // 可选,当前实现不使用此字段,服务端始终取消当前活跃请求
|
||||
}
|
||||
```
|
||||
|
||||
@@ -143,7 +143,7 @@ interface TTSAudioMessage {
|
||||
type: "tts_audio";
|
||||
request_id: string;
|
||||
audio: string; // Base64 编码的音频片段
|
||||
mime_type: string; // "audio/mpeg"
|
||||
mime_type: string; // "audio/mp3"
|
||||
is_last: boolean; // 是否为最后一片
|
||||
}
|
||||
```
|
||||
@@ -152,7 +152,7 @@ interface TTSAudioMessage {
|
||||
|
||||
| 属性 | 值 | 说明 |
|
||||
|------|------|------|
|
||||
| 编码 | `audio/mpeg`(MP3) | 浏览器 `<audio>` 原生支持,OpenAI TTS 默认输出 |
|
||||
| 编码 | `audio/mp3`(MP3) | 浏览器 `<audio>` 原生支持,OpenAI TTS 默认输出 |
|
||||
| 采样率 | 24kHz | OpenAI TTS 默认 |
|
||||
| 声道 | 单声道 | 语音不需要立体声 |
|
||||
| 传输 | Base64 编码的 MP3 片段 | 每个 `tts_audio` 消息携带一个句子的音频 |
|
||||
@@ -165,7 +165,7 @@ interface TTSAudioMessage {
|
||||
1. **排队播放**:收到 `tts_audio` 时,将 Base64 解码为 Blob URL 并加入播放队列。第一片到达即开始播放,后续片段在 `onended` 回调中自动衔接。
|
||||
2. **错误容错**:单个片段播放失败时跳过,继续播放队列中下一个,不中断整个回复。
|
||||
3. **打断清理**:收到 `interrupt` 消息或用户触发打断时,清空播放队列并释放所有 Blob URL。
|
||||
4. **类型锁定**:`mime_type` 字段固定为 `"audio/mpeg"`,前端解码时直接使用,无需运行时判断。
|
||||
4. **类型锁定**:`mime_type` 字段固定为 `"audio/mp3"`,前端解码时直接使用,无需运行时判断。
|
||||
|
||||
```typescript
|
||||
// 前端播放器伪代码
|
||||
@@ -173,7 +173,7 @@ class AudioPlayer {
|
||||
private queue: string[] = []; // Blob URL 队列
|
||||
|
||||
enqueue(base64: string) {
|
||||
const url = decodeBase64Audio(base64, "audio/mpeg");
|
||||
const url = decodeBase64Audio(base64, "audio/mp3");
|
||||
this.queue.push(url);
|
||||
if (this.queue.length === 1) this.playNext(); // 第一片到了就开始播
|
||||
}
|
||||
@@ -293,7 +293,7 @@ DELETE /api/sessions/{session_id}
|
||||
|
||||
## 三、AI 服务层接口
|
||||
|
||||
Go 网关内部与外部 AI 服务(Deepgram STT、GPT-4o、OpenAI TTS)的调用契约。前后端联调时,后端需实现这些接口。
|
||||
Go 网关内部与外部 AI 服务(STT、LLM、TTS)的调用契约。默认配置为 Deepgram STT、GPT-4o LLM、OpenAI TTS,但通过 OpenAI 兼容接口可灵活切换到其他服务商(如 MiMo ASR、通义千问等)。前后端联调时,后端需实现这些接口。
|
||||
|
||||
### STT 服务接口
|
||||
|
||||
@@ -370,7 +370,7 @@ user: [图片 + 用户语音文本]
|
||||
```
|
||||
|
||||
- 超时:10 秒,超时返回 `LLM_TIMEOUT` 错误
|
||||
- 模型选择:默认 `gpt-4o`,由 Model Router 按需切换
|
||||
- 模型选择:默认 `gpt-4o`,可通过配置切换到其他 OpenAI 兼容模型
|
||||
|
||||
### TTS 服务接口
|
||||
|
||||
@@ -644,7 +644,7 @@ backend/config.dev.yaml # 开发环境(go run 时使用)
|
||||
backend/config.prod.yaml # 生产环境
|
||||
```
|
||||
|
||||
Viper 加载顺序:先读 `config.yaml`,再根据 `APP_ENV` 环境变量尝试读 `config.{env}.yaml` 覆盖,最后所有环境变量自动覆盖对应字段。
|
||||
Viper 加载顺序:先读 `config.yaml`,再根据 `APP_ENV` 环境变量尝试读 `config.{env}.yaml` 覆盖,最后所有环境变量自动覆盖对应字段。此外,代码还通过 `godotenv` 加载 `.env` 文件(优先级最低,仅用于本地开发环境)。
|
||||
|
||||
### Go 配置结构体
|
||||
|
||||
@@ -686,6 +686,7 @@ type AIConfig struct {
|
||||
type STTConfig struct {
|
||||
Provider string `mapstructure:"provider"` // "deepgram"
|
||||
APIKey string `mapstructure:"api_key"`
|
||||
Model string `mapstructure:"model"` // 默认 "nova-2"
|
||||
Endpoint string `mapstructure:"endpoint"` // 默认 "wss://api.deepgram.com/v1/listen"
|
||||
}
|
||||
|
||||
@@ -700,6 +701,7 @@ type LLMConfig struct {
|
||||
type TTSConfig struct {
|
||||
Provider string `mapstructure:"provider"` // "openai"
|
||||
APIKey string `mapstructure:"api_key"`
|
||||
Model string `mapstructure:"model"` // 默认 "tts-1"
|
||||
Voice string `mapstructure:"voice"` // 默认 "alloy"
|
||||
Speed float64 `mapstructure:"speed"` // 默认 1.0
|
||||
Endpoint string `mapstructure:"endpoint"` // 默认 "https://api.openai.com/v1"
|
||||
@@ -738,6 +740,7 @@ redis:
|
||||
ai:
|
||||
stt:
|
||||
provider: deepgram
|
||||
model: nova-2
|
||||
endpoint: "wss://api.deepgram.com/v1/listen"
|
||||
llm:
|
||||
provider: openai
|
||||
@@ -746,6 +749,7 @@ ai:
|
||||
timeout: 10
|
||||
tts:
|
||||
provider: openai
|
||||
model: tts-1
|
||||
voice: alloy
|
||||
speed: 1.0
|
||||
endpoint: "https://api.openai.com/v1"
|
||||
@@ -791,8 +795,9 @@ func Load() (*Config, error) {
|
||||
// 1. 读默认配置文件
|
||||
v.SetConfigName("config")
|
||||
v.SetConfigType("yaml")
|
||||
v.AddConfigPath("./config") // go run 时
|
||||
v.AddConfigPath(".") // 二进制运行时
|
||||
v.AddConfigPath(".")
|
||||
v.AddConfigPath("./config")
|
||||
v.AddConfigPath("./backend")
|
||||
if err := v.ReadInConfig(); err != nil {
|
||||
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||||
return nil, fmt.Errorf("read config: %w", err)
|
||||
@@ -800,7 +805,7 @@ func Load() (*Config, error) {
|
||||
}
|
||||
|
||||
// 2. 按环境覆盖
|
||||
env := os.Getenv("CAMTALK_APP_ENV")
|
||||
env := os.Getenv("APP_ENV")
|
||||
if env == "" {
|
||||
env = "dev"
|
||||
}
|
||||
@@ -1030,7 +1035,7 @@ func NewApp(cfg *Config) *App {
|
||||
|
||||
## 十、连接管理
|
||||
|
||||
**心跳机制**:客户端每 30 秒发送 `ping`,服务端回复 `pong`。超过 60 秒无 `ping`,服务端判定连接断开并清理会话资源。
|
||||
**心跳机制**:客户端每 30 秒发送应用层 `{type: "ping"}` 消息,服务端回复 `{type: "pong"}` 并刷新心跳计时器。超过 60 秒无 `ping`,服务端判定连接断开并清理会话资源。
|
||||
|
||||
**重连策略**(指数退避 + 抖动):
|
||||
|
||||
@@ -1049,18 +1054,10 @@ function reconnect(attempt: number) {
|
||||
|
||||
**生产环境**:Nginx 将 `/`(前端)、`/api/*`(REST)、`/ws`(WebSocket)统一反代到同一域名,详见 `02-系统架构.md` 部署架构章节。
|
||||
|
||||
**开发环境**:Vite 内置代理,前端 :5173 的 `/api` 和 `/ws` 请求代理到后端 :8080:
|
||||
**开发环境**:前端 WebSocket 地址硬编码为 `ws://localhost:8080/ws`,直连后端,不经过 Vite 代理。REST API 同理直连 `http://localhost:8080`。
|
||||
|
||||
```typescript
|
||||
// frontend/vite.config.ts
|
||||
server: {
|
||||
proxy: {
|
||||
"/api": "http://localhost:8080",
|
||||
"/ws": { target: "ws://localhost:8080", ws: true },
|
||||
},
|
||||
},
|
||||
```
|
||||
> 当前开发模式为前后端直连,未使用 Vite proxy。如需使用 Vite 代理解决跨域,需在 `vite.config.ts` 中添加 `server.proxy` 配置,并将前端 WebSocket 地址改为相对路径。
|
||||
|
||||
**Go 后端 WebSocket CheckOrigin**:生产环境 Nginx 同源,`CheckOrigin` 可保持默认(拒绝跨域)。开发环境由 Vite proxy 转发,不存在跨域。因此后端无需配置 CORS 中间件,`CheckOrigin` 保持 gorilla/websocket 默认值即可。
|
||||
**Go 后端 WebSocket CheckOrigin**:生产环境 Nginx 同源,`CheckOrigin` 可保持默认(拒绝跨域)。开发环境前端直连后端,需确保 `CheckOrigin` 允许跨域或使用 Vite proxy 转发。
|
||||
|
||||
> 如果未来需要支持第三方客户端直连(如移动端),再按需添加 CORS 中间件和 `CheckOrigin` 白名单。
|
||||
|
||||
Reference in New Issue
Block a user