# 接口文档 ## 概述 前后端通信接口定义。以 WebSocket 承载实时对话,REST 端点支撑基础运维。持久化通过 PostgreSQL 实现,MemoryManager 支持 Write-Through 模式。 **设计原则**: - WebSocket 为主:所有对话数据走 WebSocket - REST 为辅:仅用于健康检查、认证、对话管理等低频操作 - 接口先行:先定义契约,再填充实现——前后端可并行开发 ## 接口全景 ``` 浏览器 Go Gateway :8080 WebSocket Client <--> /ws?token= (实时对话,需 JWT 认证) HTTP Client --> GET /api/health (健康检查) HTTP Client <--> POST /api/auth/* (注册/登录/刷新/登出) HTTP Client <--> GET/POST/PATCH/DELETE (对话 CRUD) /api/conversations/* HTTP Client <--> GET /api/conversations/:id (历史消息) /messages ``` --- ## 一、WebSocket 协议 连接地址:`ws://localhost:8080/ws?token=&conversation_id=` | 参数 | 必填 | 说明 | |------|------|------| | `token` | 是 | JWT access_token,缺失或无效时返回 401 | | `conversation_id` | 否 | 恢复已有对话;省略则创建新对话 | ### 消息格式约定 所有 WebSocket 消息均为 JSON 文本帧,统一结构: ```typescript interface WsMessage { type: string; // 消息类型,必填 request_id?: string; // 可选,用于请求-响应关联 timestamp?: number; // 可选,毫秒时间戳 [key: string]: any; // 类型特定字段 } ``` ### 客户端 → 服务端消息 #### `query` — 发起一次视觉对话 用户说完话后,客户端同时发送当前图像帧和语音片段。也支持文本输入模式(手动输入文字时跳过语音识别): ```typescript interface QueryMessage { type: "query"; request_id: string; // 客户端生成的 UUID image: string; // Base64 编码的 JPEG 图像(不含 data: 前缀) audio: string; // Base64 编码的音频片段(PCM 16kHz),文本输入时为空字符串 text?: string; // 用户手动输入的文本(有值时跳过 STT,直接使用此文本) mime_type?: string; // 音频格式,默认 "audio/pcm" } ``` > 为什么图像和音频放在同一条消息里?因为 VAD 检测到用户说完话时,需要同时捕获"此刻的画面"和"说的话",拆成两条消息会增加时序同步的复杂度。 > > **文本输入模式**:当用户关闭麦克风后,可通过对话框手动输入文字。此时 `text` 字段携带用户输入,`audio` 为空字符串,服务端跳过 STT 直接使用 `text` 进行 LLM 推理。 #### `config` — 更新会话配置 ```typescript interface ConfigMessage { type: "config"; payload: { tts_enabled?: boolean; // 是否开启语音合成,默认 true detail_level?: "low" | "high"; // 图像精度,默认 "low" language?: string; // 交互语言,默认 "zh-CN" scenario?: string; // 场景模式:free_chat / interviewer / english_teacher / debate / interpreter }; } ``` #### `interrupt` — 打断当前回复 ```typescript interface InterruptMessage { type: "interrupt"; request_id?: string; // 可选,当前实现不使用此字段,服务端始终取消当前活跃请求 } ``` #### `ping` — 心跳保活 ```typescript interface PingMessage { type: "ping"; } ``` ### 服务端 → 客户端消息 #### `connected` — 连接建立确认 ```typescript interface ConnectedMessage { type: "connected"; session_id: string; // 服务端生成的会话 ID conversation_id: string; // 同 session_id,便于前端统一使用 server_version: string; // 服务端版本号,如 "0.1.0" } ``` #### `stt_result` — 语音识别结果 ```typescript interface STTResultMessage { type: "stt_result"; request_id: string; text: string; // 识别出的用户语音文本 is_final: boolean; // 是否为最终结果 } ``` #### `llm_chunk` — LLM 流式输出片段 ```typescript interface LLMChunkMessage { type: "llm_chunk"; request_id: string; delta: string; // 本次增量文本 role: "assistant"; } ``` #### `llm_done` — LLM 输出完成 ```typescript interface LLMDoneMessage { type: "llm_done"; request_id: string; full_text: string; // 完整回复文本 tokens_used: { prompt: number; completion: number; total: number; }; model: string; // 实际使用的模型名 latency_ms: number; // 端到端延迟(毫秒) } ``` #### `tts_audio` — TTS 音频流片段 ```typescript interface TTSAudioMessage { type: "tts_audio"; request_id: string; audio: string; // Base64 编码的音频片段 mime_type: string; // "audio/mp3" is_last: boolean; // 当前句子的音频是否完整(每句结束时为 true) final: boolean; // 整轮 TTS 是否结束(所有句子合成完毕后为 true) } ``` **字段语义**: - `is_last`: 每个句子合成完毕后为 `true`,前端收到此信号即可将该句子加入播放队列。每句 TTS 音频由一次独立的 API 调用生成,对应一个 `tts_audio` 消息。 - `final`: 所有句子合成完毕后为 `true`(此时 `audio` 为空字符串),用于前端判断本轮 TTS 已全部到齐。 **音频格式规范**(前端播放依赖此约定): | 属性 | 值 | 说明 | |------|------|------| | 编码 | `audio/mp3`(MP3) | 浏览器 `