Jour 55 Day 55 · mercredi 28 octobre 2026 Wednesday 28 October 2026 IA Avancé
LLM en production : coûts, latence, evals & limites LLMs in production: cost, latency, evals & limits
Facturer au token change tout : cache, fallback, evals, observabilité — ce qui sépare une démo LLM d'un produit, et ce que les équipes IA attendent d'un stagiaire en entretien. Paying per token changes everything: caching, fallback, evals, observability — what separates an LLM demo from a product, and what AI teams expect from an intern in an interview.
L’essentiel
Une démo LLM se monte en une après-midi : un prompt, un appel d’API, une interface. La production révèle quatre murs que la démo cachait : le coût (chaque appel se facture au token), la latence (plusieurs secondes par réponse), la fiabilité (le modèle renvoie du texte libre, pas un contrat d’API) et l’évaluation (comment savoir que la qualité ne se dégrade pas ?). En entretien, ce sujet sépare ceux qui ont fait des notebooks de ceux qui ont fait tourner un produit.
La facture d’abord. Un appel se paie en tokens d’entrée (prompt système, contexte, historique) et en tokens de sortie (la réponse), la sortie coûtant en général 3 à 5 fois plus cher. Le coût caché est ailleurs : l’API est sans état, donc un chat renvoie tout l’historique à chaque tour. Au tour 50, vous re-payez les 49 tours précédents en entrée — le coût cumulé d’une conversation croît quasi quadratiquement avec sa longueur.
| Levier | Effet coût | Effet latence | Contrepartie |
|---|---|---|---|
| Cache de prompts | jusqu’à −90 % sur le préfixe répété | TTFT réduit (prefill évité) | préfixe identique octet pour octet |
| Modèle plus petit | −80 à −95 % | tokens/s plus rapides | qualité à prouver par des evals |
| Batch API | −50 % typique | résultats en minutes/heures | réservé à l’asynchrone |
max_tokens + prompts concis | proportionnel | réponses plus courtes | peut tronquer une sortie utile |
| Streaming | nul | latence perçue ÷ 10 | complexité UI (SSE, parsing partiel) |
| Tronquer/résumer l’historique | casse la croissance quadratique | entrée plus courte | perte de mémoire conversationnelle |
Comment ça marche
La latence se mesure avec deux métriques distinctes : le time-to-first-token (TTFT, le délai avant le premier mot) et le débit (tokens/seconde ensuite). Le streaming ne change rien à la durée totale mais transforme l’expérience : l’utilisateur lit pendant que le modèle écrit, la latence perçue tombe au TTFT. Un chatbot sans streaming paraît cassé au-delà de deux secondes ; avec, dix secondes de génération passent inaperçues.
La fiabilité repose sur un principe : la sortie d’un LLM est une entrée non fiable. Un modèle peut renvoyer du JSON invalide, un champ manquant, un format inattendu — même à température 0. Le pipeline robuste combine sortie structurée (tool calling ou mode JSON côté API), validation stricte par schéma (zod, pydantic), retry avec backoff, et fallback vers un second modèle si le principal échoue ou dépasse le timeout.
Requête ──▶ garde-fous d'entrée (PII, injection)
│
▼
Modèle principal ──erreur/timeout──▶ retry (backoff)
│ │ échec
▼ ▼
Validation schéma ──invalide──▶ modèle de fallback
│ ok │
▼ ▼
Réponse (stream) validation, puis réponse
│
▼
Traces : latence, tokens, coût par feature
Le même contrat en code :
import { z } from "zod";
// Le schéma EST le contrat d'API : tout ce qui sort du
// modèle est validé avant d'entrer dans le système.
const Facture = z.object({
fournisseur: z.string().min(1),
total_centimes: z.number().int().nonnegative(),
devise: z.enum(["EUR", "USD"]),
echeance: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
});
const MODELES = ["grand-modele", "petit-modele-fallback"];
async function extraireFacture(texte: string, essai = 0) {
const res = await llm.complete({
model: MODELES[Math.min(essai, 1)], // fallback au 2e essai
max_tokens: 300, // borne dure sur le coût de sortie
prompt: promptExtraction(texte),
});
const parsed = Facture.safeParse(JSON.parse(res.text));
if (!parsed.success) {
if (essai < 2) return extraireFacture(texte, essai + 1);
// → file de revue humaine, jamais de données corrompues
throw new Error("Extraction invalide après 3 essais");
}
return parsed.data; // typé, garanti conforme au schéma
}
🎤 En entretien — « Comment mettrais-tu un LLM en prod sans exploser le budget ? » Réponse qui marque : « Je mesure d’abord — traces avec tokens et coût par appel. Ensuite, dans l’ordre : cache de prompts sur le préfixe stable, modèle plus petit partout où les evals prouvent qu’il suffit, batch pour tout l’asynchrone, plafond
max_tokens. Le tout piloté par un dashboard de coût par feature, pas au feeling. »
Concepts clés à maîtriser
- Cache de prompts : le fournisseur facture ~10 % du prix normal pour un préfixe déjà vu. Condition : préfixe identique octet pour octet. D’où la règle d’architecture : instructions système et documents stables en tête du prompt, contenu variable en queue.
- Cascade de modèles : router les requêtes simples vers un petit modèle, réserver le gros aux cas difficiles. La décision se fonde sur des evals, jamais sur l’intuition.
- Evals automatisées : un jeu de test métier (50 à 200 cas réels annotés) rejoué à chaque changement de prompt ou de modèle, comme des tests de non-régression. Sans evals, modifier un prompt en prod revient à déployer sans tests.
- LLM-as-judge : faire noter les sorties par un autre LLM. Ça passe à l’échelle, avec des biais documentés : préférence pour les réponses longues (verbosity bias), pour la première position dans une comparaison (position bias), pour ses propres sorties (self-preference). À calibrer contre un échantillon annoté par des humains.
- Observabilité LLM : chaque appel tracé — prompt, réponse, latence, tokens, coût, version du prompt — et agrégé par feature. « Le résumé automatique coûte 40 % de la facture pour 5 % de l’usage » est une phrase qui déclenche des décisions.
- Hallucinations : le modèle prédit le texte le plus plausible, pas le plus vrai. C’est structurel — ça se mitige (RAG pour ancrer les réponses sur des sources, citations vérifiables, humain dans la boucle sur les actions critiques), ça ne se « corrige » pas.
- Sécurité : la prompt injection — des instructions hostiles cachées dans le contenu traité (« ignore tes instructions et… ») — n’a pas de correctif garanti, car le modèle ne sépare pas instructions et données. Défense en profondeur : moindre privilège pour les outils, validation des sorties, confirmation humaine des actions sensibles.
⚠️ Données sensibles — chaque prompt part sur les serveurs du fournisseur. Avant d’y envoyer des données clients : contrat de traitement (DPA), politique de rétention, opt-out d’entraînement, anonymisation quand c’est possible. « On enverra ça à l’API » est une décision juridique autant que technique.
En entretien
« Comment réduire la latence d’un chatbot LLM ? » — Distinguer latence réelle et perçue. Perçue : streaming — la réponse commence au TTFT, quelques centaines de millisecondes. Réelle : modèle plus petit, prompt plus court, cache de prompts (le préfixe en cache accélère le prefill), limiter la longueur de sortie. Bonus : pré-calculer hors ligne tout ce qui peut l’être.
« Comment garantir du JSON valide en sortie ? » — On ne « garantit » rien par le prompt seul : mode JSON ou tool calling côté API, puis validation systématique par schéma (zod/pydantic) côté client, retry en cas d’échec, fallback ensuite. La sortie du LLM se traite comme une entrée utilisateur non fiable.
« Comment évalues-tu la qualité d’une feature LLM ? » — Jeu de test métier construit sur des cas réels, rejoué en CI à chaque changement de prompt ou de modèle. Métriques automatisables quand la tâche le permet (exactitude d’extraction, respect du format) ; LLM-as-judge calibré sur un échantillon humain pour le reste ; en prod, échantillonnage des réponses et feedback utilisateur.
« Peut-on éliminer les hallucinations ? » — Non : elles sont structurelles, le modèle optimise la plausibilité, pas la vérité. On réduit leur fréquence et leur impact : RAG avec citations, domaine de réponse contraint, autoriser « je ne sais pas », humain dans la boucle pour les actions à conséquences. Un candidat qui promet « zéro hallucination » se disqualifie.
« C’est quoi la prompt injection ? » — L’équivalent LLM de l’injection SQL, sans l’équivalent des requêtes préparées : le modèle ne distingue pas instructions et données dans son contexte, donc un document ou une page web traités peuvent contenir des ordres hostiles. Mitigations : moindre privilège pour les outils de l’agent, validation des sorties, confirmation humaine des actions sensibles.
Pièges & idées reçues
- « On a 200k tokens de contexte, mettons tout dedans » — le contexte long se paie à chaque appel, allonge le prefill, et les modèles retrouvent mal l’information noyée au milieu (« lost in the middle »). Un RAG sélectif reste souvent plus précis et moins cher.
- « Température 0 = déterminisme » — non garanti : le calcul flottant et le batching côté serveur introduisent des variations. La validation reste obligatoire même à température 0.
- « Le petit modèle est forcément moins bon » — sur une tâche cadrée (classification, extraction), un petit modèle bien prompté fait souvent jeu égal. Seules les evals tranchent, dans les deux sens.
- Retry naïf — re-tenter sans backoff aggrave un rate limit ; re-tenter une action non idempotente (envoi d’email) la duplique. Backoff exponentiel + idempotence, comme pour n’importe quelle API.
- LLM-as-judge pris pour argent comptant — sans calibration humaine, vous optimisez les biais du juge (longueur, position), pas la qualité réelle.
💡 Réflexe budget — le coût d’une feature LLM se calcule avant de la coder : (tokens d’entrée moyens × prix d’entrée + tokens de sortie moyens × prix de sortie) × appels par jour. Trois minutes de calcul évitent la surprise en fin de mois.
Pour aller plus loin
- OWASP Top 10 for LLM Applications — la prompt injection en tête de liste
- Judging LLM-as-a-Judge (Zheng et al., 2023) — le papier de référence sur les biais du juge
- Prompt caching — doc Anthropic et Batch API — doc OpenAI
- Langfuse et les conventions OpenTelemetry GenAI pour tracer coûts et latences par feature
The essentials
An LLM demo takes an afternoon: a prompt, an API call, a UI. Production reveals four walls the demo was hiding: cost (every call is billed per token), latency (several seconds per answer), reliability (the model returns free text, not an API contract) and evaluation (how do you know quality isn’t degrading?). In an interview, this topic separates people who ran notebooks from people who ran a product.
The bill first. A call is paid in input tokens (system prompt, context, history) and output tokens (the answer), with output usually costing 3 to 5 times more. The hidden cost lies elsewhere: the API is stateless, so a chat resends the whole history on every turn. By turn 50, you are re-paying the previous 49 turns as input — the cumulative cost of a conversation grows almost quadratically with its length.
| Lever | Cost effect | Latency effect | Trade-off |
|---|---|---|---|
| Prompt caching | up to −90% on the repeated prefix | lower TTFT (prefill skipped) | prefix must be byte-identical |
| Smaller model | −80 to −95% | faster tokens/s | quality must be proven by evals |
| Batch API | typically −50% | results in minutes/hours | async workloads only |
max_tokens + concise prompts | proportional | shorter answers | may truncate useful output |
| Streaming | none | perceived latency ÷ 10 | UI complexity (SSE, partial parsing) |
| Truncate/summarize history | breaks the quadratic growth | shorter input | loss of conversational memory |
How it works
Latency is measured with two distinct metrics: time-to-first-token (TTFT, the delay before the first word) and throughput (tokens per second afterwards). Streaming doesn’t change total duration but transforms the experience: the user reads while the model writes, and perceived latency drops to the TTFT. A chatbot without streaming feels broken past two seconds; with it, ten seconds of generation go unnoticed.
Reliability rests on one principle: LLM output is untrusted input. A model can return invalid JSON, a missing field, an unexpected format — even at temperature 0. The robust pipeline combines structured output (tool calling or JSON mode on the API side), strict schema validation (zod, pydantic), retry with backoff, and fallback to a second model when the primary fails or times out.
Request ──▶ input guardrails (PII, injection)
│
▼
Primary model ──error/timeout──▶ retry (backoff)
│ │ failure
▼ ▼
Schema validation ──invalid──▶ fallback model
│ ok │
▼ ▼
Response (stream) validation, then response
│
▼
Traces: latency, tokens, cost per feature
The same contract in code:
import { z } from "zod";
// The schema IS the API contract: everything the model
// outputs is validated before it enters the system.
const Invoice = z.object({
vendor: z.string().min(1),
total_cents: z.number().int().nonnegative(),
currency: z.enum(["EUR", "USD"]),
due_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
});
const MODELS = ["big-model", "small-fallback-model"];
async function extractInvoice(text: string, attempt = 0) {
const res = await llm.complete({
model: MODELS[Math.min(attempt, 1)], // fallback on retry
max_tokens: 300, // hard cap on output cost
prompt: extractionPrompt(text),
});
const parsed = Invoice.safeParse(JSON.parse(res.text));
if (!parsed.success) {
if (attempt < 2) return extractInvoice(text, attempt + 1);
// → human review queue, never corrupted data
throw new Error("Invalid extraction after 3 attempts");
}
return parsed.data; // typed, guaranteed to match the schema
}
🎤 In an interview — “How would you put an LLM in production without blowing the budget?” An answer that lands: “I measure first — traces with tokens and cost per call. Then, in order: prompt caching on the stable prefix, a smaller model wherever evals prove it’s enough, batch for everything async, a
max_tokenscap. All driven by a cost-per-feature dashboard, not gut feeling.”
Key concepts to master
- Prompt caching: the provider bills ~10% of the normal price for a prefix it has already seen. Condition: the prefix must be byte-identical. Hence the architecture rule: system instructions and stable documents at the head of the prompt, variable content at the tail.
- Model cascade: route simple requests to a small model, keep the big one for hard cases. The decision is based on evals, never on intuition.
- Automated evals: a business test set (50 to 200 annotated real cases) replayed on every prompt or model change, like regression tests. Without evals, changing a prompt in production is deploying without tests.
- LLM-as-judge: having another LLM grade the outputs. It scales, with documented biases: preference for long answers (verbosity bias), for the first position in a comparison (position bias), for its own outputs (self-preference). Calibrate against a human-annotated sample.
- LLM observability: every call traced — prompt, response, latency, tokens, cost, prompt version — and aggregated per feature. “Auto-summary costs 40% of the bill for 5% of usage” is a sentence that triggers decisions.
- Hallucinations: the model predicts the most plausible text, not the most true. It’s structural — it can be mitigated (RAG to ground answers in sources, verifiable citations, human in the loop on critical actions), it cannot be “fixed”.
- Security: prompt injection — hostile instructions hidden in processed content (“ignore your instructions and…”) — has no guaranteed fix, because the model doesn’t separate instructions from data. Defense in depth: least privilege for tools, output validation, human confirmation for sensitive actions.
⚠️ Sensitive data — every prompt lands on the provider’s servers. Before sending customer data: data processing agreement (DPA), retention policy, training opt-out, anonymization where possible. “We’ll just send it to the API” is a legal decision as much as a technical one.
In an interview
“How do you reduce the latency of an LLM chatbot?” — Distinguish real from perceived latency. Perceived: streaming — the answer starts at the TTFT, a few hundred milliseconds. Real: smaller model, shorter prompt, prompt caching (a cached prefix speeds up prefill), capped output length. Bonus: precompute offline whatever can be.
“How do you guarantee valid JSON output?” — You “guarantee” nothing with the prompt alone: JSON mode or tool calling on the API side, then systematic schema validation (zod/pydantic) on the client side, retry on failure, fallback after that. LLM output is treated like untrusted user input.
“How do you evaluate the quality of an LLM feature?” — A business test set built from real cases, replayed in CI on every prompt or model change. Automatable metrics where the task allows (extraction accuracy, format compliance); LLM-as-judge calibrated on a human sample for the rest; in production, response sampling and user feedback.
“Can hallucinations be eliminated?” — No: they are structural, the model optimizes plausibility, not truth. You reduce their frequency and impact: RAG with citations, a constrained answer domain, allowing “I don’t know”, a human in the loop for consequential actions. A candidate who promises “zero hallucinations” disqualifies themselves.
“What is prompt injection?” — The LLM equivalent of SQL injection, without the equivalent of prepared statements: the model doesn’t distinguish instructions from data in its context, so a processed document or web page can carry hostile orders. Mitigations: least privilege for the agent’s tools, output validation, human confirmation of sensitive actions.
Pitfalls & misconceptions
- “We have 200k tokens of context, throw everything in” — long context is paid on every call, stretches prefill, and models retrieve information buried in the middle poorly (“lost in the middle”). Selective RAG is often more accurate and cheaper.
- “Temperature 0 = determinism” — not guaranteed: floating-point math and server-side batching introduce variation. Validation stays mandatory even at temperature 0.
- “The small model is necessarily worse” — on a well-scoped task (classification, extraction), a well-prompted small model often matches the big one. Only evals settle it, in both directions.
- Naive retries — retrying without backoff makes a rate limit worse; retrying a non-idempotent action (sending an email) duplicates it. Exponential backoff + idempotency, like any API.
- Taking LLM-as-judge at face value — without human calibration, you optimize the judge’s biases (length, position), not real quality.
💡 Budget reflex — the cost of an LLM feature is computed before writing it: (average input tokens × input price + average output tokens × output price) × calls per day. Three minutes of arithmetic beat a surprise at the end of the month.
Going further
- OWASP Top 10 for LLM Applications — prompt injection at the top of the list
- Judging LLM-as-a-Judge (Zheng et al., 2023) — the reference paper on judge biases
- Prompt caching — Anthropic docs and Batch API — OpenAI docs
- Langfuse and the OpenTelemetry GenAI conventions to trace cost and latency per feature