# Exercices — Session 8 : Infrastructure & Déploiement

**Programme :** Applied AI — Niveau Intermédiaire — Instructeur : Yann Isola
**Rappel du message central :** *un prototype qui marche, c'est 20 % du travail. Les 80 % restants s'appellent production : authentification, latence, coûts, observabilité, sécurité, montée en charge.*

---

## Exercice 1 — Le calculateur de coûts (15 min, en binôme)

### Contexte

Vous reprenez « SupportBot », l'assistant de support interne de la session. Il tourne en pilote depuis un mois et la direction demande une **projection de coûts** avant le déploiement à toute l'entreprise (5 000 employés).

**Profil d'usage mesuré pendant le pilote :**

| Paramètre | Valeur |
|---|---|
| Requêtes par jour (projection pleine échelle) | 10 000 |
| Prompt système + définitions d'outils + documents de référence (identiques à chaque requête) | 6 000 tokens |
| Question de l'utilisateur + contexte RAG variable | 1 500 tokens |
| Réponse générée (moyenne) | 500 tokens |

**Grille tarifaire fictive** (⚠ inspirée des ordres de grandeur réels de juillet 2026 — en production, vérifiez toujours la grille du jour) :

| Modèle | Entrée ($/million de tokens) | Sortie ($/million de tokens) | Entrée en cache ($/million) |
|---|---|---|---|
| **Grand modèle** (raisonnement complexe) | 3,00 | 15,00 | 0,30 |
| **Modèle intermédiaire** | 1,00 | 5,00 | 0,10 |
| **Petit modèle** (tâches simples) | 0,25 | 1,25 | 0,025 |

### Votre travail

Utilisez le **calculateur de la page web** (onglet « Calculateur de coûts ») ou faites les calculs à la main avec les formules ci-dessous.

**Formule de base :**
```
Coût journalier = requêtes/jour × [ (tokens_entrée / 1 000 000) × prix_entrée
                                  + (tokens_sortie / 1 000 000) × prix_sortie ]
Coût mensuel ≈ coût journalier × 30
```

**Étape 1 — Le scénario naïf.** Tout le trafic passe sur le **grand modèle**, sans caching. Les tokens d'entrée par requête = 6 000 + 1 500 = 7 500.
→ Calculez le coût journalier puis mensuel.

**Étape 2 — Activer le prompt caching.** Les 6 000 tokens stables sont facturés au tarif « en cache » (on néglige la première requête qui écrit le cache). Seuls les 1 500 tokens variables restent au plein tarif d'entrée.
→ Recalculez. Quel pourcentage d'économie sur la facture totale ?

**Étape 3 — Ajouter le routage de modèles.** L'analyse du pilote montre que **80 % des questions sont simples** (horaires, procédures standard) et peuvent être servies par le **petit modèle** ; 20 % nécessitent le **grand modèle**. Le caching reste actif sur les deux.
→ Recalculez la facture mensuelle totale.

**Étape 4 — Synthèse pour la direction.** Remplissez le tableau :

| Scénario | Coût mensuel | Économie vs naïf |
|---|---|---|
| Naïf (grand modèle, sans cache) | | — |
| + Prompt caching | | |
| + Routage 80/20 | | |

**Question bonus 🏆 :** sans changer le volume de requêtes ni la grille tarifaire, quel levier supplémentaire pourrait encore réduire la facture ? (Indice : regardez la colonne « sortie » de la grille et la longueur moyenne des réponses.)

### Corrigé indicatif (pour le formateur)

- **Étape 1 :** entrée : 10 000 × 7 500 / 1M × 3,00 $ = 225 $/j ; sortie : 10 000 × 500 / 1M × 15,00 $ = 75 $/j. **Total : 300 $/j ≈ 9 000 $/mois.**
- **Étape 2 :** entrée cachée : 10 000 × 6 000 / 1M × 0,30 $ = 18 $/j ; entrée vive : 10 000 × 1 500 / 1M × 3,00 $ = 45 $/j ; sortie inchangée 75 $/j. **Total : 138 $/j ≈ 4 140 $/mois** (−54 %).
- **Étape 3 :** 8 000 req/j sur le petit modèle : cache 8 000 × 6 000/1M × 0,025 = 1,20 $ ; entrée vive 8 000 × 1 500/1M × 0,25 = 3,00 $ ; sortie 8 000 × 500/1M × 1,25 = 5,00 $ → 9,20 $/j. 2 000 req/j sur le grand modèle : cache 3,60 $ ; entrée vive 9,00 $ ; sortie 15,00 $ → 27,60 $/j. **Total : 36,80 $/j ≈ 1 104 $/mois** (−88 % vs naïf).
- **Bonus :** réduire la longueur des sorties (consigne de concision dans le prompt, `max_tokens` plafonné) — la sortie est le token le plus cher ; passer de 500 à 300 tokens de sortie réduit encore le poste dominant. Autres réponses acceptables : réduire le contexte RAG injecté (1 500 → 800 tokens), utiliser le modèle intermédiaire au lieu du grand pour une partie des 20 %.

