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

13 KiB
Raw Blame History

接口文档

概述

前后端通信接口契约。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 文本帧,统一结构:

interface WsMessage {
  type: string;        // 消息类型,必填
  request_id?: string; // 可选,用于请求-响应关联
  timestamp?: number;  // 可选,毫秒时间戳
  [key: string]: any;  // 类型特定字段
}

客户端 → 服务端消息

query — 发起一次视觉对话

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 — 更新会话配置

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 — 打断当前回复

interface InterruptMessage {
  type: "interrupt";
  request_id?: string; // 可选,当前实现不使用此字段,服务端始终取消当前活跃请求
}

ping — 心跳保活

interface PingMessage {
  type: "ping";
}

服务端 → 客户端消息

connected — 连接建立确认

interface ConnectedMessage {
  type: "connected";
  session_id: string;
  conversation_id: string;
  config: {
    tts_enabled: boolean;
    detail_level: "low" | "high";
    language: string;
    scenario: string;
  };
}

stt_result — 语音识别结果

interface SttResultMessage {
  type: "stt_result";
  request_id: string;
  text: string;          // 识别出的文本
  is_final: boolean;     // 当前实现始终为 true
}

llm_chunk — LLM 流式响应片段

interface LlmChunkMessage {
  type: "llm_chunk";
  request_id: string;
  content: string;       // 当前 token 片段
}

llm_done — LLM 响应完成

interface LlmDoneMessage {
  type: "llm_done";
  request_id: string;
  full_text: string;     // 完整响应文本
}

tts_audio — TTS 音频片段

音频流式推送,每个消息携带一个句子的音频数据。

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 — 错误通知

interface ErrorMessage {
  type: "error";
  request_id?: string;   // 关联的请求 ID全局错误时为空
  code: string;          // 错误码,见下文错误码表
  message: string;       // 人类可读的错误描述
  details?: any;         // 可选的详细错误信息
}

pong — 心跳响应

interface PongMessage {
  type: "pong";
}

连接管理

  • 心跳机制:客户端每 30 秒发送 ping,服务端回复 pong60 秒无活动则服务端断开连接
  • 重连策略客户端断线后指数退避重连1s → 2s → 4s → ... → 最大 30s
  • 并发控制:同一连接同时只能有一个活跃的 query 请求;新请求到来时自动取消旧请求

二、REST API

所有 REST 端点均使用 JSON 格式。

2.1 健康检查

GET /api/health

检查服务健康状态。

响应

{
  "status": "healthy",
  "timestamp": "2024-01-15T10:30:00Z",
  "dependencies": {
    "database": "healthy",
    "redis": "healthy"
  }
}

2.2 认证 API

POST /api/auth/register — 用户注册

请求

{
  "username": "alice",
  "email": "alice@example.com",
  "password": "SecurePass123!"
}

响应200 OK

{
  "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 — 用户登录

请求

{
  "username": "alice",
  "password": "SecurePass123!"
}

响应200 OK

{
  "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

{
  "access_token": "eyJhbGc...",
  "refresh_token": "eyJhbGc...",
  "expires_in": 7200
}

错误

  • 401 INVALID_TOKEN: Refresh Token 无效或过期

POST /api/auth/logout — 用户登出

请求头

Authorization: Bearer <access_token>

响应200 OK

{
  "message": "Logged out successfully"
}

2.3 对话管理 API

所有端点均需 JWT 认证(Authorization: Bearer <access_token>)。

GET /api/conversations — 获取对话列表

查询参数

  • page: 页码,从 1 开始,默认 1
  • page_size: 每页条数,默认 20最大 100

响应200 OK

{
  "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 — 创建新对话

请求

{
  "title": "新的对话"
}

响应201 Created

{
  "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

{
  "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 — 更新对话

请求

{
  "title": "修改后的标题"
}

响应200 OK

{
  "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

{
  "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: 消息文本内容

三、错误码表

所有错误均使用以下格式:

{
  "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

interface User {
  id: string;              // UUID
  username: string;        // 用户名,唯一
  email: string;           // 邮箱,唯一
  created_at: string;      // ISO 8601 时间戳
  updated_at: string;      // ISO 8601 时间戳
}

对话Conversation

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

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 分钟):

{
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "username": "alice",
  "type": "access",
  "exp": 1705318200,
  "iat": 1705311000
}

Refresh Token(有效期 7 天):

{
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "refresh",
  "exp": 1705915800,
  "iat": 1705311000
}

附录:版本历史

  • v1.02024-01-15初始版本定义 WebSocket 协议和 REST API
  • v1.12024-01-20新增文本输入模式query.text 字段)
  • v1.22024-01-25新增场景模式配置config.scenario 字段)
  • v2.02026-06-21重构为纯接口契约规范移除实现细节