前后端交互API文档.md 13 KB

前后端交互API文档

1. WebSocket连接

1.1 连接建立

协议: WSS (WebSocket Secure)
地址: wss://{domain}/ws

1.2 连接参数

interface ConnectionParams {
  userId?: string;           // 用户ID(可选)
  sessionId?: string;        // 会话ID(可选,用于恢复会话)
}

1.3 连接示例

import ReconnectingWebSocket from 'reconnecting-websocket';

const ws = new ReconnectingWebSocket('wss://example.com/ws');

ws.onopen = () => {
  console.log('WebSocket已连接');
};

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  handleMessage(message);
};

ws.onerror = (error) => {
  console.error('WebSocket错误:', error);
};

ws.onclose = () => {
  console.log('WebSocket已断开');
};

2. 消息协议

2.1 消息基础结构

interface BaseMessage {
  type: string;              // 消息类型
  requestId: string;         // 请求唯一标识(UUID)
  timestamp: number;         // 时间戳(毫秒)
}

2.2 消息类型

  • audio_chunk - 音频数据块
  • audio_end - 音频结束
  • cancel - 取消请求
  • text_result - 文本识别结果
  • emotion - 情感标签
  • audio_response - 音频响应
  • error - 错误消息
  • ping / pong - 心跳

3. 客户端→服务端消息

3.1 音频数据块

interface AudioChunkMessage extends BaseMessage {
  type: 'audio_chunk';
  requestId: string;         // 本次录音的唯一ID
  data: string;              // Base64编码的PCM音频数据
  sampleRate: 16000;         // 采样率
  channels: 1;               // 声道数
  bitDepth: 16;              // 采样位数
  chunkIndex: number;        // 分块序号(从0开始)
}

音频规格:

  • 格式: PCM
  • 采样率: 16000 Hz
  • 声道: 单声道
  • 采样位数: 16 bit
  • 分块大小: 3200 字节/帧(建议)

发送示例:

const message = {
  type: 'audio_chunk',
  requestId: 'uuid-xxx',
  data: btoa(String.fromCharCode(...pcmData)),
  sampleRate: 16000,
  channels: 1,
  bitDepth: 16,
  chunkIndex: 0,
  timestamp: Date.now()
};

ws.send(JSON.stringify(message));

3.2 音频结束

interface AudioEndMessage extends BaseMessage {
  type: 'audio_end';
  requestId: string;         // 对应的录音ID
  totalChunks: number;       // 总分块数
}

发送时机: 用户点击【发送】按钮时

3.3 取消请求

interface CancelMessage extends BaseMessage {
  type: 'cancel';
  requestId: string;         // 要取消的录音ID
}

发送时机: 用户点击【取消】按钮时

处理逻辑:

  • 后端收到后立即停止处理该requestId的所有任务
  • 丢弃该requestId的所有响应
  • 前端收到该requestId的响应也直接丢弃

3.4 心跳Ping

interface PingMessage extends BaseMessage {
  type: 'ping';
  requestId: string;
}

发送频率: 每30秒

4. 服务端→客户端消息

4.1 文本识别结果

interface TextResultMessage extends BaseMessage {
  type: 'text_result';
  requestId: string;         // 对应的录音ID
  text: string;              // 识别的文本
  isFinal: boolean;          // 是否为最终结果
}

说明:

  • STT实时识别可能返回多次中间结果
  • isFinal: true 表示最终识别结果

4.2 情感标签

interface EmotionMessage extends BaseMessage {
  type: 'emotion';
  requestId: string;         // 对应的录音ID
  emotion: EmotionType;      // 情感类型
  intensity?: number;        // 情感强度(0-1)
}

type EmotionType = 
  | 'neutral'    // 中性
  | 'happy'      // 开心
  | 'sad'        // 悲伤
  | 'excited'    // 兴奋
  | 'angry'      // 生气
  | 'surprised'  // 惊讶
  | 'thoughtful' // 思考
  | 'encouraging'; // 鼓励

用途: 驱动前端动画IP表情变化

4.3 音频响应

interface AudioResponseMessage extends BaseMessage {
  type: 'audio_response';
  requestId: string;         // 对应的录音ID
  data: string;              // Base64编码的PCM音频数据
  sampleRate: 16000;         // 采样率
  channels: 1;               // 声道数
  bitDepth: 16;              // 采样位数
  chunkIndex: number;        // 分块序号
  isLast: boolean;           // 是否为最后一块
}

说明:

  • TTS流式返回,分多次发送
  • isLast: true 表示最后一块音频

接收示例:

const audioQueue = [];

