HumansXドキュメントセンター 公式サイトへ ↗

WebSocket API リファレンス

API の概要

ASR-LLM-TTS バックエンドは WebSocket プロトコルによるリアルタイム双方向通信インターフェースを提供し、テキスト入力、音声認識、ツール呼び出しなどの機能に対応しています。すべてのメッセージは JSON 形式で、統一されたメッセージプロトコル仕様に従います。

接続情報

項目 値
プロトコル WebSocket
エンドポイント /ws/chat
完全な URL ws://localhost:8000/ws/chat
サブプロトコル なし
認証 認証不要

接続例

// JavaScript客户端连接
const ws = new WebSocket('ws://localhost:8000/ws/chat');

ws.onopen = function(event) {
    console.log('WebSocket连接已建立');
};

ws.onmessage = function(event) {
    const message = JSON.parse(event.data);
    console.log('收到消息:', message);
};

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

ws.onclose = function(event) {
    console.log('WebSocket连接已关闭');
};

メッセージプロトコルの形式

基本的なメッセージ構造

interface WebSocketMessage {
    type: string;                    // 消息类型标识
    payload: {                       // 消息载荷
        [key: string]: any;
    };
    timestamp?: number;              // 可选时间戳
}

メッセージタイプの分類

分類 用途 メッセージ数
制御系 ユーザー操作の制御 3
状態系 システム状態の通知 2
データ系 データ転送 3
ツール系 ツール呼び出し関連 2
セッション系 セッション管理 3

クライアントからの送信メッセージ(C → S)

1. USER_TEXT_INPUT - ユーザーのテキスト入力

ユーザーがテキスト入力によってアシスタントと対話します。

{
    "type": "USER_TEXT_INPUT",
    "payload": {
        "text": "你好,今天天气怎么样?"
    }
}

フィールド説明:

  • text(string、必須):ユーザーが入力したテキスト内容
  • 長さの制限:1~2000 文字
  • 中国語・英語および一般的な記号に対応

使用シーン:

  • ユーザーがチャット画面でテキストを入力する場合
  • 音声認識の結果をテキスト入力として送る場合
  • プログラムからテキストを入力する場合

2. INTERRUPT_TURN - 現在の対話を中断

進行中の対話ラウンド(LLM の生成、TTS の再生など)を中断します。

{
    "type": "INTERRUPT_TURN",
    "payload": {}
}

フィールド説明:

  • 追加フィールドは不要で、payload は空です

実行時の動作:

  • 現在の LLM 生成を停止
  • TTS の音声再生を停止
  • ツール呼び出しの実行を停止
  • TURN_INTERRUPTED 応答を送信

使用シーン:

  • ユーザーが「停止」ボタンをクリックした場合
  • 新しい音声入力が検出された場合
  • 時間のかかる処理を緊急に中断する場合

3. TOGGLE_ASR_LISTENING - 音声認識の切り替え

音声認識機能のオン/オフを制御します。

{
    "type": "TOGGLE_ASR_LISTENING", 
    "payload": {
        "enabled": true
    }
}

フィールド説明:

  • enabled(boolean、必須):ASR の聞き取りを有効にするかどうか
  • true:音声認識をオンにする
  • false:音声認識をオフにする

使用シーン:

  • ユーザーが手動で音声入力を切り替える場合
  • シーンに応じて入力モードを自動で切り替える場合
  • 音声機能のデバッグやテストを行う場合

サーバーからの送信メッセージ(S → C)

1. SYSTEM_STATUS - システム状態の更新

システムまたはコンポーネントの状態変化をクライアントに通知します。

フィールド説明:

{
    "type": "SYSTEM_STATUS",
    "payload": {
        "module": "turn_handler",
        "status": "processing_started",
        "message": "开始处理用户输入",
        "user_input": "你好,今天天气怎么样?"
    }
}
  • module(string):モジュール名

  • "turn_handler":対話マネージャー

  • "llm_client":LLM クライアント
  • "tts_player":TTS プレーヤー
  • "asr_service":音声認識サービス
  • status(string):状態の識別子
  • "processing_started":処理開始
  • "processing_finished":処理完了
  • "error":エラー発生
  • message(string、任意):状態の説明
  • user_input(string、任意):関連するユーザー入力

2. TTS_STATUS - TTS 再生状態

TTS の音声合成および再生の状態変化を通知します。

フィールド説明:

