推出 Eleven v4认识 Eleven v4:迄今情感表现最丰富的模型。

跳至内容

文本转语音 API 集成:流式、批量、重试

发布时间
最近更新

收听收听本文

集成文本转语音 API 很简单……前提是先做出几个关键决策:使用哪种传输模式、如何选择模型和输出格式、如何进行流式传输、如何在不超出并发限制的情况下处理高吞吐量、如何缓存和重试以避免为同一段音频重复付费,以及如何与其他服务商对比首字节时间。

为帮助你集成文本转语音 API,我们将逐一拆解这些架构决策及相应做法。本指南将帮助你集成 ElevenLabs 文本转语音 API 并实现扩展,提供可直接粘贴到生产环境中使用的代码片段。

如需全面了解本文涉及的概念,请参阅我们的了解音频流式传输、优化延迟以及ElevenLabs 模型概览。 

摘要

  • ElevenLabs 提供一个文本转语音 API 端点,可通过 3 种方式调用:批量转换、HTTP 流式传输和 stream-input WebSocket。
  • 通过 HTTP 时,每个进行中的请求都会占用并发额度;通过 WebSocket 时,只有活跃生成会计入并发。
  • 将并行度设为略低于套餐上限,并缓存所有影响输出参数的哈希值,避免为同一段文本重复计费。
  • 对 429 和 5xx 使用指数退避与完全抖动重试,在触及并发上限前主动降速。

集成文本转语音 API 的 3 种方式

文本转语音只有一个端点,但集成方式会影响延迟、复杂度和成本。 

同一个 POST /v1/text-to-speech/{voice_id} 调用可采用 3 种形式,分别适合略有不同的任务。以下是集成文本转语音 API 的 3 种方式:

  • 批量(convert)是最简单的集成方式:发送一次请求,即可获得一次音频响应。它的实现复杂度最低,但首段音频时间最高,因为必须先合成完整音频片段,才会返回任何字节。
  • HTTP 流式传输(stream)使用相同请求,但会分块返回响应:在路径后附加 /stream,调用 stream 方法,音频便会以分块响应返回。代码几乎相同,但感知延迟低得多。
  • WebSocket(stream-input)会保持持久连接:可以逐步发送文本,并持续接收音频块。它专为交互式智能体设计,也适用于在 LLM 生成 token 时、句子尚未结束前,将其输出传入语音生成。

流式传输不会让模型更快生成音频,推理时间并不会改变。它改变的是收到首个音频块的时机:完整音频片段完成前就会发送首块,因此总工作量相同,但用户感知的等待时间更短。

Text-to-speech API integration guide comparison of batch, HTTP streaming, and WebSocket methods and tradeoffs.

批量、流式传输与 WebSocket 对比决策表

在这 3 种方法之间选择时,需要考虑多个因素。

快速参考:离线渲染请选择批量转换;用户正在等待的已知文本请选择 HTTP 流式传输;智能体和实时 LLM 转语音请选择 WebSocket。 

下表按大规模使用时关键的维度,拆解各种方案的取舍。

Batch (convert)
Time-to-first-audio
Highest (wait for full clip)
Implementation complexity
Lowest
Text known up front?
Required
Streaming LLM output into TTS
Awkward
Concurrency cost
Each request counts fully
Best for
Offline rendering, audiobooks, caching
HTTP streaming
Time-to-first-audio
Low
Implementation complexity
Low
Text known up front?
Required
Streaming LLM output into TTS
Awkward
Concurrency cost
Each request counts fully
Best for
Web/app playback of known text
WebSocket (stream-input)
Time-to-first-audio
Lowest
Implementation complexity
Highest (connection lifecycle, framing)
Text known up front?
Not required - send incrementally
Streaming LLM output into TTS
Native fit
Concurrency cost
Only active generation counts
Best for
Voice agents, live LLM to speech