---

## Exercice 2 — Concevoir une architecture de déploiement (13 min, en binôme)

### Contexte

Choisissez **un** des trois scénarios (ou celui que le formateur vous attribue). Pour chacun, vous devez concevoir l'architecture de déploiement en assemblant les composants disponibles, puis justifier vos choix. Le **constructeur d'architecture** de la page web (onglet « Architecture ») vous permet de visualiser l'assemblage et l'estimation de latence.

**Composants disponibles :** client (navigateur/mobile) · passerelle API (API gateway) · backend applicatif · cache de réponses · file d'attente (queue) + workers · base de données · fournisseur LLM (API externe) · journalisation/monitoring.

### Scénario A — Chatbot RH interne
- 2 000 employés, ~1 500 requêtes/jour, pics à 9 h et 14 h.
- Les utilisateurs attendent une réponse conversationnelle rapide.
- Données RH sensibles dans le contexte.

### Scénario B — API de résumé de documents à fort volume
- Un service interne dépose chaque nuit 50 000 documents à résumer.
- Aucun humain n'attend devant l'écran : le résultat doit être prêt le lendemain matin.
- Le fournisseur LLM impose un rate limit de 500 requêtes/minute ⚠.

### Scénario C — Assistant temps réel intégré à un site public
- Widget de chat sur un site e-commerce, trafic public imprévisible (10× les jours de solde).
- Latence perçue critique : premier token < 1 s attendu.
- Risque élevé d'entrées malveillantes (injection de prompt, spam).

### Votre travail

1. **Dessinez l'architecture** (sur la page web ou sur papier) : quels composants, dans quel ordre, qui parle à qui.
2. **Choisissez le patron de déploiement** du backend — serverless, conteneur, ou instance GPU dédiée — et justifiez en 2 phrases.
3. **Répondez aux trois questions de robustesse :**
   - Où est stockée la clé API du fournisseur LLM ? (Toute réponse impliquant le client est éliminatoire.)
   - Que se passe-t-il si le fournisseur LLM renvoie des erreurs 429 pendant 5 minutes ?
   - Quelle est la première métrique que vous mettez sous alerte ?
4. **Streaming ou pas ?** Dites si votre scénario bénéficie du streaming SSE (Server-Sent Events, événements envoyés par le serveur), et pourquoi.

### Grille d'auto-évaluation

| Critère | ✅/❌ |
|---|---|
| La clé API est côté serveur, jamais dans le client | |
| Un composant de journalisation/monitoring est présent | |
| La stratégie face aux erreurs 429 est explicite (backoff, file, message dégradé) | |
| Le choix serverless/conteneur/GPU est justifié par le profil de trafic | |
| Le scénario B utilise une file d'attente (si scénario B choisi) | |
| La décision streaming est cohérente avec le cas d'usage | |

### Corrigé indicatif (pour le formateur)