{
    "type": "TTS_STATUS",
    "payload": {
        "status": "speaking",
        "text": "今天的天气很不错",
        "progress": 0.6
    }
}
  • status(string):TTS の状態

  • "synthesizing":合成中

  • "speaking":再生中
  • "completed":再生完了
  • "stopped":再生停止
  • "error":合成または再生のエラー
  • text(string、任意):現在処理中のテキスト
  • progress(number、任意):再生の進捗(0~1)

3. LLM_CHUNK - LLM のストリーミング応答ブロック

LLM が生成した内容のストリーミング断片で、リアルタイム表示に使用します。

フィールド説明:

{
    "type": "LLM_CHUNK",
    "payload": {
        "content_delta": "今天",
        "content_full": "今天",
        "is_complete": false,
        "chunk_index": 1
    }
}
  • content_delta(string):新たに追加された内容の断片

  • content_full(string):累積した全内容

  • is_complete(boolean):応答が完了したかどうか
  • chunk_index(number):断片の番号(1 から開始)

4. LLM_FINAL_RESPONSE - LLM の最終応答

LLM が生成した完全な最終応答の内容です。

フィールド説明:

{
    "type": "LLM_FINAL_RESPONSE",
    "payload": {
        "content": "今天的天气很不错,气温适中,适合外出活动。",
        "total_tokens": 156,
        "completion_time": 2.34
    }
}
  • content(string):完全な応答内容

  • total_tokens(number、任意):合計トークン数

  • completion_time(number、任意):生成時間(秒)

5. ASR_FINAL_RESULT - 音声認識結果

音声認識の最終結果です。

フィールド説明:

{
    "type": "ASR_FINAL_RESULT",
    "payload": {
        "text": "你好,今天天气怎么样?",
        "confidence": 0.92,
        "duration": 3.2
    }
}
  • text(string):認識されたテキスト内容

  • confidence(number、任意):認識の信頼度(0~1)

  • duration(number、任意):音声の長さ(秒)

6. TOOL_CALL_START - ツール呼び出しの開始

ツール呼び出しの実行開始を通知します。

フィールド説明:

{
    "type": "TOOL_CALL_START",
    "payload": {
        "tool": "music_player_play",
        "params": {
            "music_name": "彩虹",
            "sample_rate": 48000
        },
        "round": 1,
        "description": "播放音乐《彩虹》"
    }
}
  • tool(string):ツール名

  • params(object):ツール呼び出しのパラメーター

  • round(number):ツール呼び出しのラウンド
  • description(string、任意):ツール呼び出しの説明

7. TOOL_CALL_RESULT - ツール呼び出しの結果

ツール呼び出しの実行結果です。

フィールド説明:

{
    "type": "TOOL_CALL_RESULT",
    "payload": {
        "tool": "music_player_play",
        "result": "已为您播放《彩虹》,祝您聆听愉快!",
        "status": "success",
        "round": 1,
        "execution_time": 0.85
    }
}
  • tool(string):ツール名

  • result(string):ツールの実行結果

  • status(string):実行状態
  • "success":実行成功
  • "error":実行失敗
  • round(number):対応する呼び出しラウンド
  • execution_time(number、任意):実行時間(秒)

8. TURN_ENDED - 対話ラウンドの終了

1 ラウンドの対話処理が完了したことを通知します。

フィールド説明:

{
    "type": "TURN_ENDED",
    "payload": {
        "status": "completed",
        "duration": 5.67,
        "token_usage": {
            "prompt_tokens": 128,
            "completion_tokens": 89,
            "total_tokens": 217
        }
    }
}
  • status(string):終了状態

  • "completed":正常完了

  • "error":エラーによる終了
  • "cancelled":キャンセル
  • "stopped_or_interrupted":停止または中断
  • duration(number、任意):合計処理時間(秒)
  • token_usage(object、任意):トークン使用量の統計

9. TURN_INTERRUPTED - 対話の中断

対話ラウンドが中断されたことを確認します。

フィールド説明:

{
    "type": "TURN_INTERRUPTED", 
    "payload": {
        "message": "对话已被用户中断",
        "interrupted_at": "llm_generation"
    }
}
  • message(string):中断に関する説明

  • interrupted_at(string、任意):中断が発生したフェーズ

  • "llm_generation":LLM 生成フェーズ
  • "tts_synthesis":TTS 合成フェーズ
  • "tool_execution":ツール実行フェーズ

10. ERROR_MESSAGE - エラーメッセージ

システムエラーまたは異常な状態を通知します。

フィールド説明:

{
    "type": "ERROR_MESSAGE",
    "payload": {
        "source": "llm_client",
        "message": "API调用失败:网络连接超时",
        "error_code": "CONNECTION_TIMEOUT",
        "details": "请检查网络连接后重试"
    }
}
  • source(string):エラーの発生元

  • "server_setup":サーバー設定のエラー

  • "turn_execution":対話実行のエラー
  • "llm_client":LLM クライアントのエラー
  • "tts_client":TTS クライアントのエラー
  • "asr_service":ASR サービスのエラー
  • "tool_execution":ツール実行のエラー
  • message(string):エラーの説明
  • error_code(string、任意):エラーコード
  • details(string、任意):詳細なエラー情報

典型的なインタラクションの流れ

1. 基本的なテキスト対話の流れ

2. ツール呼び出しの流れ

3. 対話中断の流れ

️

クライアントの実装例

JavaScript/TypeScript クライアント

Python クライアント

interface WebSocketMessage {
    type: string;
    payload: any;
}

class ASRLLMTTSClient {
    private ws: WebSocket | null = null;
    private messageHandlers: Map<string, Function> = new Map();

    constructor(private url: string) {}

    connect(): Promise<void> {
        return new Promise((resolve, reject) => {
            this.ws = new WebSocket(this.url);

            this.ws.onopen = () => {
                console.log('Connected to ASR-LLM-TTS server');
                resolve();
            };

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

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

            this.ws.onclose = () => {
                console.log('Connection closed');
            };
        });
    }

    private handleMessage(message: WebSocketMessage) {
        const handler = this.messageHandlers.get(message.type);
        if (handler) {
            handler(message.payload);
        }
    }

    // 注册消息处理器
    on(messageType: string, handler: Function) {
        this.messageHandlers.set(messageType, handler);
    }

    // 发送用户文本输入
    sendUserInput(text: string) {
        this.send('USER_TEXT_INPUT', { text });
    }

    // 中断当前对话
    interrupt() {
        this.send('INTERRUPT_TURN', {});
    }

    // 切换语音识别
    toggleASR(enabled: boolean) {
        this.send('TOGGLE_ASR_LISTENING', { enabled });
    }

    private send(type: string, payload: any) {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify({ type, payload }));
        }
    }

    disconnect() {
        if (this.ws) {
            this.ws.close();
            this.ws = null;
        }
    }
}

// 使用示例
const client = new ASRLLMTTSClient('ws://localhost:8000/ws/chat');

// 注册消息处理器
client.on('LLM_CHUNK', (payload) => {
    console.log('LLM chunk:', payload.content_delta);
    // 更新UI显示
});

client.on('TTS_STATUS', (payload) => {
    console.log('TTS status:', payload.status);
    // 更新播放状态指示器
});

client.on('TOOL_CALL_START', (payload) => {
    console.log('Tool call started:', payload.tool);
    // 显示工具执行状态
});

client.on('ERROR_MESSAGE', (payload) => {
    console.error('Server error:', payload.message);
    // 显示错误信息
});

// 连接并发送消息
client.connect().then(() => {
    client.sendUserInput('你好,请播放一首音乐');
});
import asyncio
import websockets
import json
from typing import Dict, Callable, Any

class ASRLLMTTSClient:
    def __init__(self, uri: str):
        self.uri = uri
        self.websocket = None
        self.message_handlers: Dict[str, Callable] = {}

    async def connect(self):
        """连接到WebSocket服务器"""
        self.websocket = await websockets.connect(self.uri)
        print(f"Connected to {self.uri}")

        # 启动消息监听任务
        asyncio.create_task(self._listen_messages())

    async def _listen_messages(self):
        """监听WebSocket消息"""
        try:
            async for message in self.websocket:
                data = json.loads(message)
                await self._handle_message(data)
        except websockets.exceptions.ConnectionClosed:
            print("Connection closed")

    async def _handle_message(self, message: Dict[str, Any]):
        """处理接收到的消息"""
        message_type = message.get('type')
        payload = message.get('payload', {})

        if message_type in self.message_handlers:
            handler = self.message_handlers[message_type]
            if asyncio.iscoroutinefunction(handler):
                await handler(payload)
            else:
                handler(payload)

    def on(self, message_type: str, handler: Callable):
        """注册消息处理器"""
        self.message_handlers[message_type] = handler

    async def send_user_input(self, text: str):
        """发送用户文本输入"""
        await self._send_message('USER_TEXT_INPUT', {'text': text})

    async def interrupt(self):
        """中断当前对话"""
        await self._send_message('INTERRUPT_TURN', {})

    async def toggle_asr(self, enabled: bool):
        """切换语音识别"""
        await self._send_message('TOGGLE_ASR_LISTENING', {'enabled': enabled})

    async def _send_message(self, msg_type: str, payload: Dict[str, Any]):
        """发送WebSocket消息"""
        if self.websocket:
            message = json.dumps({'type': msg_type, 'payload': payload})
            await self.websocket.send(message)

    async def disconnect(self):
        """断开连接"""
        if self.websocket:
            await self.websocket.close()