无论使用批量还是流式传输,通过 HTTP 发起的每个进行中请求,都会在整个请求期间占用套餐的并发额度。通过 WebSocket 时,只有模型实际生成音频的时间会计入;打开但空闲的套接字几乎不产生消耗。

对于级联式语音智能体,它会在整个对话期间保持连接,但仅在智能体发言时生成音频,因此差异很大。这也是构建智能体时使用 WebSocket 的主要原因。完整协议请参阅实时文本转语音 WebSocket 指南。

选择模型和输出格式

TTS API 集成返回的音频由两项选择决定:一是模型,决定质量和速度;二是输出格式,决定容器、比特率和采样率。

从一开始就选对这两项,可让后续各环节顺利衔接,例如延迟和电话系统兼容性。

模型

我们提供多种文本转语音模型。它们并非从优到劣排序,每种模型都有不同取舍。

Best for
eleven_flash_v2_5
Real-time, agents, bulk throughput (~75ms model inference)
eleven_flash_v2
Real-time, English only (~75ms)
eleven_multilingual_v2
Highest stable fidelity, narration
eleven_v4
Most expressive, widest language range
Languages
eleven_flash_v2_5
32
eleven_flash_v2
English
eleven_multilingual_v2
29
eleven_v4
90+
Character limit
eleven_flash_v2_5
40,000
eleven_flash_v2
30,000
eleven_multilingual_v2
10,000
eleven_v4
5,000

需要说明的是,约 75ms 指的是代表性条件下的模型推理时间,不包括网络和应用延迟。输入较长或负载较高时,时间会增加。请始终从自己的应用中测量,不要只参考基准测试数字。

Flash 模型更小,采用更激进的近似方法来缩短推理时间。Eleven v4 和 Multilingual v2 是更大的模型,会为每个字符投入更多时间,以生成更丰富的输出。

对于实时或智能体场景,请使用 eleven_flash_v2_5;它是延迟最低的多语言选项。对于旁白、有声书或营销旁白配音,如果需要稳定的高保真效果,请使用 eleven_multilingual_v2;如果需要最高表现力和情感范围,请使用 eleven_v4。 

当发音很重要时,例如电话号码、日期或货币,请在文本到达 API 前自行在应用中规范化数字。请直接拼写出希望听到的读法。 

自行规范化可让不同模型的发音保持可预测,并避免依赖可能变化的模型默认行为。

输出格式

output_format 参数控制返回音频的容器、采样率和比特率。最常用的值包括:

Use case
mp3_44100_128
General playback, downloads, highest mp3 quality shown here
mp3_22050_32
Lower-bandwidth playback, smaller files
pcm_24000 / pcm_16000
Raw PCM for your own audio pipeline or further processing
ulaw_8000
Telephony - the format used with Twilio and similar systems
Languages
mp3_44100_128
32
mp3_22050_32
English
pcm_24000 / pcm_16000
29
ulaw_8000
70+
Character limit
mp3_44100_128
40,000
mp3_22050_32
30,000
pcm_24000 / pcm_16000
10,000
ulaw_8000
5,000

音色设置

以下设置控制生成语音的呈现方式:

  • 稳定性:控制一致性与表现力之间的平衡。较低值会产生更多变化、更具表现力的语音;较高值则带来更稳定、更可预测的表达。
  • SimilarityBoost:控制输出与参考音色的贴近程度。
  • Style:提高该值会强化音色的自然说话风格。
  • useSpeakerBoost:以少量延迟为代价,增强与原始说话者的相似度。
  • 速度:调整相对于默认值 1.0 的语速。

在这些设置中,稳定性通常对感知质量影响最大。较低值会带来更具表现力但一致性较低的输出;较高值则优先保证一致性和可预测性。

选择音色时,延迟最低的组合是 Flash 搭配即时语音克隆或默认音色;专业语音克隆效果出色,但会增加每次生成的开销,需纳入考量。

本指南中的示例音色 ID 为 JBFqnCBsd6RMkjVDRZzb(George)。

流式集成(HTTP 和 WebSocket)

