Files
CamTalk/docs/02-接口文档.md
hhs 032de796c8 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),语义清晰
- 通过交叉引用连接相关文档,避免重复
2026-06-21 14:48:03 +08:00

558 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 接口文档
## 概述
前后端通信接口契约。WebSocket 承载实时对话REST API 支撑基础运维。
**设计原则**
- WebSocket 为主:所有对话数据走 WebSocket
- REST 为辅:仅用于健康检查、认证、对话管理等低频操作
- 接口先行:先定义契约,再填充实现——前后端可并行开发
## 接口全景
```
浏览器 Go Gateway :8080
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
```
---
## 一、WebSocket 协议
连接地址:`ws://localhost:8080/ws?token=<access_token>&conversation_id=<uuid>`
| 参数 | 必填 | 说明 |
|------|------|------|
| `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"
}
```
#### `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;
conversation_id: string;
config: {
tts_enabled: boolean;
detail_level: "low" | "high";
language: string;
scenario: string;
};
}
```
#### `stt_result` — 语音识别结果
```typescript
interface SttResultMessage {
type: "stt_result";
request_id: string;
text: string; // 识别出的文本
is_final: boolean; // 当前实现始终为 true
}
```
#### `llm_chunk` — LLM 流式响应片段
```typescript
interface LlmChunkMessage {
type: "llm_chunk";
request_id: string;
content: string; // 当前 token 片段
}
```
#### `llm_done` — LLM 响应完成
```typescript
interface LlmDoneMessage {
type: "llm_done";
request_id: string;
full_text: string; // 完整响应文本
}
```
#### `tts_audio` — TTS 音频片段
音频流式推送,每个消息携带一个句子的音频数据。
```typescript
interface TtsAudioMessage {
type: "tts_audio";
request_id: string;
audio: string; // Base64 编码的音频数据
format: string; // 音频格式
sample_rate: number; // 采样率Hz
sequence: number; // 句子序号,从 0 开始递增
is_final: boolean; // 是否为最后一个句子
}
```
**音频格式约束**
| 字段 | 值 | 说明 |
|------|-----|------|
| `format` | `"pcm"` | 线性 PCM小端序 |
| `sample_rate` | `24000` | 24kHz 采样率 |
| 位深度 | 16-bit | 单声道 |
| 句子划分 | 按标点符号(。!?;:)分割 | 服务端按句分割 LLM 响应,并行合成 |
#### `error` — 错误通知
```typescript
interface ErrorMessage {
type: "error";
request_id?: string; // 关联的请求 ID全局错误时为空
code: string; // 错误码,见下文错误码表
message: string; // 人类可读的错误描述
details?: any; // 可选的详细错误信息
}
```
#### `pong` — 心跳响应
```typescript
interface PongMessage {
type: "pong";
}
```
### 连接管理
- **心跳机制**:客户端每 30 秒发送 `ping`,服务端回复 `pong`60 秒无活动则服务端断开连接
- **重连策略**客户端断线后指数退避重连1s → 2s → 4s → ... → 最大 30s
- **并发控制**:同一连接同时只能有一个活跃的 `query` 请求;新请求到来时自动取消旧请求
---
## 二、REST API
所有 REST 端点均使用 JSON 格式。
### 2.1 健康检查
#### `GET /api/health`
检查服务健康状态。
**响应**
```json
{
"status": "healthy",
"timestamp": "2024-01-15T10:30:00Z",
"dependencies": {
"database": "healthy",
"redis": "healthy"
}
}
```
### 2.2 认证 API
#### `POST /api/auth/register` — 用户注册
**请求**
```json
{
"username": "alice",
"email": "alice@example.com",
"password": "SecurePass123!"
}
```
**响应**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
}
```
**错误**
- `400 INVALID_INPUT`: 参数验证失败
- `409 USER_EXISTS`: 用户名或邮箱已存在
#### `POST /api/auth/login` — 用户登录
**请求**
```json
{
"username": "alice",
"password": "SecurePass123!"
}
```
**响应**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
}
```
**错误**
- `400 INVALID_INPUT`: 参数缺失
- `401 INVALID_CREDENTIALS`: 用户名或密码错误
#### `POST /api/auth/refresh` — 刷新 Access Token
**请求头**
```
Authorization: Bearer <refresh_token>
```
**响应**200 OK
```json
{
"access_token": "eyJhbGc...",
"refresh_token": "eyJhbGc...",
"expires_in": 7200
}
```
**错误**
- `401 INVALID_TOKEN`: Refresh Token 无效或过期
#### `POST /api/auth/logout` — 用户登出
**请求头**
```
Authorization: Bearer <access_token>
```
**响应**200 OK
```json
{
"message": "Logged out successfully"
}
```
### 2.3 对话管理 API
所有端点均需 JWT 认证(`Authorization: Bearer <access_token>`)。
#### `GET /api/conversations` — 获取对话列表
**查询参数**
- `page`: 页码,从 1 开始,默认 1
- `page_size`: 每页条数,默认 20最大 100
**响应**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
}
```
#### `POST /api/conversations` — 创建新对话
**请求**
```json
{
"title": "新的对话"
}
```
**响应**201 Created
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "新的对话",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z",
"message_count": 0
}
```
#### `GET /api/conversations/:id` — 获取对话详情
**响应**200 OK
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "关于植物的对话",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T11:45:00Z",
"message_count": 12
}
```
**错误**
- `404 NOT_FOUND`: 对话不存在或无权访问
#### `PATCH /api/conversations/:id` — 更新对话
**请求**
```json
{
"title": "修改后的标题"
}
```
**响应**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
}
```
#### `DELETE /api/conversations/:id` — 删除对话
**响应**204 No Content无响应体
**错误**
- `404 NOT_FOUND`: 对话不存在或无权访问
#### `GET /api/conversations/:id/messages` — 获取对话消息
**查询参数**
- `page`: 页码,从 1 开始,默认 1
- `page_size`: 每页条数,默认 50最大 100
**响应**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
}
```
**消息字段说明**
- `role`: `"user"``"assistant"`
- `image_url`: 仅 `user` 消息可能包含,指向存储的图像
- `content`: 消息文本内容
---
## 三、错误码表
所有错误均使用以下格式:
```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 |
---
## 四、数据模型
### 用户User
```typescript
interface User {
id: string; // UUID
username: string; // 用户名,唯一
email: string; // 邮箱,唯一
created_at: string; // ISO 8601 时间戳
updated_at: string; // ISO 8601 时间戳
}
```
### 对话Conversation
```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; // 消息数量
}
```
### 消息Message
```typescript
interface Message {
id: string; // UUID
conversation_id: string; // 所属对话 ID
role: "user" | "assistant";
content: string; // 消息文本内容
image_url?: string; // 可选,用户消息的关联图像 URL
created_at: string; // ISO 8601 时间戳
}
```
### JWT Token 载荷
**Access Token**(有效期 120 分钟):
```json
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "alice",
"type": "access",
"exp": 1705318200,
"iat": 1705311000
}
```
**Refresh Token**(有效期 7 天):
```json
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"type": "refresh",
"exp": 1705915800,
"iat": 1705311000
}
```
---
## 附录:版本历史
- **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重构为纯接口契约规范移除实现细节