Réduire la latence des LLM est l’un des défis les plus critiques pour les ingénieurs qui construisent des applications IA réactives. Alors que les grands modèles de langage (LLM) gagnent sans cesse en capacité, leur génération token par token peut créer des goulots d’étranglement frustrants pour les utilisateurs finaux, et les temps d’attente prolongés entraînent directement une baisse de l’engagement et des abandons d’application. Optimiser vos pipelines d’inférence pour la vitesse est donc une exigence fondamentale pour les développeurs. Ce guide explique comment configurer le prompt caching, mettre en place le streaming des réponses, structurer le routage réseau edge et utiliser des configurations serverless pour réduire les délais de traitement.
[!TIP] Conseil sur les métriques de performance : Lorsque vous mesurez les délais des API, isolez le Time to First Token (TTFT) de la vitesse de génération globale. Un TTFT faible donne à l’application une impression d’instantanéité pour l’utilisateur, même si la génération complète de la sortie prend plusieurs secondes, car le texte commence à s’afficher immédiatement.
Points clés à retenir :
- Prompt caching : Réutilisez les préfixes statiques pour contourner les phases d’analyse et réduire le TTFT de 80 %.
- Streaming des réponses : Envoyez les tokens via Server-Sent Events (SSE) afin que les utilisateurs voient une génération de texte instantanée.
- Edge Workers : Exécutez l’autorisation et le routage des requêtes dans des centres edge régionaux proches des utilisateurs.
- Routage des modèles : Redirigez les requêtes simples vers des modèles légers afin d’optimiser les vitesses.
Les composantes de la latence des API LLM
Pour réduire les temps de réponse, vous devez d’abord comprendre quels éléments déterminent le délai global de l’API. La latence globale est la somme cumulée de trois variables distinctes.
Premièrement, le temps de transit réseau mesure la durée nécessaire à une requête pour voyager du client vers votre serveur, puis vers l’API du fournisseur du modèle. Cela fait de la distance de transit un goulot d’étranglement majeur.
Deuxièmement, le Time to First Token (TTFT) représente la durée entre la réception de la requête par le modèle et la génération de son premier token de sortie, ce qui explique pourquoi le prompt caching est si important.
Enfin, la vitesse de génération des tokens mesure le rythme auquel le matériel produit les tokens suivants. Les contraintes matérielles dictent la vitesse de génération, mais les développeurs conservent un contrôle total sur le temps de transit et le TTFT, si bien qu’un routage et un caching intelligents peuvent réduire considérablement la latence des LLM.
Accélérez votre intégration LLMPrérequis
Avant de commencer à mettre en place les optimisations ci-dessous, assurez-vous de disposer des éléments suivants. Aucun n’est exotique, mais en omettre un provoque souvent des échecs déroutants par la suite.
- Un middleware ou un runtime edge que vous contrôlez. Les exemples utilisent Cloudflare Workers, mais toute plateforme serverless capable de relayer une requête convient. Vous avez besoin d’un point situé entre le navigateur et le fournisseur du modèle.
- Des identifiants d’API pour un fournisseur qui prend en charge le streaming et le caching. OpenAI et Anthropic le font tous les deux. Stockez la clé sous forme de secret (un secret Wrangler ou une variable d’environnement), jamais dans le code côté client.
- Node.js 18 ou version ultérieure si vous souhaitez tester les Workers localement avec
wrangler dev. Les API globalesfetchetReadableStreamutilisées tout au long de ce guide sont disponibles dans ce runtime et dans les navigateurs modernes. - Une mesure de référence. Relevez votre Time to First Token actuel et le temps de réponse total avant toute modification, afin de prouver que chaque optimisation a réellement aidé. La section de benchmarking ci-dessous explique comment procéder.
- Une familiarité avec les Server-Sent Events (SSE). Les réponses en streaming arrivent sous la forme d’une séquence de lignes
data:, que vous analyserez côté client.
Mettre en place le prompt caching
Le prompt caching est le moyen le plus efficace d’optimiser le TTFT pour les applications construites autour de grands prompts système. Lorsqu’une requête contient un long bloc d’instructions statiques (comme le prompt système d’un agent ou un document de référence RAG), le fournisseur du modèle doit analyser et encoder ces tokens à chaque exécution. Anthropic et OpenAI prennent tous deux en charge le prompt caching, qui conserve les états de tokens analysés en mémoire. Les requêtes suivantes partageant le même préfixe contournent alors la phase d’analyse, réduisant le TTFT jusqu’à 80 %.
La durée de vie du cache varie selon les fournisseurs. Anthropic maintient le cache pendant environ cinq minutes d’inactivité, tandis qu’OpenAI utilise un modèle de dégradation dynamique. Planifier des pings de récupération réguliers en arrière-plan permet donc de garder les instructions système critiques actives dans la mémoire du serveur.
Le mécanisme diffère légèrement entre les deux fournisseurs, et c’est le fait de structurer correctement la requête qui détermine si le cache s’engage réellement. Avec Anthropic, vous marquez explicitement un point de rupture de cache à l’aide de cache_control. Tout ce qui précède le point de rupture est stocké, si bien que le contenu stable et statique doit venir en premier et le contenu volatil propre à chaque requête doit venir en dernier :
1import Anthropic from "@anthropic-ai/sdk";
2
3const anthropic = new Anthropic();
4
5const response = await anthropic.messages.create({
6 model: "claude-opus-4-8",
7 max_tokens: 1024,
8 system: [
9 {
10 type: "text",
11 text: SYSTEM_INSTRUCTIONS // small, sent on every request
12 },
13 {
14 type: "text",
15 text: KNOWLEDGE_BASE, // large, static reference block
16 cache_control: { type: "ephemeral" }
17 }
18 ],
19 messages: [
20 { role: "user", content: userQuestion } // volatile — after the breakpoint
21 ]
22});
23
24// Confirm the cache is working
25console.log(response.usage.cache_read_input_tokens);
L’erreur la plus fréquente consiste à placer un horodatage, un identifiant de requête ou toute chaîne propre à la requête devant le bloc mis en cache. Comme le caching repose sur une correspondance de préfixe, un seul octet modifié n’importe où avant le point de rupture invalide tout ce qui le suit, et le cache ne se déclenche jamais silencieusement. Vérifiez son bon fonctionnement en lisant usage.cache_read_input_tokens dans la réponse : si cette valeur reste à zéro pour des requêtes identiques, un élément dynamique s’est glissé dans le préfixe. Notez également que le préfixe mis en cache doit dépasser une longueur minimale (de l’ordre de 1 024 à 4 096 tokens selon le modèle) avant que le caching ne s’active.
OpenAI adopte une approche plus simple : le caching est automatique pour les prompts dépassant environ 1 024 tokens, sans aucun flag cache_control à définir. La même discipline s’applique néanmoins. Gardez le bloc d’instructions statiques tout au début de votre tableau de messages et ajoutez l’entrée utilisateur changeante à la fin, afin que le préfixe réutilisable reste identique octet par octet entre les requêtes.
Pour consulter les structures tarifaires et de paramètres du prompt caching, référez-vous au guide Anthropic sur le prompt caching .
Edge compute et routage serverless
Traiter les requêtes LLM sur un unique serveur centralisé introduit d’énormes sauts réseau pour les utilisateurs du monde entier. Déployer votre middleware d’API sur des réseaux edge serverless (comme Cloudflare Workers) raccourcit considérablement ces trajets.
L’edge worker reçoit la requête du client, autorise la session et l’achemine vers le centre de données du fournisseur de modèle le plus proche. Cette structure serverless délivre les tokens à l’écran de l’utilisateur au moment même où ils sont calculés, si bien que l’interface paraît très réactive. Le middleware JavaScript ci-dessous montre comment configurer des réponses en streaming directement depuis un runtime edge :
1export default {
2 async fetch(request, env) {
3 const payload = await request.json();
4
5 // Call the streaming LLM endpoint
6 const response = await fetch("https://api.openai.com/v1/chat/completions", {
7 method: "POST",
8 headers: {
9 "Authorization": `Bearer ${env.OPENAI_API_KEY}`,
10 "Content-Type": "application/json"
11 },
12 body: JSON.stringify({
13 model: "gpt-4o-mini",
14 messages: payload.messages,
15 stream: true
16 })
17 });
18
19 // Forward the stream directly to the client browser
20 return new Response(response.body, {
21 headers: { "Content-Type": "text/event-stream" }
22 });
23 }
24};
Cette structure serverless délivre les tokens à l’écran de l’utilisateur instantanément, au fur et à mesure qu’ils sont calculés. Pour apprendre à construire des backends optimisés pour l’edge, lisez notre guide sur la création d’une API serverless avec Cloudflare Workers .
Analyser le flux côté client
Relayer le flux depuis l’edge n’est que la moitié du travail. Le navigateur doit encore lire ces chunks à mesure qu’ils arrivent et afficher chaque token, sinon la réponse s’accumule dans un tampon et apparaît d’un seul coup, ce qui annule tout l’intérêt. C’est l’étape que la plupart des tutoriels négligent, et c’est là que la performance perçue se gagne ou se perd réellement.
Le corps de la réponse est un ReadableStream d’octets bruts. Les trames SSE arrivent sous forme de lignes data:, mais un seul chunk réseau peut contenir plusieurs trames, ou répartir une trame sur deux chunks ; vous devez donc mettre en tampon les lignes partielles plutôt que de supposer que chaque chunk est un message complet :
1async function streamCompletion(messages, onToken) {
2 const response = await fetch("/api/chat", {
3 method: "POST",
4 headers: { "Content-Type": "application/json" },
5 body: JSON.stringify({ messages })
6 });
7
8 const reader = response.body.getReader();
9 const decoder = new TextDecoder();
10 let buffer = "";
11
12 while (true) {
13 const { value, done } = await reader.read();
14 if (done) break;
15
16 buffer += decoder.decode(value, { stream: true });
17 const lines = buffer.split("\n");
18 buffer = lines.pop(); // keep the trailing partial line
19
20 for (const line of lines) {
21 if (!line.startsWith("data: ")) continue;
22 const payload = line.slice(6).trim();
23 if (payload === "[DONE]") return;
24
25 try {
26 const json = JSON.parse(payload);
27 const token = json.choices?.[0]?.delta?.content;
28 if (token) onToken(token);
29 } catch {
30 // ignore keep-alive comments and malformed partial frames
31 }
32 }
33 }
34}
Le buffer.split("\n") suivi de lines.pop() est le détail important : il conserve toute ligne incomplète jusqu’à ce que le chunk suivant la complète. Envelopper JSON.parse dans un try/catch maintient la boucle en vie lorsqu’un commentaire keep-alive ou une trame reçue à moitié arrive. Le callback onToken ajoute ensuite chaque fragment au DOM, de sorte que le texte apparaît à l’instant même où le modèle le produit.
Mesurer et évaluer la latence
Vous ne pouvez pas optimiser ce que vous n’avez pas mesuré. Avant et après chaque modification, relevez le Time to First Token et le temps de génération total afin d’attribuer une amélioration à la bonne cause. Le moyen le plus rapide d’échantillonner le TTFT est d’utiliser curl, en prenant time_starttransfer comme approximation fidèle de l’arrivée du premier octet au client :
1curl -w "TTFT: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
2 -X POST https://your-worker.example.com/chat \
3 -H "Content-Type: application/json" \
4 -d '{"messages":[{"role":"user","content":"Hello"}]}' \
5 -o /dev/null -s
Pour des chiffres au niveau de l’application, instrumentez directement le parseur côté client. Marquez l’horloge au départ de la requête, puis de nouveau à l’arrivée du premier token :
1const start = performance.now();
2let firstTokenAt = null;
3
4await streamCompletion(messages, (token) => {
5 if (firstTokenAt === null) firstTokenAt = performance.now();
6 render(token);
7});
8
9console.log(`TTFT: ${Math.round(firstTokenAt - start)}ms`);
Effectuez chaque mesure plusieurs fois et prenez la médiane plutôt qu’un échantillon unique, car la variance réseau et les démarrages à froid peuvent fausser des relevés ponctuels. Le tableau ci-dessous donne des plages indicatives montrant où le temps se répartit typiquement sur une requête globale bien maîtrisée ; considérez-les comme une forme de comparaison, et non comme des valeurs fixes, car vos propres chiffres dépendront de la région, du modèle et de la taille du prompt.
| Étape | Contribution typique | Sous votre contrôle ? |
|---|---|---|
| Transit réseau (client vers edge) | 10–60 ms | Oui — le routage edge le raccourcit |
| Traitement du middleware à l’edge | 1–15 ms | Oui — gardez le worker léger |
| Time to First Token (prompt à froid) | 400–1 200 ms | En partie — le caching le réduit fortement |
| Time to First Token (préfixe en cache) | 100–400 ms | Oui — via le prompt caching |
| Génération par token | 10–50 ms/token | Non — fixé par le modèle et le matériel |
Les deux lignes qui méritent une attention particulière sont les chiffres du TTFT avec et sans cache. Cet écart représente le plus grand gain accessible à la plupart des applications, ce qui explique pourquoi le prompt caching figure en tête de la liste des optimisations. La vitesse de génération, en revanche, est fixée par le fournisseur, si bien que rediriger les requêtes plus simples vers un modèle plus petit est le seul levier disponible.
Flux d’optimisation étape par étape
Pour optimiser la vitesse de votre application logicielle, commencez par séparer les instructions système statiques des entrées utilisateur dynamiques. Cette division vous permet de cibler proprement les points d’entrée du cache.
Ensuite, activez les flags de prompt caching dans vos payloads d’API pour garantir que le fournisseur du modèle conserve vos tokens de texte en mémoire.
Configurez toujours le streaming des réponses à l’aide de points de terminaison SSE standard. En écrivant des parseurs frontend légers qui traitent les chunks à mesure qu’ils arrivent, vous améliorez la performance perçue par l’utilisateur. Établissez ensuite des chemins de repli entre modèles : acheminez les entrées client basiques vers des modèles plus petits, en réservant les modèles de raisonnement plus volumineux aux tâches avancées. Enfin, profilez les sauts réseau pour confirmer que les workers serverless réduisent les délais de transit. Pour en savoir plus sur l’optimisation des bases de données, consultez notre guide sur le WordPress face au développement web sur mesure .
Pièges courants et dépannage
Une poignée de défaillances reviennent sans cesse lorsque les équipes déploient pour la première fois le streaming et le caching. Reconnaître le symptôme épargne des heures de tâtonnement.
| Symptôme | Cause probable | Solution |
|---|---|---|
cache_read_input_tokens reste à zéro | Un horodatage, un UUID ou un ID de session se trouve devant le point de rupture du cache, de sorte que le préfixe change à chaque requête | Déplacez tout le contenu dynamique après le bloc statique ; sérialisez tout JSON de manière déterministe |
| Les tokens arrivent d’un seul coup, pas de façon incrémentale | Un proxy intermédiaire ou un CDN met la réponse en tampon | Envoyez Cache-Control: no-transform et X-Accel-Buffering: no ; assurez-vous que le content type text/event-stream est défini |
| Le flux s’interrompt à mi-chemin | Le worker a renvoyé sa réponse avant la fin du corps en amont, ou max_tokens a été atteint | Renvoyez directement response.body plutôt que d’attendre le texte complet ; augmentez max_tokens |
| Le premier token est lent malgré le caching | Le préfixe statique est en dessous de la longueur minimale cachable du fournisseur | Regroupez les instructions afin que le bloc mis en cache dépasse le seuil d’environ 1 024 tokens |
| Le parseur client échoue sur certains chunks | Une trame a été répartie sur deux chunks réseau | Mettez en tampon les lignes partielles comme montré ci-dessus et enveloppez JSON.parse dans un try/catch |
Un piège plus subtil est la mise en tampon à l’edge lui-même. Si vous appelez await response.text() à l’intérieur du worker avant de renvoyer la réponse, vous avez discrètement retransformé une réponse en streaming en une réponse bloquante. Transmettez toujours le corps du flux directement. De même, surveillez les limites CPU du worker : un travail lourd par requête dans le middleware s’ajoute directement au TTFT, gardez donc la logique d’autorisation et de routage minimale et différez tout ce qui est coûteux.
Considérations de production
Faire fonctionner une démo en streaming dans un navigateur est simple ; l’exécuter de façon fiable sous un trafic réel exige quelques garde-fous supplémentaires.
Définissez un timeout de requête raisonnable sur l’appel en amont afin qu’une connexion bloquée du fournisseur ne puisse pas maintenir un worker ouvert indéfiniment, et associez-le à une nouvelle tentative qui bascule vers un second fournisseur ou un modèle plus petit lorsque le principal expire. Comme le prompt caching facture une légère majoration sur les écritures de cache et une forte réduction sur les lectures, il n’est rentable que lorsqu’un préfixe est réutilisé ; un ping en arrière-plan toutes les quelques minutes maintient un prompt système actif résident sans payer pour le réécrire à chaque requête utilisateur.
Instrumentez en continu plutôt qu’au seul lancement. Journalisez le TTFT et le nombre de tokens par seconde par requête et déclenchez une alerte lorsque la médiane dérive, car une régression côté fournisseur ou un changement de taille de prompt s’y manifestera en premier. Enfin, respectez les limites de débit du fournisseur : une rafale de flux concurrents peut les faire dépasser, mettez donc la charge en file d’attente ou délestez-la avec élégance au lieu de laisser les requêtes échouer silencieusement. Ces mesures transforment un prototype rapide en une application qui reste rapide quand cela compte.
Points clés à retenir
- Ciblez le Time to First Token (TTFT) et le temps de transit pour réduire la latence des LLM.
- Exploitez le prompt caching sur les API des modèles pour contourner la surcharge systémique d’analyse des instructions.
- Utilisez le streaming des réponses pour délivrer les tokens en temps réel et améliorer la vitesse perçue.
- Déployez le middleware d’API sur des runtimes edge serverless afin de raccourcir les trajets réseau mondiaux.
- Redirigez les requêtes utilisateur plus simples vers des modèles légers pour optimiser les vitesses d’exécution.
- Mettez en place des outils de surveillance de performance pour analyser et réduire la latence en continu, dans des conditions réelles d’utilisation.
Foire aux questions (FAQ)
Comment réduire la latence des LLM en production ? Pour réduire la latence des LLM en production, vous devez mettre en place le prompt caching pour les instructions statiques, activer le streaming des tokens et déployer des edge workers afin d’optimiser le routage des requêtes. En déployant des runtimes d’orchestration serverless plus près des clients du monde entier, les développeurs contournent de multiples sauts de routage réseau et délivrent le premier token de réponse en temps réel.
Qu’est-ce que le prompt caching ? Le prompt caching est une fonctionnalité d’API qui stocke les états de texte analysés dans la mémoire du serveur, permettant aux requêtes suivantes utilisant le même préfixe de s’exécuter beaucoup plus vite. En contournant le cycle d’analyse systémique des grands jeux d’instructions, cette optimisation réduit le Time to First Token (TTFT) jusqu’à quatre-vingts pour cent.
La taille du modèle affecte-t-elle la latence ? Oui, les modèles plus petits ont des vitesses de génération de tokens bien plus rapides, ce qui les rend idéaux pour les tâches simples où la latence est une préoccupation majeure. Rediriger les requêtes simples de classification ou d’extraction vers des modèles edge spécialisés garantit des délais de traitement rapides tout en réservant les modèles denses aux tâches de raisonnement.
Comment le streaming Server-Sent Events (SSE) aide-t-il à réduire la latence perçue ? Le streaming SSE envoie les tokens de sortie textuelle depuis l’hôte du modèle vers l’écran du client en temps réel, à mesure qu’ils sont compilés. Bien que cela ne réduise pas la durée totale d’exécution, cela minimise le Time to First Token (TTFT) et offre à l’utilisateur une interface d’application réactive et active.
Comment mettre en cache les réponses LLM dynamiques à l’edge ? Vous pouvez mettre en cache les réponses dynamiques à l’edge à l’aide de bases de données KV ou d’instances Redis avec de courtes limites de TTL (Time to Live). La mise en cache des réponses dynamiques est efficace pour les requêtes utilisateur répétitives ou les intentions courantes du service client, évitant totalement les appels réseau vers le fournisseur du modèle.
Commentaires