> 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.

# 实时流式传输对话

文本转对话 WebSocket（`/v1/text-to-dialogue/stream-input`）会保持单个连接处于打开状态，让你发送对话行并接收 Base64 编码的音频块。它仅适用于 **Eleven v3** 和 **Eleven v4** 对话模型（`model_id` 必须以 `eleven_v3` 或 `eleven_v4` 开头）。

> **Note**
>
> 本指南介绍 **文本转对话** WebSocket。如需使用 **Flash**、**Multilingual v2** 或其他
> 非 v3 TTS 模型，请使用[实时 TTS WebSocket](/docs/zh/eleven-api/guides/how-to/websockets/realtime-tts)。如需并排了解
> 两种协议的摘要，请参阅[文本转语音与文本转对话 WebSocket](/docs/zh/eleven-api/guides/how-to/websockets/tts-vs-ttd-websockets)。

## 要求

* 拥有 ElevenLabs 账户和 API 密钥（[身份验证](/docs/zh/api-reference/authentication)）。
* API 密钥必须具有 `Text to Speech` 权限。
* 设备上已安装 Python 或 Node.js。

## 设置

**`Python`**

```python Python
pip install python-dotenv websockets
```

**`TypeScript`**

```typescript TypeScript
npm install dotenv ws
npm install @types/dotenv @types/ws --save-dev
```

创建 `.env` 文件：

**`.env`**

```bash .env
ELEVENLABS_API_KEY=your_elevenlabs_api_key_here
```

