2026-06-12 17:08:20 +08:00
|
|
|
|
# 接口文档
|
|
|
|
|
|
|
|
|
|
|
|
## 概述
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
前后端通信接口契约。WebSocket 承载实时对话,REST API 支撑基础运维。
|
2026-06-12 17:08:20 +08:00
|
|
|
|
|
|
|
|
|
|
**设计原则**:
|
|
|
|
|
|
- WebSocket 为主:所有对话数据走 WebSocket
|
2026-06-19 15:31:52 +08:00
|
|
|
|
- REST 为辅:仅用于健康检查、认证、对话管理等低频操作
|
2026-06-12 17:08:20 +08:00
|
|
|
|
- 接口先行:先定义契约,再填充实现——前后端可并行开发
|
|
|
|
|
|
|
|
|
|
|
|
## 接口全景
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
浏览器 Go Gateway :8080
|
2026-06-14 18:05:50 +08:00
|
|
|
|
WebSocket Client <--> /ws?token=<jwt> (实时对话,需 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
|
2026-06-12 17:08:20 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 一、WebSocket 协议
|
|
|
|
|
|
|
2026-06-14 18:05:50 +08:00
|
|
|
|
连接地址:`ws://localhost:8080/ws?token=<access_token>&conversation_id=<uuid>`
|
|
|
|
|
|
|
|
|
|
|
|
| 参数 | 必填 | 说明 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| `token` | 是 | JWT access_token,缺失或无效时返回 401 |
|
|
|
|
|
|
| `conversation_id` | 否 | 恢复已有对话;省略则创建新对话 |
|
|
|
|
|
|
|
2026-06-12 17:08:20 +08:00
|
|
|
|
### 消息格式约定
|
|
|
|
|
|
|
|
|
|
|
|
所有 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: 前缀)
|
2026-06-14 12:52:39 +08:00
|
|
|
|
audio: string; // Base64 编码的音频片段(PCM 16kHz),文本输入时为空字符串
|
|
|
|
|
|
text?: string; // 用户手动输入的文本(有值时跳过 STT,直接使用此文本)
|
2026-06-12 17:08:20 +08:00
|
|
|
|
mime_type?: string; // 音频格式,默认 "audio/pcm"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### `config` — 更新会话配置
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
interface ConfigMessage {
|
|
|
|
|
|
type: "config";
|
|
|
|
|
|
payload: {
|
|
|
|
|
|
tts_enabled?: boolean; // 是否开启语音合成,默认 true
|
|
|
|
|
|
detail_level?: "low" | "high"; // 图像精度,默认 "low"
|
|
|
|
|
|
language?: string; // 交互语言,默认 "zh-CN"
|
2026-06-19 15:31:52 +08:00
|
|
|
|
scenario?: string; // 场景模式:free_chat / interviewer / english_teacher / debate / interpreter
|
2026-06-12 17:08:20 +08:00
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### `interrupt` — 打断当前回复
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
interface InterruptMessage {
|
|
|
|
|
|
type: "interrupt";
|
2026-06-14 08:52:36 +08:00
|
|
|
|
request_id?: string; // 可选,当前实现不使用此字段,服务端始终取消当前活跃请求
|
2026-06-12 17:08:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### `ping` — 心跳保活
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
interface PingMessage {
|
|
|
|
|
|
type: "ping";
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 服务端 → 客户端消息
|
|
|
|
|
|
|
|
|
|
|
|
#### `connected` — 连接建立确认
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
interface ConnectedMessage {
|
|
|
|
|
|
type: "connected";
|
2026-06-21 14:48:03 +08:00
|
|
|
|
session_id: string;
|
|
|
|
|
|
conversation_id: string;
|
|
|
|
|
|
config: {
|
|
|
|
|
|
tts_enabled: boolean;
|
|
|
|
|
|
detail_level: "low" | "high";
|
|
|
|
|
|
language: string;
|
|
|
|
|
|
scenario: string;
|
|
|
|
|
|
};
|
2026-06-12 17:08:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### `stt_result` — 语音识别结果
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
2026-06-21 14:48:03 +08:00
|
|
|
|
interface SttResultMessage {
|
2026-06-12 17:08:20 +08:00
|
|
|
|
type: "stt_result";
|
|
|
|
|
|
request_id: string;
|
2026-06-21 14:48:03 +08:00
|
|
|
|
text: string; // 识别出的文本
|
|
|
|
|
|
is_final: boolean; // 当前实现始终为 true
|
2026-06-12 17:08:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `llm_chunk` — LLM 流式响应片段
|
2026-06-12 17:08:20 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2026-06-21 14:48:03 +08:00
|
|
|
|
interface LlmChunkMessage {
|
2026-06-12 17:08:20 +08:00
|
|
|
|
type: "llm_chunk";
|
|
|
|
|
|
request_id: string;
|
2026-06-21 14:48:03 +08:00
|
|
|
|
content: string; // 当前 token 片段
|
2026-06-12 17:08:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `llm_done` — LLM 响应完成
|
2026-06-12 17:08:20 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2026-06-21 14:48:03 +08:00
|
|
|
|
interface LlmDoneMessage {
|
2026-06-12 17:08:20 +08:00
|
|
|
|
type: "llm_done";
|
|
|
|
|
|
request_id: string;
|
2026-06-21 14:48:03 +08:00
|
|
|
|
full_text: string; // 完整响应文本
|
2026-06-12 17:08:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `tts_audio` — TTS 音频片段
|
|
|
|
|
|
|
|
|
|
|
|
音频流式推送,每个消息携带一个句子的音频数据。
|
2026-06-12 17:08:20 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2026-06-21 14:48:03 +08:00
|
|
|
|
interface TtsAudioMessage {
|
2026-06-12 17:08:20 +08:00
|
|
|
|
type: "tts_audio";
|
|
|
|
|
|
request_id: string;
|
2026-06-21 14:48:03 +08:00
|
|
|
|
audio: string; // Base64 编码的音频数据
|
|
|
|
|
|
format: string; // 音频格式
|
|
|
|
|
|
sample_rate: number; // 采样率(Hz)
|
|
|
|
|
|
sequence: number; // 句子序号,从 0 开始递增
|
|
|
|
|
|
is_final: boolean; // 是否为最后一个句子
|
2026-06-12 17:08:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**音频格式约束**:
|
2026-06-13 13:18:17 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
| 字段 | 值 | 说明 |
|
|
|
|
|
|
|------|-----|------|
|
|
|
|
|
|
| `format` | `"pcm"` | 线性 PCM,小端序 |
|
|
|
|
|
|
| `sample_rate` | `24000` | 24kHz 采样率 |
|
|
|
|
|
|
| 位深度 | 16-bit | 单声道 |
|
|
|
|
|
|
| 句子划分 | 按标点符号(。!?;:)分割 | 服务端按句分割 LLM 响应,并行合成 |
|
2026-06-13 13:18:17 +08:00
|
|
|
|
|
2026-06-12 17:08:20 +08:00
|
|
|
|
#### `error` — 错误通知
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
interface ErrorMessage {
|
|
|
|
|
|
type: "error";
|
2026-06-21 14:48:03 +08:00
|
|
|
|
request_id?: string; // 关联的请求 ID,全局错误时为空
|
|
|
|
|
|
code: string; // 错误码,见下文错误码表
|
2026-06-12 17:08:20 +08:00
|
|
|
|
message: string; // 人类可读的错误描述
|
2026-06-21 14:48:03 +08:00
|
|
|
|
details?: any; // 可选的详细错误信息
|
2026-06-12 17:08:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### `pong` — 心跳响应
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
interface PongMessage {
|
|
|
|
|
|
type: "pong";
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
### 连接管理
|
2026-06-14 12:52:39 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
- **心跳机制**:客户端每 30 秒发送 `ping`,服务端回复 `pong`;60 秒无活动则服务端断开连接
|
|
|
|
|
|
- **重连策略**:客户端断线后指数退避重连(1s → 2s → 4s → ... → 最大 30s)
|
|
|
|
|
|
- **并发控制**:同一连接同时只能有一个活跃的 `query` 请求;新请求到来时自动取消旧请求
|
2026-06-14 12:52:39 +08:00
|
|
|
|
|
2026-06-12 17:08:20 +08:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 二、REST API
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
所有 REST 端点均使用 JSON 格式。
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
### 2.1 健康检查
|
2026-06-12 17:08:20 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `GET /api/health`
|
2026-06-20 16:51:03 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
检查服务健康状态。
|
2026-06-12 17:08:20 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**响应**:
|
2026-06-12 17:08:20 +08:00
|
|
|
|
```json
|
|
|
|
|
|
{
|
2026-06-21 14:48:03 +08:00
|
|
|
|
"status": "healthy",
|
|
|
|
|
|
"timestamp": "2024-01-15T10:30:00Z",
|
|
|
|
|
|
"dependencies": {
|
|
|
|
|
|
"database": "healthy",
|
|
|
|
|
|
"redis": "healthy"
|
|
|
|
|
|
}
|
2026-06-12 17:08:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
### 2.2 认证 API
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `POST /api/auth/register` — 用户注册
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**请求**:
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"username": "alice",
|
|
|
|
|
|
"email": "alice@example.com",
|
|
|
|
|
|
"password": "SecurePass123!"
|
2026-06-14 18:05:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**响应**(200 OK):
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"user": {
|
|
|
|
|
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
|
"username": "alice",
|
|
|
|
|
|
"email": "alice@example.com",
|
|
|
|
|
|
"created_at": "2024-01-15T10:30:00Z"
|
|
|
|
|
|
},
|
|
|
|
|
|
"access_token": "eyJhbGc...",
|
|
|
|
|
|
"refresh_token": "eyJhbGc...",
|
|
|
|
|
|
"expires_in": 7200
|
2026-06-14 18:05:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**错误**:
|
|
|
|
|
|
- `400 INVALID_INPUT`: 参数验证失败
|
|
|
|
|
|
- `409 USER_EXISTS`: 用户名或邮箱已存在
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `POST /api/auth/login` — 用户登录
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**请求**:
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"username": "alice",
|
|
|
|
|
|
"password": "SecurePass123!"
|
|
|
|
|
|
}
|
2026-06-14 18:05:50 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**响应**(200 OK):
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"user": {
|
|
|
|
|
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
|
"username": "alice",
|
|
|
|
|
|
"email": "alice@example.com"
|
|
|
|
|
|
},
|
|
|
|
|
|
"access_token": "eyJhbGc...",
|
|
|
|
|
|
"refresh_token": "eyJhbGc...",
|
|
|
|
|
|
"expires_in": 7200
|
2026-06-14 18:05:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**错误**:
|
|
|
|
|
|
- `400 INVALID_INPUT`: 参数缺失
|
|
|
|
|
|
- `401 INVALID_CREDENTIALS`: 用户名或密码错误
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `POST /api/auth/refresh` — 刷新 Access Token
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**请求头**:
|
2026-06-14 18:05:50 +08:00
|
|
|
|
```
|
2026-06-21 14:48:03 +08:00
|
|
|
|
Authorization: Bearer <refresh_token>
|
2026-06-14 18:05:50 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**响应**(200 OK):
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"access_token": "eyJhbGc...",
|
|
|
|
|
|
"refresh_token": "eyJhbGc...",
|
|
|
|
|
|
"expires_in": 7200
|
2026-06-14 18:05:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**错误**:
|
|
|
|
|
|
- `401 INVALID_TOKEN`: Refresh Token 无效或过期
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `POST /api/auth/logout` — 用户登出
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**请求头**:
|
2026-06-14 18:05:50 +08:00
|
|
|
|
```
|
|
|
|
|
|
Authorization: Bearer <access_token>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**响应**(200 OK):
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"message": "Logged out successfully"
|
2026-06-14 18:05:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
### 2.3 对话管理 API
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
所有端点均需 JWT 认证(`Authorization: Bearer <access_token>`)。
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `GET /api/conversations` — 获取对话列表
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
|
|
|
|
|
**查询参数**:
|
2026-06-21 14:48:03 +08:00
|
|
|
|
- `page`: 页码,从 1 开始,默认 1
|
|
|
|
|
|
- `page_size`: 每页条数,默认 20,最大 100
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**响应**(200 OK):
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"conversations": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
|
"title": "关于植物的对话",
|
|
|
|
|
|
"created_at": "2024-01-15T10:30:00Z",
|
|
|
|
|
|
"updated_at": "2024-01-15T11:45:00Z",
|
|
|
|
|
|
"message_count": 12
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
"total": 42,
|
|
|
|
|
|
"page": 1,
|
|
|
|
|
|
"page_size": 20
|
2026-06-14 18:05:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `POST /api/conversations` — 创建新对话
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**请求**:
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"title": "新的对话"
|
2026-06-12 17:08:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**响应**(201 Created):
|
2026-06-14 18:05:50 +08:00
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
2026-06-21 14:48:03 +08:00
|
|
|
|
"title": "新的对话",
|
|
|
|
|
|
"created_at": "2024-01-15T10:30:00Z",
|
|
|
|
|
|
"updated_at": "2024-01-15T10:30:00Z",
|
|
|
|
|
|
"message_count": 0
|
2026-06-14 18:05:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
2026-06-12 17:08:20 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `GET /api/conversations/:id` — 获取对话详情
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**响应**(200 OK):
|
2026-06-14 18:05:50 +08:00
|
|
|
|
```json
|
|
|
|
|
|
{
|
2026-06-21 14:48:03 +08:00
|
|
|
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
|
"title": "关于植物的对话",
|
|
|
|
|
|
"created_at": "2024-01-15T10:30:00Z",
|
|
|
|
|
|
"updated_at": "2024-01-15T11:45:00Z",
|
|
|
|
|
|
"message_count": 12
|
2026-06-13 13:18:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**错误**:
|
|
|
|
|
|
- `404 NOT_FOUND`: 对话不存在或无权访问
|
2026-06-20 13:24:53 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `PATCH /api/conversations/:id` — 更新对话
|
2026-06-13 13:18:17 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**请求**:
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"title": "修改后的标题"
|
2026-06-13 13:18:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**响应**(200 OK):
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
|
"title": "修改后的标题",
|
|
|
|
|
|
"created_at": "2024-01-15T10:30:00Z",
|
|
|
|
|
|
"updated_at": "2024-01-15T12:00:00Z",
|
|
|
|
|
|
"message_count": 12
|
2026-06-20 13:24:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `DELETE /api/conversations/:id` — 删除对话
|
2026-06-19 14:58:43 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**响应**(204 No Content):无响应体
|
2026-06-19 14:58:43 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**错误**:
|
|
|
|
|
|
- `404 NOT_FOUND`: 对话不存在或无权访问
|
2026-06-13 13:24:25 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
#### `GET /api/conversations/:id/messages` — 获取对话消息
|
2026-06-19 15:31:52 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**查询参数**:
|
|
|
|
|
|
- `page`: 页码,从 1 开始,默认 1
|
|
|
|
|
|
- `page_size`: 每页条数,默认 50,最大 100
|
2026-06-13 13:24:25 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**响应**(200 OK):
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"messages": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"id": "660e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
|
"conversation_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
|
"role": "user",
|
|
|
|
|
|
"content": "这是什么植物?",
|
|
|
|
|
|
"image_url": "/api/images/abc123.jpg",
|
|
|
|
|
|
"created_at": "2024-01-15T10:30:00Z"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
"id": "770e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
|
"conversation_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
|
"role": "assistant",
|
|
|
|
|
|
"content": "这是一株向日葵...",
|
|
|
|
|
|
"created_at": "2024-01-15T10:30:15Z"
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
"total": 12,
|
|
|
|
|
|
"page": 1,
|
|
|
|
|
|
"page_size": 50
|
2026-06-19 15:31:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
2026-06-19 14:58:43 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**消息字段说明**:
|
|
|
|
|
|
- `role`: `"user"` 或 `"assistant"`
|
|
|
|
|
|
- `image_url`: 仅 `user` 消息可能包含,指向存储的图像
|
|
|
|
|
|
- `content`: 消息文本内容
|
2026-06-13 13:24:25 +08:00
|
|
|
|
|
2026-06-19 15:31:52 +08:00
|
|
|
|
---
|
2026-06-13 13:24:25 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
## 三、错误码表
|
2026-06-19 15:31:52 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
所有错误均使用以下格式:
|
2026-06-20 13:24:53 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "ERROR_CODE",
|
|
|
|
|
|
"message": "Human-readable error description",
|
|
|
|
|
|
"details": {}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### WebSocket 错误码
|
|
|
|
|
|
|
|
|
|
|
|
| 错误码 | 说明 | HTTP 状态码(若适用)|
|
|
|
|
|
|
|--------|------|---------------------|
|
|
|
|
|
|
| `INVALID_MESSAGE` | 消息格式错误或缺少必填字段 | - |
|
|
|
|
|
|
| `SESSION_NOT_FOUND` | 会话不存在 | - |
|
|
|
|
|
|
| `RATE_LIMITED` | 请求频率过高 | 429 |
|
|
|
|
|
|
| `IMAGE_TOO_LARGE` | 图像超过大小限制(5MB)| - |
|
|
|
|
|
|
| `AUDIO_TOO_LARGE` | 音频超过大小限制(10MB)| - |
|
|
|
|
|
|
| `STT_ERROR` | 语音识别服务错误 | - |
|
|
|
|
|
|
| `LLM_ERROR` | LLM 服务错误 | - |
|
|
|
|
|
|
| `LLM_TIMEOUT` | LLM 响应超时(60 秒)| - |
|
|
|
|
|
|
| `TTS_ERROR` | 语音合成服务错误 | - |
|
|
|
|
|
|
| `CONCURRENT_REQUEST` | 同一连接已有进行中的请求 | - |
|
|
|
|
|
|
| `INTERNAL_ERROR` | 服务器内部错误 | 500 |
|
|
|
|
|
|
|
|
|
|
|
|
### REST API 错误码
|
|
|
|
|
|
|
|
|
|
|
|
| 错误码 | 说明 | HTTP 状态码 |
|
|
|
|
|
|
|--------|------|-------------|
|
|
|
|
|
|
| `INVALID_INPUT` | 请求参数验证失败 | 400 |
|
|
|
|
|
|
| `INVALID_TOKEN` | JWT Token 无效或过期 | 401 |
|
|
|
|
|
|
| `INVALID_CREDENTIALS` | 用户名或密码错误 | 401 |
|
|
|
|
|
|
| `UNAUTHORIZED` | 未认证或认证失败 | 401 |
|
|
|
|
|
|
| `FORBIDDEN` | 无权访问资源 | 403 |
|
|
|
|
|
|
| `NOT_FOUND` | 资源不存在 | 404 |
|
|
|
|
|
|
| `USER_EXISTS` | 用户名或邮箱已存在 | 409 |
|
|
|
|
|
|
| `RATE_LIMITED` | 请求频率过高 | 429 |
|
|
|
|
|
|
| `INTERNAL_ERROR` | 服务器内部错误 | 500 |
|
|
|
|
|
|
| `SERVICE_UNAVAILABLE` | 依赖服务不可用 | 503 |
|
2026-06-13 13:24:25 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
## 四、数据模型
|
2026-06-13 13:31:48 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
### 用户(User)
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
```typescript
|
|
|
|
|
|
interface User {
|
|
|
|
|
|
id: string; // UUID
|
|
|
|
|
|
username: string; // 用户名,唯一
|
|
|
|
|
|
email: string; // 邮箱,唯一
|
|
|
|
|
|
created_at: string; // ISO 8601 时间戳
|
|
|
|
|
|
updated_at: string; // ISO 8601 时间戳
|
2026-06-13 13:31:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
### 对话(Conversation)
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
```typescript
|
|
|
|
|
|
interface Conversation {
|
|
|
|
|
|
id: string; // UUID
|
|
|
|
|
|
user_id: string; // 所属用户 ID
|
|
|
|
|
|
title: string; // 对话标题
|
|
|
|
|
|
created_at: string; // ISO 8601 时间戳
|
|
|
|
|
|
updated_at: string; // ISO 8601 时间戳
|
|
|
|
|
|
message_count: number; // 消息数量
|
2026-06-14 18:05:50 +08:00
|
|
|
|
}
|
2026-06-12 17:08:20 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
### 消息(Message)
|
2026-06-12 17:08:20 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2026-06-21 14:48:03 +08:00
|
|
|
|
interface Message {
|
|
|
|
|
|
id: string; // UUID
|
|
|
|
|
|
conversation_id: string; // 所属对话 ID
|
2026-06-12 17:08:20 +08:00
|
|
|
|
role: "user" | "assistant";
|
2026-06-21 14:48:03 +08:00
|
|
|
|
content: string; // 消息文本内容
|
|
|
|
|
|
image_url?: string; // 可选,用户消息的关联图像 URL
|
|
|
|
|
|
created_at: string; // ISO 8601 时间戳
|
2026-06-12 17:08:20 +08:00
|
|
|
|
}
|
2026-06-21 14:48:03 +08:00
|
|
|
|
```
|
2026-06-12 17:08:20 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
### JWT Token 载荷
|
2026-06-14 18:05:50 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**Access Token**(有效期 120 分钟):
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"user_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
|
"username": "alice",
|
|
|
|
|
|
"type": "access",
|
|
|
|
|
|
"exp": 1705318200,
|
|
|
|
|
|
"iat": 1705311000
|
2026-06-14 18:05:50 +08:00
|
|
|
|
}
|
2026-06-12 17:08:20 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
**Refresh Token**(有效期 7 天):
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"user_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
|
"type": "refresh",
|
|
|
|
|
|
"exp": 1705915800,
|
|
|
|
|
|
"iat": 1705311000
|
2026-06-12 17:08:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
2026-06-13 14:05:57 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
---
|
2026-06-13 14:05:57 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
## 附录:版本历史
|
2026-06-13 14:05:57 +08:00
|
|
|
|
|
2026-06-21 14:48:03 +08:00
|
|
|
|
- **v1.0**(2024-01-15):初始版本,定义 WebSocket 协议和 REST API
|
|
|
|
|
|
- **v1.1**(2024-01-20):新增文本输入模式(`query.text` 字段)
|
|
|
|
|
|
- **v1.2**(2024-01-25):新增场景模式配置(`config.scenario` 字段)
|
|
|
|
|
|
- **v2.0**(2026-06-21):重构为纯接口契约规范,移除实现细节
|