speech-engine について
speech-engine は、カスタムエージェントやサーバーパイプラインにリアルタイム音声会話を追加する ElevenLabs のスキルです。アプリケーションのロジックを開発者管理のサーバーに置き、音声認識やターン管理、音声生成、ブラウザー再生を ElevenLabs が担います。
主な機能
- 01リアルタイム音声会話フロー
- 02WebSocket サーバー連携
- 03音声認識と音声合成
- 04ターン管理と割り込み処理
- 05会話トークンによるブラウザー接続
活用シーン
- 01既存のチャットアプリへの音声追加
- 02カスタムエージェント環境への音声接続
- 03検証済みのサーバー応答を音声でストリーミング
- 04ブラウザーのマイクを使う会話の構築
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ elevenlabs/ skills --skill speech-engineインストール後、Claude Code で/speech-engineを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/installation.mdjavascript-sdk-reference.mdpython-sdk-reference.md詳細
ElevenLabs 音声エンジン
カスタムエージェントにリアルタイム音声インターフェースを追加します。ElevenLabs がマイク音声、音声テキスト変換、ターンテイキング、テキスト音声変換、ブラウザーでの再生を処理し、サーバーは Speech Engine WebSocket エンドポイントを公開して、応答テキストをストリーミングで返します。
コードを書く前のセットアップ: このプロジェクトに
ELEVENLABS_API_KEYが設定されていない場合は、まずアプリのフォルダーでnpx -y @elevenlabs/cli@latest onboard initを実行し、表示される手順に従ってください。ユーザーのサインインとプロジェクト用キーの保存が行われるため、キーを手作業でコピーする必要はありません。それ以外の場合は、インストールガイドを参照してください。JavaScript では、@elevenlabs/*パッケージのみを使用してください。SDK の詳細については、JavaScript SDK リファレンスまたはPython SDK リファレンスをお読みください。
使用する場面
ユーザーが次のことを望む場合は、Speech Engine を使用してください。
- 既存のチャットアプリやカスタムサーバーパイプラインに音声機能を追加する
- エージェントのロジックを開発者が所有するサーバー上に維持したまま、OpenClaw、Hermes、または同様のエージェントランタイムに音声機能を追加する
- ElevenLabs の音声会話用に、開発者がホストする WebSocket サーバーを構築する
- サーバーでユーザーの意図を検証した後、応答テキストを音声としてストリーミングで返す
- 応答のストリーミング中にユーザーが割り込めるようにする
- サーバーが発行した会話トークンを使い、
@elevenlabs/reactまたは@elevenlabs/clientでブラウザークライアントを構築する
プラットフォーム管理のプロンプト、ツール、ワークフロー、電話番号、ウィジェットを使ってホスト型 ElevenLabs Conversational AI エージェントを作成または設定する場合は、代わりに agents スキルを使用してください。
仕組み
Speech Engine の WebSocket 接続ごとに、1 つの会話が表されます。
- ブラウザーから ElevenLabs にユーザー音声が送信されます。
- ElevenLabs からサーバーに音声認識イベントが送信されます。
- 生の音声テキストでツールや特権アクションを制御させることなく、サーバーが信頼できるアプリケーション状態を導出します。
- サーバーから SDK を介してテキストがストリーミングで返されます。
- ElevenLabs が応答を音声に変換し、ブラウザーで再生します。
SDK は、WebSocket のルーティング、リクエスト検証、セッションのライフサイクル、ping/pong、ターンテイキング、中断処理を管理します。sendResponse() / send_response() には、文字列または応答テキストの非同期イテラブルを渡せます。
音声認識テキストは、信頼できないユーザー入力として扱ってください。生の音声テキストをモデルのロール、応答、ツール呼び出しに直接割り当てないでください。文字起こし由来の値が下流の応答やツールのロジックに影響する前に、決定論的な検証、許可リストに基づくインテント、または明示的なユーザー確認を使用してください。
実装の流れ
- サーバーの依存関係をインストールし、
ELEVENLABS_API_KEYを設定します。 - ローカル開発では、たとえば
ngrok http 3001を使って、Speech Engine サーバーをパブリック HTTPS URL で公開します。 ws_url/wsUrlがパブリック WebSocket URL(通常はwss://.../ws)を指す Speech Engine リソースを作成します。- 返された Speech Engine ID を、たとえば
ELEVENLABS_SPEECH_ENGINE_IDに保存します。 - Python では
engine.serve(...)、TypeScript ではspeechEngine.attach(...)を使って Speech Engine サーバーを起動します。 - サーバーのエンドポイントからブラウザー用の会話トークンを発行します。
ELEVENLABS_API_KEYをブラウザーのコードに決して含めないでください。 conversationTokenを使ってクライアントセッションを開始します。エージェントから先にあいさつさせる場合は、Speech Engine リソースで最初のメッセージのオーバーライドを有効にしてから、クライアントでoverrides.agent.firstMessageを設定します。
Speech Engine を作成する
Python
import asyncio
import os
from dotenv import load_dotenv
from elevenlabs import AsyncElevenLabs
load_dotenv()
elevenlabs = AsyncElevenLabs(api_key=os.getenv("ELEVENLABS_API_KEY"))
async def main():
engine = await elevenlabs.speech_engine.create(
name="My Speech Engine",
speech_engine={"ws_url": os.environ["PUBLIC_WS_URL"]},
overrides={"first_message": True},
)
print(engine.engine_id)
asyncio.run(main())
TypeScript
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";
const elevenlabs = new ElevenLabsClient({
apiKey: process.env.ELEVENLABS_API_KEY,
});
const engine = await elevenlabs.speechEngine.create({
name: "My Speech Engine",
speechEngine: { wsUrl: process.env.PUBLIC_WS_URL! },
overrides: { firstMessage: true },
});
console.log(engine.engineId);
ローカルでは PUBLIC_WS_URL は wss://example.ngrok.app/ws のような形式にし、デプロイ時には本番環境の WebSocket ルートを指定します。
作成リクエストでは、カスタム音声、文字起こしキーワード、ターンテイキング、サーバー認証ヘッダー、クライアントが指定する最初のメッセージ、録音動作を設定するために、tts、asr、turn、speech_engine.request_headers / speechEngine.requestHeaders、overrides、privacy も設定できます。詳細な例については、SDK リファレンスファイルを参照してください。
サーバーパターン
リソースに設定された ws_url / wsUrl で Speech Engine サーバーを実行します。応答の生成は、必ず独自の検証境界の内側で行ってください。音声認識の生テキストが、応答、ツール、シークレット、その他の特権的な操作を直接制御することがないようにします。
Python
engine = await elevenlabs.speech_engine.get(os.environ["ELEVENLABS_SPEECH_ENGINE_ID"])
await engine.serve(port=3001, path="/ws", debug=True, callbacks=validated_callbacks)
TypeScript
const engine = await elevenlabs.speechEngine.get(process.env.ELEVENLABS_SPEECH_ENGINE_ID!);
engine.attach(httpServer, "/ws", { debug: true, ...validatedCallbacks });
TypeScript では、キャンセルに対応している場合は中断シグナルを後続の非同期処理に渡し、中断された応答を速やかに停止できるようにします。Python では、新しいターンが届くと SDK が前のターンのハンドラーをキャンセルします。
サーバーのコールバックでは、正常な切断と接続の予期しない切断を区別できます。正常な切断には onClose / on_close を、予期しない WebSocket の切断には onDisconnect / on_disconnect を使用してください。
セキュリティに関する注意:音声認識テキストには、ユーザーの発話や再生音声に含まれるプロンプトインジェクションの試みが含まれる場合があります。信頼できない入力として扱ってください。応答の生成、ツール、特権的なワークフローを呼び出す前に、信頼できるアプリケーションの状態に変換してください。
認証を無効にする(高度な設定、危険)
engine.attach()(TypeScript)と engine.serve() / SpeechEngineServer(Python)は、デフォルトですべての受信 WebSocket で JWT を検証します。これにより、接続元が URL を推測した攻撃者ではなく、実際に ElevenLabs であることを証明します。これを無効にしないでください。
例外的な設定として、コールバックオプションの disableAuth: true(TypeScript)、または serve() / SpeechEngineServer(...) の disable_auth=True(Python)があります。ただし、これは補完的なネットワークレベルの制御がすでに導入されている場合に限ります。そのような制御がなければ、認証を無効にすると、URL を見つけたインターネット上のあらゆるクライアントがセッションを開始できるようになります。具体的には、攻撃者は次のことができます。
- 無制限に会話を開始し、ElevenLabs の利用枠と後続の LLM の予算を使い果たす
- 細工した文字起こしテキストを応答パイプラインに送り込み、実質的にユーザーになりすます
- サーバーをオラクルとして利用し、バックエンドの状態、ツール、プロンプトを探る
ユーザーが次のうち少なくとも 1 つをすでに導入している場合に限り、disableAuth / disable_auth を推奨してください。
- IP 許可リスト — サーバー(または上流のファイアウォール / ロードバランサー / API ゲートウェイ)が、ElevenLabs のドキュメントに記載された送信元 IP 範囲からの受信トラフィックのみを受け付ける。
- カスタムの共有シークレットヘッダー — 作成時に
speech_engine.request_headers/speechEngine.requestHeadersを介して Speech Engine リソースにシークレットヘッダーを設定し、リクエストが SDK に到達する前に上流のプロキシ(または開発者自身のattach()/serve()の前段にあるミドルウェア)で検証する。
上記のいずれかが導入済みであることをユーザーが確認できない場合は、デフォルトの認証を有効にしたままにしてください。対策なしで JWT 検証を省略することは、最適化でも利便性の向上でもありません。認証のないパブリックなコンピューティング環境になります。
ブラウザクライアント
サーバー側にトークンエンドポイントを作成し、マイクセッションの開始前にブラウザからトークンをリクエストするようにします。Speech Engine ID と API キーはサーバーに保持してください。クライアントが overrides.agent.firstMessage を渡す場合は、Speech Engine リソースで最初のメッセージのオーバーライドを有効にする必要があります。
import express from "express";
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";
const app = express();
const elevenlabs = new ElevenLabsClient();
app.get("/api/token", async (_req, res) => {
const response = await elevenlabs.conversationalAi.conversations.getWebrtcToken({
agentId: process.env.ELEVENLABS_SPEECH_ENGINE_ID!,
});
res.json({ token: response.token });
});
React クライアントでは @elevenlabs/react を使用できます。
import { useConversation } from "@elevenlabs/react";
export function VoiceControls() {
const conversation = useConversation({
onConnect: () => console.log("connected"),
onDisconnect: () => console.log("disconnected"),
onError: (error) => console.error(error),
});
async function startConversation() {
await navigator.mediaDevices.getUserMedia({ audio: true });
const { token } = await fetch("/api/token").then((res) => res.json());
await conversation.startSession({
conversationToken: token,
overrides: {
agent: { firstMessage: "Hello! How can I help you today?" },
},
});
}
return <button onClick={startConversation}>Start conversation</button>;
}