从[声音库](https://elevenlabs.io/voice-library)选择一个**音色 ID**。以下示例使用 `eleven_v4_turbo`，它允许每个连接注册**一个**音色。

## 打开 WebSocket

使用 `model_id` 和 `output_format` 等查询参数连接到 `wss://api.elevenlabs.io/v1/text-to-dialogue/stream-input`。可以在 `xi-api-key` 标头中发送 API 密钥，也可以在第一条 JSON 消息中发送（此处为便于各种语言遵循统一模式，展示的是消息正文方式）。

**`text-to-dialogue-websocket.py`**

```python text-to-dialogue-websocket.py
import asyncio
import base64
import json
import os

from dotenv import load_dotenv
import websockets

load_dotenv()
ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY")
VOICE_ID = "21m00Tcm4TlvDq8ikWAM"
MODEL_ID = "eleven_v4_turbo"

URI = (
    "wss://api.elevenlabs.io/v1/text-to-dialogue/stream-input"
    f"?model_id={MODEL_ID}&output_format=mp3_44100_128"
)
```

**`text-to-dialogue-websocket.ts`**

```typescript text-to-dialogue-websocket.ts
import * as dotenv from "dotenv";
import * as fs from "node:fs";
import WebSocket from "ws";

dotenv.config();
const ELEVENLABS_API_KEY = process.env.ELEVENLABS_API_KEY;
const voiceId = "21m00Tcm4TlvDq8ikWAM";
const modelId = "eleven_v4_turbo";

const uri = `wss://api.elevenlabs.io/v1/text-to-dialogue/stream-input?model_id=${modelId}&output_format=mp3_44100_128`;
const websocket = new WebSocket(uri);

const outputDir = "./output";
try {
  fs.accessSync(outputDir, fs.constants.R_OK | fs.constants.W_OK);
} catch {
  fs.mkdirSync(outputDir);
}
const writeStream = fs.createWriteStream(`${outputDir}/dialogue-ws.mp3`, { flags: "a" });
```

## 注册音色并流式传输文本

发送包含 `voices`（必填）的**第一条消息**；如果未设置 `xi-api-key` 标头，还需包含 `xi_api_key`。随后发送一个或多个包含 `inputs` 的帧：每个项目都有 `text`、`voice_id` 和可选的 `new_turn`。

服务器会缓冲文本，直到获得足够上下文（约 **40 个字符**和 **8 个词**），然后输出 `audio` 块。响应字段使用 **snake_case**（例如 `is_final`）。

**`text-to-dialogue-websocket.py`**

```python text-to-dialogue-websocket.py
async def stream_dialogue():
    async with websockets.connect(URI) as websocket:
        await websocket.send(
            json.dumps(
                {
                    "voices": [VOICE_ID],
                    "xi_api_key": ELEVENLABS_API_KEY,
                }
            )
        )

        line = (
            "This is a longer line of dialogue used to exceed the minimum buffer so the model "
            "starts generating streamed audio for the registered voice. "
        )
        await websocket.send(
            json.dumps(
                {
                    "inputs": [
                        {"text": line, "voice_id": VOICE_ID, "new_turn": False},
                    ],
                }
            )
        )

        await websocket.send(json.dumps({"close_socket": True}))

        os.makedirs("output", exist_ok=True)
        out_path = "output/dialogue-ws.mp3"
        with open(out_path, "wb") as audio_file:
            while True:
                raw = await websocket.recv()
                msg = json.loads(raw)
                if msg.get("error"):
                    raise RuntimeError(msg)
                if msg.get("audio"):
                    audio_file.write(base64.b64decode(msg["audio"]))
                if msg.get("is_final"):
                    break
        print(f"Wrote {out_path}")


asyncio.run(stream_dialogue())
```

**`text-to-dialogue-websocket.ts`**

```typescript text-to-dialogue-websocket.ts
websocket.on("open", () => {
  websocket.send(
    JSON.stringify({
      voices: [voiceId],
      xi_api_key: ELEVENLABS_API_KEY,
    })
  );

  const line =
    "This is a longer line of dialogue used to exceed the minimum buffer so the model starts generating streamed audio for the registered voice. ";

  websocket.send(
    JSON.stringify({
      inputs: [{ text: line, voice_id: voiceId, new_turn: false }],
    })
  );

  websocket.send(JSON.stringify({ close_socket: true }));
});

websocket.on("message", (data) => {
  const msg = JSON.parse(data.toString());
  if (msg.error) {
    console.error(msg);
    return;
  }
  if (msg.audio) {
    writeToLocal(msg.audio, writeStream);
  }
});

function writeToLocal(base64str: string, stream: fs.WriteStream) {
  stream.write(Buffer.from(base64str, "base64"));
}

websocket.on("close", () => {
  writeStream.end();
});
```

`close_socket` 会刷新所有缓冲文本，发送剩余音频，然后在连接关闭前发送一个 `is_final: true` 的最终帧。如需在对话行之间**保持连接打开**，请在会话结束前省略 `close_socket`；使用 `flush` 可在不关闭连接的情况下，强制为较短的缓冲文本生成音频。

## 运行脚本

**`Python`**

```python Python
python text-to-dialogue-websocket.py
```

**`TypeScript`**

```typescript TypeScript
npx tsx text-to-dialogue-websocket.ts
```

应会在 `output/` 下获得一个 MP3 文件（文件名与上述示例相同）。

## 行为说明

### 缓冲

与 TTS WebSocket 的 `chunk_length_schedule` 不同，对话流式传输会在输出第一段部分音频前使用**固定的服务器阈值**（字符数和词数）。如果发送短句时出现延迟，请在每个 `inputs` 帧中稍微合并更多文本，或发送 `flush: true` 以在不关闭 socket 的情况下强制生成。

### 对话轮次和音色

说话者结束一轮发言时，设置 `new_turn: true`，让韵律清晰重置。在 `inputs` 条目之间更改 `voice_id` 也会开启新一轮。使用 `eleven_v4_turbo` 时，请在 `voices` 中注册**恰好一个**音色；`eleven_v4` 最多支持 **10 个**已注册音色。

### 非活动状态

如果服务器在 **20 秒**内**未收到客户端消息**，连接将结束。发送 `{"keep_alive": true}` 可在不合成音频的情况下重置计时器。

### 并发

每个打开的连接会在保持打开期间占用一个对话会话，该会话来自独立的专用池，与套餐的标准并发限制分开。通过连接生成的音频不计入标准并发。请参阅[文本转对话并发](/docs/zh/overview/models#text-to-dialogue-concurrency)。

### 对齐

在查询字符串中添加 `sync_alignment=true`，即可在可用时接收块上的 `alignment` 对象（snake_case 时间数组）。请参阅 [API 参考文档](/docs/zh/api-reference/text-to-dialogue/ttd-websocket)。

## 后续步骤

#### [TTS 与 TTD WebSocket](/docs/zh/eleven-api/guides/how-to/websockets/tts-vs-ttd-websockets)

选择合适的 WebSocket，并比较消息结构。

#### [文本转对话 WebSocket（API）](/docs/zh/api-reference/text-to-dialogue/ttd-websocket)

查询参数、消息架构和示例。

#### [流式传输对话（HTTP）](/docs/zh/api-reference/text-to-dialogue/stream)

当无需 WebSocket 即可获得完整请求文本时使用。

#### [实时 TTS WebSocket](/docs/zh/eleven-api/guides/how-to/websockets/realtime-tts)

通过 WebSocket 进行单音色非 v3 流式传输。