Hoppa till innehållet

AI-ratebegränsning för röst: Samtidighet, köer och 429:or

Publicerad
Senast uppdaterad

LyssnaLyssna på den här artikeln

De flesta team hanterar AI-rate limiting för röst på samma sätt som andra API:er: de begränsar antalet förfrågningar per minut, försöker igen när servern säger ifrån och går vidare. För arbetslaster på ElevenLabs fungerar den modellen inte vid den första trafiktoppen, eftersom gränsen du faktiskt når är samtidighet, inte antalet förfrågningar.

Den här guiden förklarar varför samtidighet är den verkliga begränsningen och går sedan igenom mönster på klientsidan som hjälper dig hålla dig inom den. Från begränsade samtidighetspooler och smidig hantering av 429 till rättvisa för flera kunder samt token- och läckande hinkar erbjuder vi praktiska system som du kan implementera. Vi har kopplat varje mönster till en fungerande TypeScript-implementation som du kan anpassa.

Om du bygger röstagenter, berättelsepipelines eller andra produktionssystem ovanpå våra modeller och vill skala upp, är den här guiden för dig.

Sammanfattning

  • AI-rate limiting för röst handlar om samtidighetskontroll, inte om att räkna förfrågningar per minut.
  • När du når gränsen för rate limiting avvisas trafiken inte direkt. I stället hamnar förfrågningarna i en prioritetskö som lägger till omkring 50 ms.
  • Om du överskrider kapaciteten även efter köhantering uppstår ett HTTP 429-fel.
  • WebSockets ökar den effektiva kapaciteten kraftigt, eftersom bara aktiv generering räknas mot din gräns.
  • System för flera kunder behöver ett extra rättviselager: hinkar per kund, viktad rättvis köhantering, reserverad marginal och uppdelning mellan nycklar för isolering. 
  • Två svarshuvuden, current-concurrent-requests och maximum-concurrent-requests, visar var du står med AI-rate limiting.

Varför gränsen är samtidighet, inte förfrågningar per minut

Samtidighet är antalet förfrågningar som pågår vid samma tidpunkt. Förfrågningar per minut är genomströmningen under ett tidsintervall. Det är viktigt att förstå skillnaden eftersom den avgör vilken åtgärd som håller dig inom gränsen.

När du använder en av ElevenLabs modeller skalar serverbelastningen med antalet samtidiga användare. Ljudgenerering upptar en plats under hela genereringen, och tiden varierar beroende på inmatningens längd, modell och belastning.

Ett tak för förfrågningar per minut säger ingenting om hur många platser som är upptagna just nu, vilket är det enda servern mäter.

Gränser per plan och modellfamilj

Din budget för samtidighet är inte ett enda tal. Samtidighetsgränser skiljer sig mellan planer och modellfamiljer. Till exempel har Speech to Text en högre gräns än Text to Speech, eftersom transkriberingsförfrågningar vanligtvis är kortare och systemet kan hantera fler av dem samtidigt.

Multilingual v2
Free
2
Starter
3
Creator
5
Pro
10
Scale
15
Business
15
Enterprise
Elevated
Flash
Free
4
Starter
6
Creator
10
Pro
20
Scale
30
Business
30
Enterprise
Elevated
STT
Free
8
Starter
12
Creator
20
Pro
40
Scale
60
Business
60
Enterprise
Elevated
Realtime STT
Free
6
Starter
9
Creator
15
Pro
30
Scale
45
Business
45
Enterprise
Elevated
Queue weight
Free
3
Starter
4
Creator
5
Pro
5
Scale
5
Business
5
Enterprise
6

Gränsen gäller per modellfamilj. Om du kör Flash för agenter och Multilingual v2 för berättarröst använder du två separata budgetar samtidigt. Aktuella siffror per plan och avsnittet om samtidighet finns dokumenterade på modellsidan.

Vad händer när du når samtidighetsgränsen?