function handleAudioResponse(message) {
  if (message.type === 'audio_response') {
    const pcmData = atob(message.data);
    const audioBuffer = new Uint8Array(pcmData.length);
    for (let i = 0; i < pcmData.length; i++) {
      audioBuffer[i] = pcmData.charCodeAt(i);
    }
    
    audioQueue.push({
      buffer: audioBuffer,
      isLast: message.isLast
    });
    
    playNextAudio();
  }
}

4.4 错误消息

interface ErrorMessage extends BaseMessage {
  type: 'error';
  requestId: string;         // 对应的请求ID
  code: string;              // 错误代码
  message: string;           // 错误描述
}

错误代码:

  • STT_ERROR - 语音识别失败
  • LLM_ERROR - 文本生成失败
  • TTS_ERROR - 语音合成失败
  • NETWORK_ERROR - 网络错误
  • RATE_LIMIT - API限流
  • INVALID_AUDIO - 音频格式错误
  • CANCELLED - 请求已取消

4.5 心跳Pong

interface PongMessage extends BaseMessage {
  type: 'pong';
  requestId: string;
}

5. 完整交互流程

5.1 正常对话流程

客户端                                服务端
  |                                     |
  |----(1) audio_chunk (chunkIndex=0)-->|
  |----(2) audio_chunk (chunkIndex=1)-->|
  |----(3) audio_chunk (chunkIndex=2)-->| --> STT处理
  |                                     |
  |<---(4) text_result (isFinal=false)--|
  |                                     |
  |----(5) audio_end------------------->| --> LLM处理
  |                                     |
  |<---(6) text_result (isFinal=true)---|
  |<---(7) emotion----------------------|
  |                                     | --> TTS处理
  |<---(8) audio_response (chunk 0)-----|
  |<---(9) audio_response (chunk 1)-----|
  |<---(10) audio_response (chunk 2, isLast)--|
  |                                     |

5.2 取消流程

客户端                                服务端
  |                                     |
  |----(1) audio_chunk (chunkIndex=0)-->|
  |----(2) audio_chunk (chunkIndex=1)-->| --> STT处理中
  |                                     |
  |----(3) cancel---------------------->| --> 停止处理
  |                                     |     丢弃结果
  |                                     |

5.3 错误处理流程

客户端                                服务端
  |                                     |
  |----(1) audio_chunk------------------>|
  |----(2) audio_end-------------------->| --> STT失败
  |                                     |
  |<---(3) error (code: STT_ERROR)------|
  |                                     |

6. 状态管理

6.1 客户端状态

type ClientState = 
  | 'idle'       // 空闲(等待状态)
  | 'recording'  // 录音中(听取状态)
  | 'sending'    // 发送中(等待响应)
  | 'playing'    // 播放中(说话状态)
  | 'error';     // 错误状态

interface ClientStateManager {
  currentState: ClientState;
  currentRequestId: string | null;
  audioQueue: AudioBuffer[];
  
  startRecording(): void;
  stopRecording(): void;
  sendAudio(): void;
  cancelRequest(): void;
  playAudioResponse(): void;
}

6.2 状态转换

idle -> recording: 用户按下录音按钮
recording -> idle: 用户点击取消
recording -> sending: 用户松开按钮/点击发送
sending -> playing: 收到首个audio_response
playing -> idle: 音频播放完成
任意状态 -> error: 发生错误
error -> idle: 错误恢复

7. 性能优化

7.1 延迟优化策略

  • 流式处理: 边收边处理,不等待完整音频
  • STT触发: 识别到句子暂停即触发LLM(不等audio_end)
  • LLM触发: 生成前N个token即触发TTS
  • TTS返回: 合成首个音频片段即返回
  • 音频播放: 收到首个audio_response即开始播放

7.2 音频缓冲策略

class AudioPlayer {
  private minBufferSize = 2;      // 最小缓冲块数
  private audioQueue: AudioBuffer[] = [];
  private isPlaying = false;
  
  addChunk(audioBuffer: AudioBuffer) {
    this.audioQueue.push(audioBuffer);
    
    // 达到最小缓冲即开始播放
    if (!this.isPlaying && this.audioQueue.length >= this.minBufferSize) {
      this.play();
    }
  }
  
  play() {
    // 播放逻辑
  }
}

8. 连接管理

8.1 心跳机制

class WebSocketManager {
  private pingInterval = 30000;  // 30秒
  private pongTimeout = 5000;    // 5秒超时
  
