跳到内容

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

发布时间

收听收听本文

集成文本转语音 API 很简单,但需要先做几个关键决策:选择哪种传输方式、如何选模型和输出格式、如何流式处理、如何高并发不超限、如何缓存和重试避免重复付费、以及如何对比不同服务商的首字节响应时间。

为帮助你集成文本转语音 API,我们拆解了每个架构决策及应对方法。本指南将帮助你集成 ElevenLabs 文本转语音 API 并实现扩展,附带可直接用于生产环境的代码示例。

如需了解相关概念,可参考我们的 音频流式传输原理指南延迟优化指南,以及 ElevenLabs 模型概览

摘要

  • ElevenLabs 文本转语音 API 只有一个端点,可通过三种方式访问:批量转换、HTTP 流式、WebSocket 流式输入。
  • HTTP 下,无论批量还是流式,每个进行中的请求都会占用并发额度;WebSocket 下,只有正在生成音频时才计入并发。
  • 并发数建议略低于套餐上限,并对所有影响输出的参数做哈希缓存,避免重复计费。
  • 遇到 429 和 5xx 错误时,建议使用指数退避和全抖动重试,避免触及并发上限。

三种文本转语音 API 集成方式

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

同一个 POST /v1/text-to-speech/{voice_id} 调用有三种用法,适合不同场景。以下是三种集成方式的详细说明:

  • 批量(convert)是最简单的集成方式: 只需发送一个请求,返回一个音频响应。复杂度最低,但首段音频返回时间最长,因为需要等全部音频合成后才返回。
  • HTTP 流式(stream)请求方式相同,但响应为分块返回: 只需在路径后加 /stream,调用 stream 方法,音频将以分块形式返回。代码几乎一致,感知延迟大幅降低。
  • WebSocket(stream-input)保持长连接: 可分段发送文本,实时接收音频分块。适合交互式智能体或将 LLM 输出实时转为语音,边生成边播放。

流式处理不会让模型生成更快,推理时间不变。区别在于首段音频更早返回,用户等待时间更短,总耗时不变。

批量、流式、WebSocket 选择对比表

选择三种方式时,需要考虑多个因素。

简要建议:离线渲染选批量,用户等待文本选 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_v3
Most expressive, widest language range
Languages
eleven_flash_v2_5
32
eleven_flash_v2
English
eleven_multilingual_v2
29
eleven_v3
70+
Character limit
eleven_flash_v2_5
40,000
eleven_flash_v2
30,000
eleven_multilingual_v2
10,000
eleven_v3
5,000

约 75ms 是模型在典型条件下的推理时间,不含网络和应用延迟。输入越长或负载越高,耗时越多。建议用实际应用环境测量延迟,不要只看基准数据。

Flash 模型体积小,推理更快但近似更激进。Eleven v3 和 Multilingual v2 体积更大,每个字符处理更久,输出更丰富。没有既有 Eleven v3 质量又有 Flash 速度的设置,因为高质量本身需要更多计算。

实时或智能体场景建议用 eleven_flash_v2_5,延迟最低且支持多语言。旁白、有声书、营销配音建议用 eleven_multilingual_v2(高保真稳定)或 eleven_v3(表现力和情感最丰富)。

遇到电话号码、日期、货币等对发音有要求的内容,建议在应用层自行做数字规范化,按期望的发音拼写文本。

自行规范化能保证不同模型下发音一致,避免依赖模型默认设置(这些可能会变)。

输出格式

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

音色设置

以下设置影响生成语音的表现:

  • Stability: 控制一致性和表现力。值低更有表现力但不稳定,值高更稳定但变化少。
  • SimilarityBoost: 控制输出与参考声音的相似度。
  • Style: 提高后会夸大声音的自然说话风格。
  • useSpeakerBoost: 增强与原说话人相似度,延迟略有增加。
  • Speed: 调整语速,默认 1.0。

这些设置中,Stability 对感知质量影响最大。值低更有表现力但不稳定,值高更稳定更可预测。