本节介绍文本转语音 API 集成的实操核心,包括安装 SDK、打开流,以及在音频块到达时进行消费。HTTP 路径适用于大多数网页和应用播放场景,WebSocket 路径适用于智能体和实时 LLM 输出。

这两种方式均假定你已按如下方式初始化 ElevenLabs 客户端。

npm install @elevenlabs/elevenlabs-js
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

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

流式路径会打开一个流,并在音频块到达时进行消费。voiceId 是第一个位置参数,后接一个使用驼峰式键名的选项对象(modelId、outputFormat、voiceSettings):

const stream = await elevenlabs.textToSpeech.stream("JBFqnCBsd6RMkjVDRZzb", {
  text,
  modelId: "eleven_flash_v2_5",
  outputFormat: "mp3_44100_128",
  voiceSettings: { stability: 0, similarityBoost: 1.0, style: 0, useSpeakerBoost: true, speed: 1.0 },
});

for await (const chunk of stream) {
  // chunk is a Buffer; feed it to the player as it arrives
}

对于 WebSocket 变体,请连接到 wss://api.elevenlabs.io/v1/text-to-speech/{voice_id}/stream-input,先发送一条包含音色设置和前导空格的消息,然后在文本可用时发送文本消息,并读取 audio 字段包含 Base64 编码音频块的 JSON 帧。

高吞吐量的批处理与并发限制

高吞吐量集成受并发数制约,即同一时刻正在生成音频的请求数。每个套餐针对各模型系列设有上限。 

每个套餐都有不同的并发限制:

  • 免费版:4 个并发 Flash 请求。
  • 入门版:6 个并发 Flash 请求。
  • 创作者版:10 个并发 Flash 请求。
  • 专业版:20 个并发 Flash 请求。
  • 扩展版和商业版:30 个并发 Flash 请求;企业版限制可定制。

Multilingual v2 的限制约为上述数值的一半。

可通过限制同时运行的请求数量的有界池缓解这一问题:

// Set MAX_CONCURRENCY at or below your plan's Flash concurrency limit.
const MAX_CONCURRENCY = 8;