När du når samtidighetsgränsen avvisas trafiken inte direkt. Systemet hanterar situationen mjukt via en prioritetskö och går över till fullständig avvisning först när du fortfarande överskrider den totala kapaciteten för rate limiting.

Så länge du ligger under gränsen körs förfrågningar direkt. När du når den placeras efterföljande förfrågningar i en kö som sorteras efter din plans prioritetsnivå. Kön lägger vanligtvis till omkring 50 ms latens, så en kort överskridning märks oftast inte av användarna.

Om systemet fortfarande saknar kapacitet efter köhanteringen får du ett HTTP 429. Det är signalen att sakta ned i stället för att försöka igen direkt. Prioritetsnivån i tabellen avgör hur dina köade förfrågningar ordnas i förhållande till annan trafik; högre planer tömmer kön snabbare.

HTTP jämfört med WebSocket: Så räknas de mot din gräns

Vilket transportprotokoll du väljer påverkar direkt rate limiting och budget. Samma inkommande konversation kan använda mycket olika mycket av din samtidighetsbudget beroende på om den körs via HTTP eller WebSocket.

Via HTTP räknas varje förfrågan individuellt mot din samtidighetsgräns under hela dess varaktighet. Via WebSocket räknas bara tiden då modellen aktivt genererar ljud. En öppen men inaktiv WebSocket räknas i stort sett inte.

För en röstagent har en konversation långa perioder då ingen pratar och modellen inte genererar något. Med HTTP skulle du hålla en plats under förfrågans hela längd i varje tur. Med WebSocket används platsen bara under millisekunderna av aktiv generering, så en samtidighetsplats kan delas mellan många konversationer. 

Se guiden för WebSocket med TTS i realtid för protokolldetaljer. För interaktiv trafik är WebSockets det bästa standardvalet.

Varför ~5 samtidiga förfrågningar kan stödja ~100 sändningar

Matematiken bakom samtidighet är kontraintuitiv tills du tar hänsyn till uppspelningstiden. Generering går mycket snabbare än uppspelning, och en plats är bara aktivt upptagen när ljud genereras. Det är just detta glapp som gör att en liten budget kan betjäna en stor publik.

En förfrågan som tar en bråkdel av en sekund att generera producerar flera sekunders ljud som lyssnaren sedan spelar upp. Under uppspelningen frigörs platsen och blir tillgänglig för andra lyssnare.

Som tumregel kan en samtidighetsgräns på 5 stödja ungefär 100 samtidiga ljudsändningar. Det exakta antalet beror på rösten, talets tempo och hur mycket tystnad som finns mellan yttranden.

Svarshuvudena som visar var du står

Du behöver inte uppskatta var du ligger i förhållande till din gräns. Varje svar innehåller två siffror som du kan använda för att mäta tillgänglig marginal i stället för att bara gissa.

Håll utkik efter dessa två svarshuvuden:

  • current-concurrent-requests: hur många förfrågningar pågår just nu?
  • maximum-concurrent-requests: din gräns för den modellfamiljen.

Tillsammans ger dessa svarshuvuden en realtidsöversikt över din aktuella användning och tillgängliga kapacitet. Du ska inte behöva gissa innan du stöter på AI-gränser.

Strategier på klientsidan för AI-rate limiting

Det finns fyra grundverktyg som täcker nästan alla scenarier för AI-rate limiting:

  • En tokenhink: Om tokens finns tillgängliga kan förfrågningar fortsätta. Kapaciteten fylls på över tid, vilket gör att den kan hantera korta toppar utan att nå rate limits.
  • En läckande hink: Jämnar ut inkommande trafik till en fast utmatningstakt, vilket hindrar plötsliga toppar från att överbelasta dina nedströmsystem.
  • En begränsad samtidighetspool: Begränsar det totala antalet förfrågningar som kan vara aktiva samtidigt, så att du aldrig överskrider gränser för samtidiga förfrågningar.
  • Exponentiell backoff med full jitter: Ökar successivt tiden mellan misslyckade förfrågningar för att förhindra att alla klienter försöker igen samtidigt.