- **Scénario A :** client → passerelle API → backend (serverless convient bien : trafic modéré avec pics, montée automatique) → fournisseur LLM, + journalisation + cache éventuel sur les questions fréquentes. Streaming : **oui** (conversationnel). Attention données RH : journalisation pseudonymisée, rétention limitée.
- **Scénario B :** dépôt des documents → **file d'attente** → workers en conteneurs qui consomment au rythme du rate limit (500 req/min → ~100 minutes pour 50 000 documents, marge confortable sur une nuit) → base de données des résultats. Streaming : **non** (aucun humain n'attend). Patron : conteneurs (charge régulière, longue durée d'exécution — le serverless est limité en durée). Métrique clé : profondeur de la file + taux d'erreur.
- **Scénario C :** client → passerelle API (avec limitation de débit par IP contre le spam) → backend serverless (trafic imprévisible ×10 → montée automatique) → fournisseur LLM en **streaming SSE**. Assainissement des entrées obligatoire (longueur max, filtrage injection), filtrage des sorties avant affichage. Métrique clé : TTFT (Time To First Token, délai avant le premier token) p95 et taux d'erreur.
- **GPU dédié :** aucun des trois scénarios ne le justifie — point à souligner en restitution. Le GPU ne se justifie que pour l'auto-hébergement d'un modèle open-weights (souveraineté, volume extrême).

---

## Exercice 3 — Concevoir un tableau de bord de monitoring (devoir à la maison ou bonus, ~30 min)

### Contexte

SupportBot est en production depuis deux semaines. Hier, un utilisateur a signalé « c'est lent et parfois ça répond n'importe quoi ». Vous n'avez actuellement **aucun** tableau de bord. Votre mission : concevoir (sur papier ou avec l'outil de votre choix) le tableau de bord de monitoring que l'équipe consultera chaque matin, en vous inspirant de la maquette de la page web (onglet « Monitoring »).

### Votre travail

1. **Choisissez 6 à 8 métriques**, pas plus. Pour chacune, précisez :
   - le nom et l'unité ;
   - la source (journal des requêtes, API du fournisseur, retour utilisateur…) ;
   - la fenêtre d'observation (temps réel, horaire, quotidienne) ;
   - le **seuil d'alerte** et l'action associée (une alerte sans action documentée est interdite).