async function synthMany(texts: string[]): Promise<Buffer[]> {
  const results: Buffer[] = [];
  for (let i = 0; i < texts.length; i += MAX_CONCURRENCY) {
    const batch = texts.slice(i, i + MAX_CONCURRENCY);
    results.push(...(await Promise.all(batch.map(eachSingleRequest)))); // never more than MAX_CONCURRENCY in flight
  }
  return results;

将 MAX_CONCURRENCY 设为略低于套餐上限,而非刚好等于上限。这部分余量可吸收使用相同密钥的其他流量,避免触及返回 429 的阈值。

字符限制与长文本拆分

每个模型都会限制单个请求可接受的字符数。任何长文本集成都必须拆分文本,再将音频拼接起来。 

以下是各模型的单请求字符限制:

  • Flash v2.5:每个请求最多接受 40,000 个字符。
  • Flash v2:每个请求最多接受 30,000 个字符。
  • Multilingual v2:每个请求最多接受 10,000 个字符。
  • Eleven v3:每个请求最多接受 5,000 个字符。
  • Eleven v4:每个请求最多接受 10,000 个字符。

超过限制的文本必须拆分为多个请求。应尽量按句子边界拆分,以便在片段接缝处保留韵律。

function splitText(text: string, maxChars: number): string[] {
  const sentences = text.trim().split(/(?<=[.!?])\s+/);
  const chunks: string[] = [];
  let current = "";
  for (let sentence of sentences) {
    if (current.length + sentence.length + 1 > maxChars) {
      if (current) chunks.push(current.trim());
      // A single sentence longer than the limit is hard-split.
      while (sentence.length > maxChars) {
        chunks.push(sentence.slice(0, maxChars));
        sentence = sentence.slice(maxChars);
      }
      current = sentence;
    } else {
      current = `${current} ${sentence}`.trim();
    }
  }
  if (current) chunks.push(current.trim());
  return chunks;
}

按顺序渲染各片段并拼接音频。对于每个片段彼此独立的长篇旁白,这两部分可直接组合:将 splitText 的输出传入上述有界池,其余工作由它处理。

缓存与幂等性

文本转语音输出具有足够的确定性,因此使用相同音色、模型和设置重新渲染相同文本会造成浪费。根据影响音频的输入哈希缓存结果,同一个键还可在重试时作为幂等令牌。

具体做法如下。

import { createHash } from "node:crypto";

function cacheKey(text: string, voiceId: string, modelId: string,
                  outputFormat: string, settings: object): string {
  // Every parameter that changes the audio must be in the key.
  const payload = JSON.stringify({ text, voiceId, modelId, outputFormat, settings });
  return createHash("sha256").update(payload).digest("hex");
}

async function cachedSynth(text: string, voiceId: string, modelId: string,
                           outputFormat: string, settings: object): Promise<Buffer> {
  const key = cacheKey(text, voiceId, modelId, outputFormat, settings);
  const cached = await cacheGet(key);          // e.g. read from disk or S3
  if (cached) return cached;

  const audio = await elevenlabs.textToSpeech.convert(voiceId, { text, modelId, outputFormat });
  await cachePut(key, audio);                   // store the bytes under the key
  return audio;
}

实现这一点的规则是:所有会改变音频的参数都必须包含在键中,包括 outputFormat 和音色设置。配置正确时,同一个键可兼作幂等令牌。客户端重试已成功的请求时,返回缓存的字节即可,无需再次生成。

错误处理与速率限制(429)

生产环境客户端需要结合退避和抖动进行重试,并按状态码采取不同处理方式,因为有些故障值得重试,有些则不值得。 

下表将每种状态映射到相应操作,本节还会说明为什么 429 是软限制而非硬性屏障。

Meaning
401
Authentication failed
422
Invalid request
429
Concurrency exceeded
5xx
Transient server error
Action
401
Do not retry. Check the xi-api-key header and key validity.
422
Do not retry. Fix the payload (bad voice id, unsupported format, text over limit).
429
Retry with exponential backoff and jitter.
5xx
Retry with backoff.
Character limit
401
40,000
422
30,000
429
10,000
5xx
5,000

429 并非硬性屏障,了解其机制很有帮助。超过并发限制时,请求会先按优先级排队,通常会增加约 50ms。只有之后仍超出容量时,才会收到 429。 

响应还会包含 current-concurrent-requests 和 maximum-concurrent-requests 标头,用于显示实时余量,因此可读取这些标头,在达到上限前主动降速。

const RETRYABLE = new Set([429, 500, 502, 503, 504]);

async function synthWithRetry(text: string, voiceId: string, maxRetries = 5): Promise<Buffer> {
  let delay = 500; // ms, base for exponential backoff
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await elevenlabs.textToSpeech.convert(voiceId, {
        text, modelId: "eleven_flash_v2_5", outputFormat: "mp3_44100_128",
      });
    } catch (err: any) {
      const status = err.statusCode;
      // 401/422 and exhausted retries are not recoverable here.
      if (!RETRYABLE.has(status) || attempt === maxRetries) throw err;
      // Exponential backoff with full jitter.
      await new Promise((r) => setTimeout(r, Math.random() * delay));
      delay = Math.min(delay * 2, 8000);
    }
  }
  throw new Error("unreachable");
}

如果需要的是更多余量而非更好的重试行为,请升级套餐。企业版客户可通过客户经理申请提高限制。

延迟与首字节时间基准测试

延迟取决于所在区域、输入内容和当前负载,因此唯一值得依赖的延迟数据,是从自己环境中测得的数据。 

本节提供 Flash 流式端点的首字节时间(TTFB)测试方式,其结构允许你将同一套测试工具用于其他服务商,并在相同条件下进行比较。

请将其视为一种方法,而非已发布的结果。单次运行无法保证任何结果。 