Avsnitten nedan visar hur du bygger upp dem en i taget, med början i den som motsvarar samtidighetsgränsen mest direkt.

Alla kodexempel nedan utgår från en enda klient som initieras en gång:

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

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

Begränsad samtidighet: grundverktyget som matchar gränsen

Eftersom servern mäter samtidighet är den mest direkta styrningen på klientsidan en begränsad worker-pool som sätter ett tak för hur många förfrågningar som pågår samtidigt. Sätt taket lite under gränsen för din plan, så att det finns utrymme för prioritetskön och jitter.

async function pool<T, R>(
  items: T[],
  maxInFlight: number,
  worker: (item: T) => Promise<R>,
): Promise<R[]> {
  const results: R[] = new Array(items.length);
  let next = 0;

  async function run(): Promise<void> {
    while (next < items.length) {
      const i = next++;
      results[i] = await worker(items[i]); // never more than maxInFlight of these run at once
    }
  }

  await Promise.all(
    Array.from({ length: Math.min(maxInFlight, items.length) }, run),
  );
  return results;
}

async function synthesize(text: string): Promise<Buffer> {
  const stream = await elevenlabs.textToSpeech.stream("JBFqnCBsd6RMkjVDRZzb", {
    text,
    modelId: "eleven_flash_v2_5",
    outputFormat: "mp3_44100_128",
  });
  const chunks: Buffer[] = [];
  for await (const chunk of stream) chunks.push(Buffer.from(chunk));
  return Buffer.concat(chunks);
}

// Plan Flash limit is, say, 10. Stay under it.
const texts = Array.from({ length: 50 }, (_, i) => `Sentence number ${i}.`);
const audio = await pool(texts, 8, synthesize); // never more than 8 in flight

Tokenhink: tillåt toppar, begränsa genomsnittet

En tokenhink rymmer upp till capacity tokens och fylls på med refillRate tokens per sekund. Varje förfrågan förbrukar en token, så hinken tillåter korta toppar upp till sin storlek samtidigt som den begränsar takten på lång sikt. 

Det är rätt verktyg för att jämna ut ögonblicket då en arbetskö plötsligt anländer, så att du inte skickar allt på en gång och skapar en topp i samtidigheten.

class TokenBucket {
  private tokens: number;
  private updated = performance.now();

  constructor(private capacity: number, private refillPerSec: number) {
    this.tokens = capacity;
  }

  private refill(): void {
    const now = performance.now();
    const elapsed = (now - this.updated) / 1000;
    this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillPerSec);
    this.updated = now;
  }

  tryAcquire(cost = 1): boolean {
    this.refill();
    if (this.tokens >= cost) {
      this.tokens -= cost;
      return true;
    }
    return false;
  }

  timeUntil(cost = 1): number {
    this.refill();
    return this.tokens >= cost ? 0 : ((cost - this.tokens) / this.refillPerSec) * 1000;
  }
}

Läckande hink: upprätthåll ett jämnt flöde

I vissa fall vill du inte tolerera toppar alls. En läckande hink släpper igenom arbete i en fast, konstant takt oavsett hur ojämn inmatningen är. Det är ett bättre val när nedströmssystemet föredrar en jämn, förutsägbar belastning framför tillfälliga toppar.

Till exempel när du medvetet håller dig väl inom en liten samtidighetsbudget som delas med andra tjänster.

class LeakyBucket {
  private next = performance.now();
  constructor(private intervalMs: number) {} // admit at most one item per intervalMs

  async acquire(): Promise<void> {
    const now = performance.now();
    const wait = Math.max(0, this.next - now);
    this.next = Math.max(now, this.next) + this.intervalMs;
    if (wait > 0) await new Promise((r) => setTimeout(r, wait));
  }
}

Exponentiell backoff med full jitter

När en förfrågan misslyckas med en status som kan göras om, förvärrar ett omedelbart nytt försök situationen. Backoff sprider ut nya försök, och full jitter slumpmässiggör varje fördröjning över hela intervallet. Det hindrar många klienter från att försöka igen synkront och återskapa samma topp som orsakade felet.