选择音色时,最低延迟组合是 Flash 搭配即时语音克隆或默认音色;专业语音克隆音质更好,但每次生成会有额外延迟。

本指南示例音色 id 为 JBFqnCBsd6RMkjVDRZzb(George)。

流式集成(HTTP 和 WebSocket)

本节介绍文本转语音 API 集成的核心实践,包括安装 SDK、开启流式、实时消费音频。HTTP 适合网页和 App 播放,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 为第一个参数,后面是包含 camelCase 键(modelId、outputFormat、voiceSettings)的 options 对象:

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,首次发送带音色设置和前导空格的消息,随后按需发送文本消息,并读取返回的 JSON 帧(audio 字段为 base64 编码音频分块)。

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

高吞吐量集成受并发数限制,即同一时刻生成音频的请求数。每个套餐对每类模型有单独上限。

每个套餐的并发限制如下:

  • 免费版: 同时支持 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 建议略低于套餐上限,预留空间给同一 key 的其他流量,避免触发 429。

字符限制与长文本拆分

每个模型对单次请求的字符数有限制。长文本需拆分后合成,再拼接音频。

各模型单次请求字符上限如下:

  • Flash v2.5: 单次最多 40,000 字符。
  • Flash v2: 单次最多 30,000 字符。
  • Multilingual v2: 单次最多 10,000 字符。
  • Eleven v3:单次最多 5,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;
}

按顺序渲染分块并拼接音频。长篇旁白等每段独立的场景,可将拆分结果直接送入有界池自动处理。

缓存与幂等性

文本转语音输出具有确定性,同样文本、音色、模型和设置重复渲染会浪费资源。建议用影响音频的参数哈希做缓存,重试时同样可用作幂等性 token。

具体做法如下。

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 和音色设置。这样同一个 key 既可做缓存,也可做幂等性 token。客户端重试已成功的请求时,直接返回缓存内容,无需重新生成。

错误处理与限流(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");
}

对比其他服务商时,写一个同样结构的函数。用小型 runner 跑两家服务,丢弃一次预热,采集约 20 次分散样本,报告中位数和 p95(单位 ms)。

公平对比的关键是控制变量。

建议在同一台服务器和网络环境下测试,最好用实际部署的服务器而非家用宽带。输入文本一致,音频尽量短,让模型推理主导延迟。多次测试取中位数和 p95,单次测量无参考价值。

注意:公网 TTFB 包含 20-200ms 网络往返,与模型无关。我们在北美、欧洲和东南亚设有集群,会自动路由到最近节点,建议测试客户端与部署地同区,否则测到的主要是数据中心距离。

文本转语音 API 集成要点总结

生产级文本转语音 API 集成,核心在于几个关键决策。

这些选对了,其他问题都能顺利解决:

  • 按场景选模型: 交互场景用 Flash v2.5,离线渲染对音质有要求时选 Multilingual v2 或 Eleven v3
  • 用户等待时用流式: 已知文本用 HTTP 流式,智能体用 WebSocket,空闲时间不占并发额度。
  • 并发数控制在套餐上限内: 并发请求数略低于套餐上限,所有影响输出的参数都做哈希缓存,避免重复计费。
  • 429 和 5xx 用指数退避和全抖动重试: 429 和 5xx 错误时退避重试,关注并发响应头,随时掌握距离上限的情况。
  • 长文本按句子拆分: 每个模型字符上限内按句子拆分,保证语音连贯。

想深入了解,可参考 流式集成操作指南音频流式原理身份验证,以及 客户端一次性 token

用 ElevenAPI 构建文本转语音集成

看完本指南,你已掌握生产级文本转语音 API 集成的所有模式。无论流式、批量、缓存、重试还是基准测试,都可直接落地。

建议先进一步了解 文本转语音 API注册,立即用 ElevenAPI 发起首次调用。

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

相关内容

用高质量 AI 音频创作