# 使用示例
async def main():
    client = ASRLLMTTSClient('ws://localhost:8000/ws/chat')

    # 注册消息处理器
    def on_llm_chunk(payload):
        print(f"LLM: {payload.get('content_delta', '')}", end='', flush=True)

    def on_tts_status(payload):
        print(f"\nTTS Status: {payload.get('status')}")

    def on_tool_call(payload):
        print(f"Tool Call: {payload.get('tool')} - {payload.get('result')}")

    client.on('LLM_CHUNK', on_llm_chunk)
    client.on('TTS_STATUS', on_tts_status)
    client.on('TOOL_CALL_RESULT', on_tool_call)

    # 连接并交互
    await client.connect()
    await client.send_user_input('你好,请播放一首音乐')

    # 保持连接
    await asyncio.sleep(10)
    await client.disconnect()

# 运行客户端
asyncio.run(main())

パフォーマンスに関する考慮事項

1. メッセージの頻度

メッセージタイプごとの目安は次のとおりです。

メッセージタイプ 一般的な頻度 推奨される処理
LLM_CHUNK 10-50/sec デバウンス処理、UI の一括更新
TTS_STATUS 1-5/sec 状態をキャッシュして重複処理を回避
SYSTEM_STATUS 1-10/round ログ記録、表示は任意

2. 接続管理

3. メモリ管理

// 实现自动重连
class RobustWebSocketClient {
    constructor(url, options = {}) {
        this.url = url;
        this.reconnectInterval = options.reconnectInterval || 5000;
        this.maxReconnectAttempts = options.maxReconnectAttempts || 10;
        this.reconnectAttempts = 0;
    }

    connect() {
        this.ws = new WebSocket(this.url);

        this.ws.onopen = () => {
            this.reconnectAttempts = 0;
            console.log('Connected');
        };

        this.ws.onclose = () => {
            this.attemptReconnect();
        };

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

    attemptReconnect() {
        if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++;
            console.log(`Reconnecting... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);

            setTimeout(() => {
                this.connect();
            }, this.reconnectInterval);
        }
    }
}

⚠️

// 限制消息历史记录
class MessageBuffer {
    constructor(maxSize = 1000) {
        this.messages = [];
        this.maxSize = maxSize;
    }

    addMessage(message) {
        this.messages.push(message);

        if (this.messages.length > this.maxSize) {
            this.messages.shift(); // 移除最老的消息
        }
    }

    clear() {
        this.messages = [];
    }
}

エラー処理

よくあるエラーのシナリオ

主なエラーは次のとおりです。

  1. 接続失敗:サーバーに到達できない、またはポートが使用中
  2. メッセージ形式のエラー:JSON の形式が正しくない、または必須フィールドが欠けている
  3. 業務ロジックのエラー:API 呼び出しの失敗、またはツール実行のエラー
  4. ネットワークの切断:予期しない接続の切断

エラー処理のベストプラクティス

// 全面的错误处理
class ErrorHandler {
    static handleWebSocketError(error, context = '') {
        console.error(`WebSocket error in ${context}:`, error);

        // 根据错误类型采取不同处理策略
        if (error.type === 'close') {
            // 连接关闭,尝试重连
            this.handleConnectionClosed();
        } else if (error.type === 'error') {
            // 一般错误,记录并通知用户
            this.notifyUser('连接出现问题,正在尝试恢复...');
        }
    }

    static handleMessageError(message, error) {
        console.error('Message handling error:', error);

        // 检查是否为关键消息
        const criticalTypes = ['ERROR_MESSAGE', 'TURN_ENDED'];
        if (criticalTypes.includes(message.type)) {
            // 关键消息处理失败,需要特殊处理
            this.handleCriticalMessageError(message);
        }
    }

    static notifyUser(message) {
        // 实现用户通知逻辑
        console.log('User notification:', message);
    }
}

この WebSocket API リファレンスは、フロントエンド開発者向けに完全なインターフェース仕様と実装ガイドを提供し、クライアントが ASR-LLM-TTS バックエンドシステムと正しく通信できるようにするものです。