Kodexemplet nedan refererar till RetryableError, en liten klass som innehåller felstatusen och ett eventuellt Retry-After-värde. Den definieras i avsnittet om smidig 429-hantering nedan.

async function withBackoff<T>(
  call: () => Promise<T>,
  opts: { maxAttempts?: number; baseMs?: number; capMs?: number } = {},
): Promise<T> {
  const { maxAttempts = 5, baseMs = 500, capMs = 20_000 } = opts;
  let attempt = 0;
  for (;;) {
    try {
      return await call();
    } catch (e) {
      if (!(e instanceof RetryableError) || ++attempt >= maxAttempts) throw e;
      // honor Retry-After if present; otherwise capped exponential growth with full jitter
      const delay =
        e.retryAfterMs ?? Math.random() * Math.min(capMs, baseMs * 2 ** attempt);
      await new Promise((r) => setTimeout(r, delay));
    }
  }
}

Smidig 429-hantering: Vad du gör när du når taket

En 429 innebär att du överskred kapaciteten även efter prioritetskön, så rätt respons är att sakta ned i stället för att försöka igen hårdare. Det finns fyra sätt att hantera detta. Bra hantering bygger på fyra strategier:

  • Identifiering
  • Respektera Retry-After
  • Synliggör backpressure
  • Undvik retry-stormar med en circuit breaker

Låt oss gå igenom dem mer i detalj.

Det första är identifiering. Behandla HTTP 429 (och tillfälliga 500, 502, 503 och 504) som möjliga att försöka igen, och behandla 400, 401, 403 och 422 som inte möjliga att försöka igen. Att försöka igen med en felaktig eller obehörig förfrågan lyckas aldrig och slösar bara en plats.

Det andra är att respektera Retry-After. Om svaret innehåller det svarshuvudet ska du följa det exakt i stället för att beräkna din egen fördröjning. Servern berättar när den förväntar sig att ha kapacitet, och den vet bättre än din exponentiella formel. Använd bara jitterad backoff när svarshuvudet saknas.

class RetryableError extends Error {
  constructor(public status: number, public retryAfterMs?: number) {
    super(`retryable ${status}`);
  }
}

function classify(resp: Response): void {
  if ([429, 500, 502, 503, 504].includes(resp.status)) {
    const ra = resp.headers.get("retry-after");
    throw new RetryableError(resp.status, ra ? Number(ra) * 1000 : undefined);
  }
  if (!resp.ok) throw new Error(`non-retryable ${resp.status}`);
}

Den tredje aspekten är att synliggöra backpressure. Låt inte nya försök samlas på hög osynligt. Om ditt ködjup eller din uppmätta marginal visar att du inte snart kan hantera en ny förfrågan, avvisa den vid kanten med en tydlig signal till anroparen i stället för att ta emot arbete du inte kan utföra.

Den fjärde är att undvika retry-stormar med en circuit breaker. Om antalet fel passerar ett tröskelvärde, öppna kretsen och avbryt snabbt under en nedkylningsperiod i stället för att skicka förfrågningar som du förväntar dig ska misslyckas. Efter perioden skickar du några testförfrågningar; om de lyckas stänger du kretsen.

class CircuitBreaker {
  private failures = 0;
  private openedAt: number | null = null;
  constructor(private threshold = 5, private cooldownMs = 10_000) {}

  allow(): boolean {
    if (this.openedAt === null) return true;
    if (performance.now() - this.openedAt >= this.cooldownMs) {
      this.openedAt = null; // half-open: allow a probe
      this.failures = 0;
      return true;
    }
    return false;
  }

  record(ok: boolean): void {
    if (ok) {
      this.failures = 0;
      this.openedAt = null;
    } else if (++this.failures >= this.threshold) {
      this.openedAt = performance.now();
    }
  }
}

Kvotmönster för AI-rate limiting i system med flera kunder