对文本转语音 API 集成的延迟进行基准测试时,请注意以下几点:

  • 计入网络往返时间:TTFB 取决于所在位置和服务商最近的集群,因此应从服务器通常运行的位置执行测试。
  • 舍弃一次预热运行:对冷连接的首次请求较慢,可能会扭曲数据。
  • 保持输入不变:输入长度、音色、模型和负载都会影响结果,因此不同服务商之间应保持完全一致。
  • 报告分布数据:每次运行的数值会有差异,因此请报告中位数和 p95,而非单一数值。

考虑完这些因素后,就可以开始进行基准测试了。

const TEXT = "This is a fixed benchmark sentence used for every provider.";

async function measureElevenLabs(): Promise<number> {
  const start = performance.now();
  const res = await fetch(
    "https://api.elevenlabs.io/v1/text-to-speech/JBFqnCBsd6RMkjVDRZzb/stream?output_format=mp3_44100_128",
    {
      method: "POST",
      headers: { "xi-api-key": process.env.ELEVENLABS_API_KEY!, "Content-Type": "application/json" },
      body: JSON.stringify({ text: TEXT, model_id: "eleven_flash_v2_5" }),
    },
  );
  for await (const _ of res.body!) {
    return performance.now() - start; // first chunk received
  }
  throw new Error("no audio returned");
}

若要与其他服务商比较,请编写一个结构相同的函数。然后通过一个小型运行器驱动两者:舍弃一次预热调用,采集约 20 个间隔足够大、不互相冲突的计时样本,并以毫秒报告中位数和 p95。

公平比较的关键在于控制变量。 

从同一台机器和网络测试两家服务商,最好使用实际部署区域中的服务器,而非住宅宽带上的笔记本电脑。使用相同输入文本,并保持音频较短,让模型推理而非生成长度主导数值。请汇总多次运行的中位数和 p95,因为单次测量只是噪声。 

请注意,公共互联网的 TTFB 包含 20–200ms 与模型无关的网络往返时间。我们在北美、欧洲和东南亚设有集群,并路由至最近的集群,因此应相应地将测试客户端部署在附近,否则测试的大多只是到数据中心的距离。

文本转语音 API 集成要点

生产环境的文本转语音 API 集成,归根结底取决于几个重要决策。

做好以下几点,其余环节便会顺利衔接:

  • 按任务选择模型:所有交互式场景均使用 Flash v2.5;对于延迟要求较低的离线渲染,使用 Multilingual v2 等高保真模型或Eleven v4。
  • 只要用户在等待,就使用流式传输:已知文本使用 HTTP 流式传输,智能体使用 WebSocket,避免空闲时间占用并发预算。
  • 将并行度限制在套餐上限内:将并发请求数设为略低于套餐上限,并根据所有影响输出的参数哈希进行缓存,避免为同一段音频重复计费。
  • 使用指数退避与完全抖动重试 429 和 5xx:针对 429 和 5xx 使用完全抖动退避,并关注并发标头,了解距离上限还有多远。
  • 按句子边界拆分长文本:在各模型的字符限制内按句子边界拆分,以保留韵律,使其跨越片段接缝。

如需进一步深入了解,请查看流式传输操作指南、音频流式传输概念、身份验证以及供客户端使用的一次性令牌。

使用 ElevenAPI 构建文本转语音集成

阅读完本指南后,你已掌握生产环境文本转语音 API 集成所需的全部模式。无论是流式传输、批处理、缓存、重试,还是基准测试,你都可以着手投入实际运行。 

先了解文本转语音 API,或注册,立即通过ElevenAPI 发起首次调用。

文本转语音 API 集成常见问题

作者

Tadas Petra 是一名开发者关系工程师,致力于帮助开发者使用 ElevenLabs 产品。他还是开发者教育平台 Hungrimind 的创始人。此前曾任 Agora 首席开发者布道师和 Zero To Mastery 讲师。

相关内容

用高质量 AI 音频创作