2. **Organisez-les en trois zones** sur votre maquette :
   - 🟢 **Santé** — le service fonctionne-t-il ? (disponibilité, taux d'erreur…)
   - ⏱️ **Performance** — est-il assez rapide ? (latence p50/p95/p99, TTFT…)
   - 💰 **Coûts** — combien brûle-t-on ? (tokens/jour, coût cumulé vs budget, taux de cache hit…)

3. **Rédigez les 3 règles d'alerte prioritaires** au format :
   > SI `<métrique>` `<condition>` PENDANT `<durée>` ALORS `<qui est prévenu>` FAIT `<quoi>`.

4. **Question piège :** votre tableau de bord détecte-t-il le problème « parfois ça répond n'importe quoi » ? Si non (probable), que faut-il ajouter ? (Indice : la qualité des réponses ne se mesure ni en millisecondes ni en tokens — pensez aux évaluations continues et au retour utilisateur 👍/👎.)

### Grille d'évaluation (pour le formateur)

| Critère | Points |
|---|---|
| 6–8 métriques pertinentes, avec unité et source | /4 |
| Latence exprimée en percentiles (pas seulement en moyenne) | /2 |
| Chaque alerte a un seuil, une durée ET une action documentée | /3 |
| Zone coûts : budget de tokens + taux de cache présents | /2 |
| Question piège : identifie la limite (qualité ≠ métriques techniques) et propose un signal qualité (évals en continu, feedback utilisateur, échantillonnage manuel) | /4 |
| Clarté générale de la maquette | /1 |
| **Total** | **/16** |

### Éléments de corrigé (pour le formateur)

Métriques attendues (toute sélection cohérente est acceptable) : disponibilité (%), taux d'erreur (% sur 5 min), taux d'erreurs 429 spécifiquement, latence p50/p95/p99 (s), TTFT p95 (ms), tokens consommés/jour vs budget (%), coût cumulé mensuel ($), taux de cache hit (%), répartition du routage petit/grand modèle (%), profondeur de file d'attente (si file), score moyen des évals continues, taux de 👍 utilisateur.

Exemple de règle d'alerte bien formée :
> SI `taux d'erreur` `> 2 %` PENDANT `5 minutes` ALORS `l'ingénieur d'astreinte reçoit une notification` FAIT `vérifier le statut du fournisseur LLM, activer le mode dégradé si panne confirmée`.

Réponse à la question piège : non — les métriques techniques ne voient pas la qualité. Ajouter : (1) un bouton 👍/👎 dans l'interface, agrégé quotidiennement ; (2) une éval automatique sur un échantillon de X % des réponses en production (juge LLM ou règles) ; (3) une revue humaine hebdomadaire d'un échantillon aléatoire. C'est le pont vers la Session 9.

---

## Exercice 4 — Auto-hébergement : dimensionner une quantization (10 min, en binôme)

### Contexte

Une clinique refuse d'envoyer les données de ses patients à un fournisseur externe (**souveraineté des données** — les données ne doivent jamais sortir du bâtiment). Vous devez donc **auto-héberger** un assistant sur un serveur équipé de **2 cartes GPU de 24 Go** de mémoire vidéo (VRAM), soit **48 Go** au total. Le modèle open-weights retenu compte **70 milliards de paramètres**.

**Rappel du cours (formule d'empreinte) :**
```
Taille mémoire des poids ≈ nombre_de_paramètres × (bits_par_poids / 8)  [en octets]
```
(On néglige ici la mémoire de contexte/KV-cache ; l'ordre de grandeur suffit pour décider.)

### Votre travail

**Étape 1 — Calculez l'empreinte** du modèle 70B à chaque précision et remplissez le tableau. Indiquez si ça **tient** dans les 48 Go disponibles.

| Précision | Bits/poids | Empreinte ≈ | Tient dans 48 Go ? |
|---|---|---|---|
| FP32 | 32 | | |
| FP16 | 16 | | |
| INT8 (Q8) | 8 | | |
| INT4 (Q4) | 4 | | |

**Étape 2 — Choisissez** la précision que vous déploieriez et justifiez en 2 phrases (empreinte **et** qualité).

**Étape 3 — Traduisez** votre choix en une commande concrète : quel outil (Ollama / llama.cpp), quel format de fichier, quel suffixe de quantization ? Écrivez la ligne `ollama pull ...` correspondante.

**Étape 3-bis — Test local si possible.** Si votre machine dispose d'Ollama, lancez un petit modèle déjà quantifié et notez : (1) la commande, (2) le temps avant le premier token, (3) votre impression qualitative sur une réponse courte. Exemple copiable :
```bash
ollama run llama3:8b-instruct-q4_K_M "Explique la quantization en 2 phrases."
```
Si Ollama n'est pas installé, écrivez simplement ce que vous testeriez et pourquoi ce modèle Q4 est moins risqué qu'un 70B pour un poste de formation.

**Étape 4 — Question de jugement.** Un collègue propose « prenons plutôt un petit modèle de 8B en FP16, comme ça pas besoin de quantifier ». Que répondez-vous ? (Indice : comparez la **qualité** attendue d'un 70B-Q4 face à un 8B-FP16.)

**Question bonus 🏆 :** pourquoi la quantization **accélère-t-elle** l'inférence, et pas seulement la réduction d'empreinte ? (Indice : Session 1, le goulot d'étranglement de l'inférence.)

### Corrigé indicatif (pour le formateur)

- **Étape 1 :** FP32 = 70 × 4 = **280 Go** (ne tient pas) ; FP16 = 70 × 2 = **140 Go** (ne tient pas) ; INT8 = 70 × 1 = **70 Go** (ne tient pas dans 48 Go) ; INT4 = 70 × 0,5 = **~35–40 Go** (✅ tient, avec une marge pour le contexte).
- **Étape 2 :** **INT4 (Q4)** — seule précision qui rentre dans 48 Go, et la perte de qualité face à FP16 est généralement de l'ordre de 1–3 points ⚠, acceptable pour un assistant interne. (Q5_K_M ≈ 45–48 Go peut aussi être tenté si la qualité mesurée le justifie, mais la marge est mince.)
- **Étape 3 :** `ollama pull <modele>:70b-q4_K_M` (format **GGUF**, 4 bits, blocs « K », taille « M » medium), puis `ollama run`.
- **Étape 3-bis :** attendu : un test court du type `ollama run llama3:8b-instruct-q4_K_M "Explique la quantization en 2 phrases."`, avec observation du temps avant le premier token et de la fluidité. Si non testé, l'apprenant doit justifier : contrainte d'installation / matériel / temps, et préciser que l'exercice vérifie surtout le raisonnement de dimensionnement.
- **Étape 4 :** un **70B-Q4 reste nettement meilleur** qu'un 8B-FP16 pour une empreinte comparable. La quantization ne rend pas un petit modèle « aussi bon » qu'un grand — elle rend un **grand modèle exécutable** sur du matériel modeste. Le réflexe « petit modèle » ne se justifie que si les tâches sont réellement simples (cf. routage, Exercice 1).
- **Bonus :** l'inférence est limitée par la **bande passante mémoire** — il faut relire tous les poids depuis la mémoire à chaque token généré. Moins de bits par poids = moins d'octets à relire par token = plus de tokens par seconde. La quantization allège **et** accélère pour la même raison.