Allt hittills utgår från en enda applikation med en enda budget. När du bygger en SaaS ovanpå ElevenLabs förändras problemet: din samtidighetsbudget delas mellan alla dina egna kunder, och en kund som kör ett batchjobb ska inte kunna tränga undan all live-trafik för övriga kunder. Du behöver ett rättviselager mellan dina kunder och den enda uppströmsgränsen.

Grunden är tokenhinkar per kund. Ge varje kund en egen hink dimensionerad efter deras tilldelning och släpp bara igenom en förfrågan när både kundens hink och en global begränsare tillåter det.

class MultiTenantAdmission {
  private tenantBuckets = new Map<string, TokenBucket>();
  constructor(private globalMaxInFlight: number) {}

  private bucket(tenant: string): TokenBucket {
    let b = this.tenantBuckets.get(tenant);
    if (!b) {
      // Each tenant: burst of 5, sustained 2 starts/sec. Tune per tier.
      b = new TokenBucket(5, 2);
      this.tenantBuckets.set(tenant, b);
    }
    return b;
  }

  async run<R>(tenant: string, work: () => Promise<R>): Promise<R> {
    const b = this.bucket(tenant);
    if (!b.tryAcquire()) {
      throw new RetryableError(429, b.timeUntil());
    }
    // ... then admit through the global limiter (e.g. the bounded pool above)
    return work();
  }
}

Hinkar ser till att en enskild kund håller sig inom sin ram, men de avgör inte vem som vinner när kunder konkurrerar om den globala begränsaren. Använd viktad rättvis köhantering för det. 

Använd inte först till kvarn, eftersom en topp från en kund då kan monopolisera platserna. Ha en kö per kund och distribuera proportionellt efter varje kunds vikt, så att en betalande kund får en större andel av konkurrensutsatt kapacitet än en kostnadsfri kund.

Utöver rättvisa ska du reservera marginal. Låt aldrig normal trafik använda 100 % av samtidighetsgränsen. Reservera en del, exempelvis 15–20 %, som buffert för latenskänsliga interaktiva förfrågningar och prioritetskön.

När rättvisa inom en enda budget inte längre räcker, dela upp mellan arbetsytor eller nycklar. En enskild samtidighetsbudget blir till slut flaskhalsen oavsett hur rättvist du delar upp den. 

Dela då upp arbetslaster mellan separata arbetsytor eller API-nycklar med egna budgetar: till exempel en nyckel för agenttrafik i realtid och en annan för berättarröst i bakgrunden, så att en kö för berättarröst inte kan påverka agentkapaciteten.

Arbetsytor låter dig också tillämpa omfångsbegränsningar, kreditkvoter och kontroller per nyckel, som beskrivs i autentiseringsdokumentationen.

Övervaka din samtidighetsanvändning

Inget av detta går att finjustera utan mätning; du kan inte hantera en marginal som du inte mäter. Registrera current-concurrent-requests och maximum-concurrent-requests för varje svar, taggat efter modellfamilj, och skicka ut användningskvoten som ett mätvärde.

function recordHeadroom(resp: Response, metrics: Metrics): void {
  const cur = Number(resp.headers.get("current-concurrent-requests"));
  const max = Number(resp.headers.get("maximum-concurrent-requests"));
  if (Number.isFinite(cur) && Number.isFinite(max)) {
    metrics.gauge("el.concurrency.current", cur);
    metrics.gauge("el.concurrency.max", max);
    if (max > 0) metrics.gauge("el.concurrency.utilization", cur / max);
  }
}

Fyra signaler att följa:

  • Användning (nuvarande / maximalt).
  • 429-frekvens som andel av alla förfrågningar.
  • Retry-djup, antalet försök per logisk förfrågan.
  • Tid till första ljud, mätt från din applikation och inte från modellens inferenssiffror. Se förstå latens för vad TTFA omfattar.

Ett välfungerande system håller användningen bekvämt under mättnad och ser 429-fel bara vid enstaka toppar. Genom att övervaka dessa signaler får du insyn i trycket från rate limiting långt innan det leder till driftstörningar.

