> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Speech Engineクイックスタート

このガイドでは、Speech Engineを使って音声対応エージェントを構築します。LLMをElevenLabsに接続するサーバーをセットアップし、ユーザーがエージェントと音声で会話できるようにブラウザクライアントを連携させます。

> **Tip**
>
> [ElevenLabs Speech Engineスキル](https://github.com/elevenlabs/skills/tree/main/speech-engine)を使って、チャットエージェントに音声を追加できます。
>
> ```bash
> npx skills add elevenlabs/skills --skill speech-engine
> ```

## Speech Engineの仕組み

Speech EngineはLLMをElevenLabsに接続し、ユーザーがエージェントに話しかけ、その応答を聞けるようにします。ElevenLabsがスピーチtoテキストとテキスト読み上げを処理し、サーバーがLLMロジックを提供します。

```mermaid
sequenceDiagram
    participant Browser
    participant ElevenLabs

    box Your Server
        participant SDK as Speech Engine SDK
        participant LLM
    end

    Browser->>ElevenLabs: User speaks (audio)
    ElevenLabs->>SDK: Transcript (WebSocket)
    SDK->>LLM: Conversation history
    LLM->>SDK: Streamed response
    SDK->>ElevenLabs: Text chunks
    ElevenLabs->>Browser: Agent speaks (audio)
```

各WebSocket接続は1つの会話を表します。ユーザーが話すと、ElevenLabsがオーディオを文字起こしし、文字起こし結果をサーバーに送信します。サーバーはそれをLLMに渡し、応答をストリーミングで返します。ElevenLabsはテキストを音声に変換し、ブラウザで再生します。SDKが接続管理、ターンテイキング、割り込み検出を処理します。

## 前提条件

このチュートリアルでは、LLMにOpenAIのAPIを使用します。`OPENAI_API_KEY`環境変数にOpenAI APIキーを設定する必要があります。

## サーバーのセットアップ

#### APIキーを作成

[ダッシュボードでAPIキーを作成](https://elevenlabs.io/app/settings/api-keys)し、安全に[APIへアクセス](/docs/ja/api-reference/authentication)するために使用します。

キーは管理されたシークレットとして保存し、好みに応じて`.env`ファイルによる環境変数として、またはアプリの設定で直接SDKに渡してください。

**`.env`**

```js title=".env"
ELEVENLABS_API_KEY=<your_api_key_here>
```

#### 依存関係をインストール

```python
pip install elevenlabs openai python-dotenv
```

```typescript
npm install @elevenlabs/elevenlabs-js openai
```

#### サーバーを公開

Speech Engineには、パブリックにアクセス可能なURLが必要です。[ngrok](https://ngrok.com)を使用してローカルサーバーを公開します。サーバーはまだ作成していませんが、次の手順でURLが必要になるため、先にngrokを起動しておく必要があります。

```bash
ngrok http 3001
```

転送URL（例：`https://abc123.ngrok.io`）をコピーします。

#### Speech Engineインスタンスを作成

SDKを使ってSpeech Engineインスタンスを作成します。WebSocket URLには、ngrok URLの末尾に`/ws`パスを付けて渡します。

**`create_engine.py`**

```python title="create_engine.py"
import asyncio
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={
            # Note we use the wss protocol instead of https
            "ws_url": "wss://abc123.ngrok.io/ws",
        },
    )

    print(f"Speech Engine ID: {engine.engine_id}")


if __name__ == "__main__":
    asyncio.run(main())
```

**`create-engine.mts`**

```typescript title="create-engine.mts"
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: {
    // Note we use the wss protocol instead of https
    wsUrl: "wss://abc123.ngrok.io/ws",
  },
});

console.log("Speech Engine ID:", engine.engineId);
```

このスクリプトを実行し、次の手順で使用するSpeech Engine ID（例：`seng_8k3m9xr4hjnfg983brhmhkd98n6`）をコピーします。

#### サーバーを作成

`server.py`または`server.mts`というファイルを作成し、以下の内容を追加します。これによりサーバーをセットアップし、`/ws`パスにSpeech Engineを接続して、OpenAIで応答を生成します。

**`server.py`**

```python maxLines=0 title="server.py"
import asyncio
import os

from dotenv import load_dotenv
from openai import AsyncOpenAI
from elevenlabs import AsyncElevenLabs

load_dotenv()

# Replace with your Speech Engine ID from step 4
SPEECH_ENGINE_ID = "seng_8k3m9xr4hjnfg983brhmhkd98n6"

openai = AsyncOpenAI(
  api_key=os.getenv("OPENAI_API_KEY"),
)
elevenlabs = AsyncElevenLabs(
  api_key=os.getenv("ELEVENLABS_API_KEY"),
)


def on_init(conversation_id, session):
    print(f"Session started: {conversation_id}")


async def on_transcript(transcript, session):
    stream = await openai.responses.create(
        model="gpt-4o",
        instructions="You are a helpful voice assistant. Keep responses concise and conversational.",
        input=[
            {"role": "assistant" if m.role == "agent" else m.role, "content": m.content}
            for m in transcript
        ],
        stream=True,
    )

    await session.send_response(stream)


def on_close(session):
    print(f"Session ended: {session.conversation_id}")


def on_error(err, session):
    print(f"Error: {err}")


async def main():
    engine = await elevenlabs.speech_engine.get(SPEECH_ENGINE_ID)

    await engine.serve(
        port=3001,
        path="/ws",
        debug=True,
        on_init=on_init,
        on_transcript=on_transcript,
        on_close=on_close,
        on_error=on_error,
    )


if __name__ == "__main__":
    asyncio.run(main())
```

**`server.mts`**

```typescript maxLines=0 title="server.mts"
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import { createServer } from "node:http";
import OpenAI from "openai";
import "dotenv/config";

// Replace with your Speech Engine ID from step 4
const SPEECH_ENGINE_ID = "seng_8k3m9xr4hjnfg983brhmhkd98n6";

const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});
const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

const httpServer = createServer();

await elevenlabs.speechEngine.attach(SPEECH_ENGINE_ID, httpServer, "/ws", {
  debug: true,

  onInit(conversationId) {
    console.log("Session started:", conversationId);
  },

  async onTranscript(transcript, signal, session) {
    const response = await openai.responses.create(
      {
        model: "gpt-4o",
        instructions:
          "You are a helpful voice assistant. Keep responses concise and conversational.",
        input: transcript.map((m) => ({
          role: m.role === "agent" ? "assistant" : m.role,
          content: m.content,
        })),
        stream: true,
      },
      { signal },
    );

    session.sendResponse(response);
  },

  onClose(session) {
    console.log("Session ended:", session.conversationId);
  },

  onError(err) {
    console.error("Error:", err);
  },
});

httpServer.listen(3001, () => {
  console.log("Speech Engine server listening on port 3001");
});
```

`onTranscript` / `on_transcript`コールバックは、会話履歴全体と現在のセッションを受け取ります。TypeScript SDKは、ユーザーが応答の途中で割り込んだ場合に発火する`AbortSignal`も提供します。OpenAI呼び出しに`signal`を渡すと、割り込み時にLLMリクエストが自動的にキャンセルされます。

`sendResponse()` / `send_response()`は、文字列、非同期イテラブル、またはOpenAI、Anthropic、Google Geminiからのストリームを受け取ります。SDKがテキストコンテンツを自動的に抽出します。

> **Warning**
>
> 上記の例では、ユーザーからの文字起こし全体がLLMに渡されます。本番環境では、プロンプトインジェクションや操作の試みを防ぐためのガードレールを追加してください。

#### サーバーを起動

```python
python server.py
```

```typescript
npx tsx server.mts
```

## クライアントのセットアップ

#### クライアントSDKをインストール

#### React

```bash
npm install @elevenlabs/react
```

#### JavaScript

```bash
npm install @elevenlabs/client
```

#### トークンエンドポイントを作成

会話トークンを生成するサーバー側エンドポイントを追加します。これによりAPIキーをブラウザに公開せず、最高のオーディオ品質を実現するWebRTCを使用できます。

**`token_server.py`**

```python title="token_server.py"
import os

from dotenv import load_dotenv
from flask import Flask, jsonify
from elevenlabs import ElevenLabs

load_dotenv()

app = Flask(__name__)
elevenlabs = ElevenLabs(
    api_key=os.getenv("ELEVENLABS_API_KEY"),
)


@app.route("/api/token")
def get_token():
    # Replace with your Speech Engine ID from step 4 of the server setup
    speech_engine_id = "seng_8k3m9xr4hjnfg983brhmhkd98n6"

    response = elevenlabs.conversational_ai.conversations.get_webrtc_token(
        agent_id=speech_engine_id,
    )

    return jsonify(token=response.token)


if __name__ == "__main__":
    app.run(port=3002)
```

**`token-server.mts`**

```typescript title="token-server.mts"
import express from "express";
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";

const app = express();
const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});

app.get("/api/token", async (req, res) => {
  // Replace with your Speech Engine ID from step 4 of the server setup
  const speechEngineId = "seng_8k3m9xr4hjnfg983brhmhkd98n6";

  const response = await elevenlabs.conversationalAi.conversations.getWebrtcToken({
    agentId: speechEngineId,
  });

  res.json({ token: response.token });
});

app.listen(3002, () => {
  console.log("Token server listening on port 3002");
});
```

#### 会話UIを構築

サーバーから会話トークンを取得し、それを使用してセッションを開始します。

#### React

**`App.tsx`**

```tsx title="App.tsx"
import { useConversation } from "@elevenlabs/react";
import { useCallback } from "react";

async function getToken(): Promise<string> {
  const response = await fetch("/api/token");
  if (!response.ok) {
    throw Error("Failed to get conversation token");
  }
  const data = await response.json();
  return data.token;
}

export default function App() {
  const conversation = useConversation({
    onConnect: () => console.log("Connected"),
    onDisconnect: () => console.log("Disconnected"),
    onError: (error: Error) => console.error("Error:", error),
  });

  const startConversation = useCallback(async () => {
    await navigator.mediaDevices.getUserMedia({ audio: true });
    const token = await getToken();
    await conversation.startSession({ conversationToken: token });
  }, [conversation]);

  const stopConversation = useCallback(async () => {
    await conversation.endSession();
  }, [conversation]);

  return (
    <div>
      <p>Status: {conversation.status}</p>
      <button onClick={startConversation} disabled={conversation.status === "connected"}>
        Start conversation
      </button>
      <button onClick={stopConversation} disabled={conversation.status !== "connected"}>
        End conversation
      </button>
    </div>
  );
}
```

#### JavaScript

**`main.ts`**

```typescript title="main.ts"
import { Conversation } from "@elevenlabs/client";

let conversation: Conversation | null = null;

async function getToken(): Promise<string> {
  const response = await fetch("/api/token");
  if (!response.ok) throw Error("Failed to get conversation token");
  const data = await response.json();
  return data.token;
}

document.getElementById("start")!.addEventListener("click", async () => {
  await navigator.mediaDevices.getUserMedia({ audio: true });
  const token = await getToken();

  conversation = await Conversation.startSession({
    conversationToken: token,
    onConnect: () => {
      document.getElementById("status")!.textContent = "Connected";
      (document.getElementById("start") as HTMLButtonElement).disabled = true;
      (document.getElementById("stop") as HTMLButtonElement).disabled = false;
    },
    onDisconnect: () => {
      document.getElementById("status")!.textContent = "Disconnected";
      (document.getElementById("start") as HTMLButtonElement).disabled = false;
      (document.getElementById("stop") as HTMLButtonElement).disabled = true;
    },
    onError: (error) => console.error("Error:", error),
  });
});

document.getElementById("stop")!.addEventListener("click", () => {
  if (conversation) conversation.endSession();
});
```

#### 試してみる

以下の3つのプロセスが起動していることを確認してください。

1. **ngrok** - ポート3001に転送
2. **Speech Engineサーバー** - `python server.py`または`npx tsx server.mts`
3. **トークンサーバー** - `npx tsx token-server.mts`または`python token_server.py`

ブラウザでクライアントアプリケーションを開き、**会話を開始** をクリックします。求められたらマイクへのアクセスを許可してから話しかけてください。スピーカーからエージェントの応答が聞こえます。

サーバーで`debug: true`を有効にしている場合は、受信した文字起こしと送信する応答がコンソールに記録されます。

## セッションイベント

| イベント              | TypeScriptコールバック | Pythonコールバック    | 説明                                    |
| ----------------- | ---------------- | --------------- | ------------------------------------- |
| `user_transcript` | `onTranscript`   | `on_transcript` | ユーザーの音声を文字起こしします。会話履歴全体と中断シグナルが含まれます。 |
| `init`            | `onInit`         | `on_init`       | 会話IDでセッションを初期化します。                    |
| `close`           | `onClose`        | `on_close`      | ElevenLabsから正常に切断されました。               |
| `disconnected`    | `onDisconnect`   | `on_disconnect` | WebSocketが予期せず切断されました。                |
| `error`           | `onError`        | `on_error`      | プロトコルまたはWebSocketのエラー。                |

## エージェントの最初のメッセージを設定

デフォルトでは、エージェントはユーザーが先に話すのを待ちます。会話開始時にエージェントからユーザーにあいさつさせるには、セッション開始時にクライアントの`overrides`オプションで最初のメッセージを設定します。

エージェントが先に話せるようにするには、クライアントからこの設定を行えるようSpeech Engineリソースを更新する必要があります。

```python
engine = await elevenlabs.speech_engine.update(
    speech_engine_id="seng_8k3m9xr4hjnfg983brhmhkd98n6",
    overrides={
      "first_message": True,
    },
)
```

```typescript
const engine = await elevenlabs.speechEngine.update("seng_8k3m9xr4hjnfg983brhmhkd98n6", {
  overrides: {
    firstMessage: true,
  },
});
```

次に、クライアントSDKで最初のメッセージを設定します。

#### React

```tsx
conversation.startSession({
  conversationToken: token,
  overrides: {
    agent: {
      firstMessage: "Hello! How can I help you today?",
    },
  },
});
```

#### JavaScript

```typescript
const conversation = await Conversation.startSession({
  conversationToken: token,
  overrides: {
    agent: {
      firstMessage: "Hello! How can I help you today?",
    },
  },
});
```

接続が確立されるとすぐに、エージェントが最初のメッセージを発話します。このメッセージはサーバーの`onTranscript`コールバックをトリガーしません。すべてElevenLabs側で処理されます。

## 会話の最大時間を設定

デフォルトでは、会話は600秒（10分）後に終了します。Speech Engineは最大2,400秒（40分）の会話に対応しています。より長い会話を可能にするには、`max_duration_seconds`をより大きい値にしてSpeech Engineリソースを更新します。

```python
engine = await elevenlabs.speech_engine.update(
    speech_engine_id="seng_8k3m9xr4hjnfg983brhmhkd98n6",
    conversation={
        "max_duration_seconds": 2400,
    },
)
```

```typescript
const engine = await elevenlabs.speechEngine.update("seng_8k3m9xr4hjnfg983brhmhkd98n6", {
  conversation: {
    maxDurationSeconds: 2400,
  },
});
```

ダッシュボードでも設定できます。Speech Engineを開き、**詳細** タブに移動して、**会話の動作** の **会話の最大時間** を設定します。

> **Warning**
>
> 40分を超える会話はサポートされておらず、
> `max_duration_seconds`により大きい値が設定されている場合でも切断されることがあります。より長い会話が必要な場合は、[チームにお問い合わせください](https://elevenlabs.io/contact-sales)。

## 次のステップ

#### [JavaScript SDKリファレンス](/docs/ja/eleven-api/resources/libraries/speech-engine/javascript-sdk-reference)

JavaScript SDKのクラス、メソッド、イベント。

#### [Python SDKリファレンス](/docs/ja/eleven-api/resources/libraries/speech-engine/python-sdk-reference)

Python SDKのクラス、メソッド、イベント。

#### [APIリファレンス](/docs/ja/api-reference/speech-engine/create)

Speech Engineのすべてのパラメータと応答形式を確認できます。

#### [Next.jsのサンプルアプリ](https://github.com/elevenlabs/examples/tree/main/speech-engine/nextjs/quickstart)

完全なSpeech Engineクイックスタートアプリをローカルで実行します。