  startHeartbeat() {
    setInterval(() => {
      this.ws.send(JSON.stringify({
        type: 'ping',
        requestId: generateUUID(),
        timestamp: Date.now()
      }));
      
      // 等待pong响应
      setTimeout(() => {
        if (!this.receivedPong) {
          // 未收到pong,重连
          this.reconnect();
        }
      }, this.pongTimeout);
    }, this.pingInterval);
  }
}

8.2 断线重连

const ws = new ReconnectingWebSocket('wss://example.com/ws', [], {
  maxRetries: 3,
  connectionTimeout: 5000,
  maxReconnectionDelay: 10000,
  minReconnectionDelay: 1000,
  reconnectionDelayGrowFactor: 1.5
});

9. 安全性

9.1 连接要求

  • 必须使用 HTTPS/WSS
  • 支持 CORS 配置
  • API密钥通过服务端配置,前端不暴露

9.2 数据验证

# 服务端验证
def validate_audio_chunk(message: dict) -> bool:
    required_fields = ['type', 'requestId', 'data', 'sampleRate', 
                      'channels', 'bitDepth', 'chunkIndex']
    
    if not all(field in message for field in required_fields):
        return False
    
    if message['sampleRate'] != 16000:
        return False
    
    if message['bitDepth'] != 16:
        return False
    
    return True

10. 错误处理与重试

10.1 客户端错误处理

function handleError(error: ErrorMessage) {
  switch (error.code) {
    case 'STT_ERROR':
      showToast('语音识别失败,请重试');
      break;
    case 'LLM_ERROR':
      showToast('生成回复失败,请重试');
      break;
    case 'TTS_ERROR':
      showToast('语音合成失败,请重试');
      break;
    case 'RATE_LIMIT':
      showToast('请求过于频繁,请稍后再试');
      break;
    default:
      showToast('发生错误,请重试');
  }
  
  // 重置状态
  resetToIdle();
}

10.2 重试策略

  • 网络错误: 自动重连(最多3次)
  • API错误: 提示用户,不自动重试
  • 超时: 30秒无响应视为超时,提示用户

11. 监控与调试

11.1 日志格式

interface LogEntry {
  timestamp: number;
  requestId: string;
  type: 'send' | 'receive' | 'error';
  messageType: string;
  data?: any;
  duration?: number;  // 耗时(毫秒)
}

11.2 性能监控

class PerformanceMonitor {
  logLatency(requestId: string, stage: string, duration: number) {
    console.log(`[${requestId}] ${stage}: ${duration}ms`);
  }
  
  trackFullLatency(requestId: string) {
    const start = Date.now();
    
    return {
      markSTT: () => this.logLatency(requestId, 'STT', Date.now() - start),
      markLLM: () => this.logLatency(requestId, 'LLM', Date.now() - start),
      markTTS: () => this.logLatency(requestId, 'TTS', Date.now() - start),
      markTotal: () => this.logLatency(requestId, 'Total', Date.now() - start)
    };
  }
}

12. TypeScript类型定义

12.1 完整类型定义

// 消息类型
type MessageType = 
  | 'audio_chunk'
  | 'audio_end'
  | 'cancel'
  | 'text_result'
  | 'emotion'
  | 'audio_response'
  | 'error'
  | 'ping'
  | 'pong';

// 情感类型
type EmotionType = 
  | 'neutral'
  | 'happy'
  | 'sad'
  | 'excited'
  | 'angry'
  | 'surprised'
  | 'thoughtful'
  | 'encouraging';

// 错误代码
type ErrorCode =
  | 'STT_ERROR'
  | 'LLM_ERROR'
  | 'TTS_ERROR'
  | 'NETWORK_ERROR'
  | 'RATE_LIMIT'
  | 'INVALID_AUDIO'
  | 'CANCELLED';

// 所有消息类型的联合
type Message =
  | AudioChunkMessage
  | AudioEndMessage
  | CancelMessage
  | TextResultMessage
  | EmotionMessage
  | AudioResponseMessage
  | ErrorMessage
  | PingMessage
  | PongMessage;

13. 实现示例

13.1 前端完整示例

参考: 开源组件推荐清单.md - Recorder.js + reconnecting-websocket

13.2 后端完整示例

参考: 开源组件推荐清单.md - FastAPI + DashScope SDK

附录

A. API配置

  • API Key: sk-1c11338d4c3b44bca4ab407c558a67cc
  • Base URL: https://dashscope.aliyuncs.com

B. 模型配置

  • STT: paraformer-realtime-v2
  • LLM: qwen-plus
  • TTS: qwen3-tts-flash

C. 性能目标

  • 端到端延迟: ≤ 300ms(理想)/ ≤ 1000ms(可接受)
  • 音频分块大小: 3200 字节
  • WebSocket心跳间隔: 30秒