## 主要变更 ### 文档重构(减少 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),语义清晰 - 通过交叉引用连接相关文档,避免重复
13 KiB
13 KiB
接口文档
概述
前后端通信接口契约。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,服务端回复pong;60 秒无活动则服务端断开连接 - 重连策略:客户端断线后指数退避重连(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 开始,默认 1page_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 开始,默认 1page_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.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):重构为纯接口契约规范,移除实现细节