När du ska skala bortom rate limiting på klientsidan

Mönster på klientsidan kan göra mycket, men den kontinuerliga efterfrågan kommer till slut att växa ur dem. När det händer är det dags att göra ändringar som hjälper både kostnad och arbetsinsats. 

Vart och ett av följande steg ger dig extra kapacitet.

Börja med att byta från HTTP till WebSockets för interaktiv trafik. Om dina agenter eller användningsfall i realtid körs över HTTP ändrar en flytt till WebSocket beräkningen så att bara aktiv generering räknas. För konversationsbaserade arbetslaster mångdubblar detta ofta den effektiva kapaciteten utan att du byter plan, eftersom inaktiv samtalstid slutar använda platser.

Om dina toppar är kraftiga men din genomsnittliga belastning ryms inom budgeten, jämnar en token- eller läckande hink tillsammans med en begränsad pool ut topparna till genomsnittet.

Välj sedan rätt modell. Snabbare generering håller varje plats kortare tid, vilket ökar antalet sändningar som en fast samtidighetsgräns kan upprätthålla. Eleven Flash v2.5 är alternativet med lägst latens för realtidsarbete; tillsammans med en Instant Voice Clone eller en standardröst undviker den omkostnad per generering som Professional Voice Clones medför.

Först därefter bör du uppgradera planen. När din kontinuerliga efterfrågan faktiskt överskrider budgeten efter att klienten fungerar väl, höjer en större plan både samtidighetsgränsen per modell och din köprioritet. Jämför nivåer på API-prissidan.

Om du behöver högre gränser än de som publiceras erbjuder Enterprise-planer höjda och anpassade samtidighetsgränser samt högsta köprioritet. Ytterligare kontroller finns för kvalificerade användningsfall, som IP-vitlistning (i Enterprise-förhandsversion) och lägen utan datalagring. Kontakta din kundansvariga för att höja gränserna.

Sammanfattning: vad du bör tänka på kring AI-rate limiting 

Det grundläggande misstaget är att behandla AI-rate limiting för röst som att räkna förfrågningar. Allt här handlar om samtidighetskontroll. Siffran som avgör om du lyckas är hur många förfrågningar som genererar ljud i samma ögonblick och hur länge var och en upptar sin plats.

Bygg klienten utifrån det faktumet. 

Begränsa pågående förfrågningar med en begränsad pool, styr insläppet med en token- eller läckande hink, försök igen med begränsad exponentiell backoff och full jitter, respektera Retry-After och bryt kretsen innan en retry-storm uppstår. 

För system med flera kunder lägger du till hinkar per kund, viktad rättvisa, reserverad marginal och uppdelning för isolering. Följ svarshuvudena current-concurrent-requests och maximum-concurrent-requests och larma för användningstrenden, inte för felen. 

När du verkligen behöver mer kapacitet, gå igenom listan i ordning: först WebSockets och bättre klientbeteende, sedan rätt modell, därefter en planuppgradering och slutligen Enterprise-gränser.

Bygg röstapplikationer med ElevenAPI

AI-rate limiting för produktionsmiljöer börjar med rätt transportprotokoll, rätt modell och svarshuvuden som visar exakt var du står.

ElevenAPI erbjuder modeller med låg latens som Eleven Flash v2.5, WebSocket-streaming i realtid, Speech to Text och Text to Speech-API:er, samt samtidighetssvarshuvuden per svar som gör att du kan bygga röstagenter som skalar inom dina gränser. 

Tillsammans med strategierna för AI-rate limiting i den här artikeln kan du skapa responsiva röstupplevelser med förutsägbar prestanda, även under belastning. 

Utforska ElevenAPI för att se hela modellutbudet i praktiken, eller skapa ett konto för att börja bygga med ElevenLabs i dag.

Vanliga frågor om AI-rate limiting

Liknande artiklar

Skapa med AI-ljud av högsta kvalitet