Applied AI â Niveau AvancĂ©
Session 4 : Architecture multi-agents
Programme : Applied AI â Formation professionnelle en intelligence artificielle Instructeur : Yann Isola Niveau : AvancĂ© â Architectes solutions prĂ©parant la certification Claude Certified Architect DurĂ©e recommandĂ©e : 3 h 30 (2 h de cours magistral + 1 h 30 dâexercices pratiques) PrĂ©requis : Sessions 1 Ă 3 du niveau avancĂ© (orchestration, outils/function calling, gestion de contexte)
Objectifs pédagogiques
Ă lâissue de cette session, les participants seront capables de :
- Choisir et justifier une stratégie de décomposition de tùches (verticale, horizontale, récursive) selon la complexité, le budget de tokens et le bénéfice de spécialisation.
- Concevoir une politique dâescalade complĂšte avec Human-in-the-Loop (HITL â humain dans la boucle) : dĂ©clencheurs, motifs dâescalade, intĂ©gration du feedback humain et gestion des SLA (Service Level Agreement â accord de niveau de service).
- ImplĂ©menter les patrons de gestion dâerreurs propres aux systĂšmes multi-agents : retry idempotent, checkpointing dâĂ©tat, circuit breaker (disjoncteur logiciel), dĂ©gradation gracieuse et files de lettres mortes (DLQ â Dead Letter Queue).
- Instrumenter un systÚme multi-agents avec du traçage distribué et des identifiants de corrélation.
Plan de la session
| Bloc | Durée | Contenu |
|---|---|---|
| 1 | 40 min | Stratégies de décomposition de tùches (Chapitre 8) |
| 2 | 40 min | Escalade et Human-in-the-Loop (Chapitre 9) |
| 3 | 40 min | Gestion dâerreurs multi-agents (Chapitre 10) |
| 4 | 90 min | Exercices pratiques + démonstration interactive (page web) |
| 5 | 10 min | Quiz de validation et synthĂšse |
Bloc 1 â StratĂ©gies de dĂ©composition de tĂąches
1.1 Pourquoi décomposer ?
Un agent unique confronté à une tùche complexe rencontre trois murs :
- Le mur du contexte : la fenĂȘtre de contexte (quantitĂ© de texte que le modĂšle peut traiter en une fois) est finie. Une tĂąche massive sature le contexte et dĂ©grade la qualitĂ© des rĂ©ponses.
- Le mur de la cohérence : plus une tùche est longue, plus le risque de dérive (perte de fil, contradictions internes) augmente.
- Le mur de la spĂ©cialisation : un prompt systĂšme gĂ©nĂ©raliste est moins performant quâun prompt spĂ©cialisĂ© sur chaque sous-domaine.
La décomposition transforme un problÚme monolithique en un graphe de sous-tùches confiées à des agents dédiés, coordonnés par un orchestrateur.
Point pĂ©dagogique : insistez sur le fait que la dĂ©composition nâest pas gratuite. Chaque agent supplĂ©mentaire ajoute de la latence, du coĂ»t en tokens et de la complexitĂ© opĂ©rationnelle. La question nâest jamais « peut-on dĂ©composer ? » mais « le gain dĂ©passe-t-il le coĂ»t ? ».
1.2 Décomposition verticale (par profondeur)
Principe : découper le flux de travail en étapes séquentielles de nature différente. Le motif canonique :
Planification â ExĂ©cution â VĂ©rification
- Agent planificateur : analyse la demande, produit un plan structuré (liste ordonnée de sous-tùches, dépendances, critÚres de succÚs).
- Agent exĂ©cutant : rĂ©alise chaque Ă©tape du plan (appels dâoutils, gĂ©nĂ©ration de contenu, transformations).
- Agent vérificateur : contrÎle le résultat contre les critÚres de succÚs, détecte les incohérences, déclenche des corrections.
Avantages :
- Chaque agent a un prompt systÚme court et ciblé.
- Le vĂ©rificateur, indĂ©pendant de lâexĂ©cutant, rĂ©duit les biais dâauto-Ă©valuation (un agent qui vĂ©rifie son propre travail est notoirement indulgent avec lui-mĂȘme).
- Le plan sert de contrat : il est auditable et modifiable par un humain avant exécution.
Limites :
- Latence cumulée : chaque étage attend le précédent.
- Le plan peut devenir obsolĂšte en cours dâexĂ©cution si lâenvironnement change (nĂ©cessite une boucle de replanification).
Exemple dâimplĂ©mentation (pseudocode Python) :
class VerticalPipeline:
def __init__(self, planner, executor, verifier, max_repairs=2):
self.planner = planner # Agent LLM avec prompt "planificateur"
self.executor = executor # Agent LLM avec accĂšs aux outils
self.verifier = verifier # Agent LLM avec prompt "auditeur"
self.max_repairs = max_repairs
def run(self, task: str) -> Result:
plan = self.planner.plan(task) # â liste d'Ă©tapes + critĂšres
result = self.executor.execute(plan) # â artefacts produits
for attempt in range(self.max_repairs):
report = self.verifier.check(result, plan.success_criteria)
if report.ok:
return Result(status="success", artifacts=result)
# Le rapport d'audit devient une consigne de réparation
result = self.executor.repair(result, report.issues)
return Result(status="needs_human_review", artifacts=result)
Notez la sortie needs_human_review : la dĂ©composition verticale sâarticule naturellement avec lâescalade (Bloc 2).
1.3 Décomposition horizontale (par domaine)
Principe : dĂ©couper par domaine de compĂ©tence. Chaque agent est propriĂ©taire dâun domaine et le traite de bout en bout.
Exemple â analyse dâun dossier de conformitĂ© financiĂšre :
ââââââââââââââââââââ
â Orchestrateur â
ââââââââââŹââââââââââ
ââââââââââââââââŹâââââŽââââââââââŹââââââââââââââââ
⌠⌠⌠âŒ
Agent juridique Agent fiscal Agent risque Agent rédaction
(réglementation) (imposition) (scoring) (synthÚse finale)
- Les agents juridique, fiscal et risque travaillent en parallĂšle (gain de latence majeur).
- Lâagent de rĂ©daction fusionne les analyses (Ă©tape de fan-in, convergence des rĂ©sultats).
Avantages :
- ParallĂ©lisme natif â latence proche de celle de lâagent le plus lent, pas de la somme.
- Spécialisation maximale : chaque agent embarque le vocabulaire, les outils et les garde-fous de son domaine.
- Isolation des pannes : lâĂ©chec de lâagent fiscal nâempĂȘche pas lâagent juridique de livrer.
Limites :
- Risque dâincohĂ©rences inter-domaines (deux agents tirent des conclusions contradictoires) â nĂ©cessite une Ă©tape de rĂ©conciliation.
- Lâorchestrateur doit savoir router : mal dĂ©couper les frontiĂšres de domaines crĂ©e des zones grises oĂč aucun agent ne se sent responsable.
1.4 Décomposition récursive
Principe : chaque agent peut lui-mĂȘme dĂ©composer sa sous-tĂąche et dĂ©lĂ©guer Ă des sous-agents, formant un arbre de dĂ©lĂ©gation.
Orchestrateur racine
âââ Agent A (sous-tĂąche 1)
â âââ Sous-agent A1
â âââ Sous-agent A2
â âââ Sous-sous-agent A2a
âââ Agent B (sous-tĂąche 2)
RÚgles de sécurité indispensables :
- Profondeur maximale (
max_depth) : sans limite, un agent peut crĂ©er une rĂ©cursion infinie de dĂ©lĂ©gations. Valeur typique : 2 Ă 3 niveaux. â (valeur indicative, Ă calibrer selon votre charge) - Budget de tokens hĂ©ritĂ© : chaque niveau reçoit une fraction du budget parent. Si le parent a 100 000 tokens, il nâen dĂ©lĂšgue par exemple que 30 000 par enfant.
- Contrat de résultat : chaque sous-agent retourne un format structuré (JSON avec statut, artefacts, coût consommé), jamais du texte libre non contraint.
Quand la rĂ©cursion brille : les problĂšmes Ă structure fractale â revue de code dâun monorepo (chaque module se dĂ©compose en fichiers), due diligence documentaire (chaque dossier se dĂ©compose en piĂšces), gĂ©nĂ©ration de sites (chaque page se dĂ©compose en sections).
1.5 Quand décomposer ? Les trois critÚres de décision
| CritĂšre | Question Ă poser | Seuil indicatif |
|---|---|---|
| Seuil de complexitĂ© | La tĂąche comporte-t-elle plus de N Ă©tapes hĂ©tĂ©rogĂšnes ? Un seul prompt systĂšme peut-il couvrir tous les cas ? | > 5â7 Ă©tapes distinctes â |
| Budget de tokens | Le contexte nĂ©cessaire (documents + historique + outils) dĂ©passe-t-il ~60â70 % de la fenĂȘtre ? â | Saturation prĂ©visible â dĂ©composer |
| Bénéfice de spécialisation | Des prompts spécialisés amélioreraient-ils mesurablement la qualité par domaine ? | Gain qualité > surcoût orchestration |
Arbre de décision à présenter au tableau :
- La tĂąche tient-elle confortablement dans un seul contexte avec une qualitĂ© acceptable ? â Ne dĂ©composez pas. (Lâarchitecture la plus robuste est celle qui nâexiste pas.)
- Les Ă©tapes sont-elles de natures diffĂ©rentes (planifier vs exĂ©cuter vs vĂ©rifier) ? â Verticale.
- Les sous-problĂšmes relĂšvent-ils de domaines diffĂ©rents et parallĂ©lisables ? â Horizontale.
- La structure est-elle imprĂ©visible ou fractale ? â RĂ©cursive, avec garde-fous stricts.
- Cas rĂ©els : souvent hybride â un pipeline vertical dont lâĂ©tape dâexĂ©cution est horizontale.
PiĂšge de certification : les questions dâexamen opposent souvent verticale et horizontale sur un cas ambigu. Le discriminant fiable : sĂ©quentialitĂ© de natures diffĂ©rentes â verticale ; parallĂ©lisme de domaines â horizontale.
Bloc 2 â Escalade et Human-in-the-Loop (HITL)
2.1 Pourquoi lâescalade est une exigence dâarchitecture, pas une option
Aucun systĂšme dâagents nâatteint 100 % dâautonomie fiable. Lâarchitecte doit concevoir dĂšs le dĂ©part les chemins par lesquels le systĂšme rend la main Ă lâhumain. Un systĂšme sans escalade explicite escalade quand mĂȘme â mais de façon chaotique : tickets support, incidents, perte de confiance.
HITL (Human-in-the-Loop â humain dans la boucle) dĂ©signe lâensemble des mĂ©canismes par lesquels un humain valide, corrige ou reprend le travail dâun agent.
2.2 Les trois dĂ©clencheurs dâescalade
a) Confiance sous le seuil
Lâagent (ou un modĂšle Ă©valuateur) produit un score de confiance sur sa propre sortie. Sous un seuil dĂ©fini, la tĂąche est escaladĂ©e.
CONFIDENCE_THRESHOLD = 0.75 # â Ă calibrer sur donnĂ©es rĂ©elles, jamais au doigt mouillĂ©
assessment = judge_agent.evaluate(answer, context)
if assessment.confidence < CONFIDENCE_THRESHOLD:
escalate(task, reason="low_confidence", score=assessment.confidence)
Point critique Ă enseigner : les LLM (Large Language Models â grands modĂšles de langage) sont mal calibrĂ©s sur leur propre confiance auto-dĂ©clarĂ©e. Bonnes pratiques :
- Utiliser un agent juge distinct plutĂŽt que lâauto-Ă©valuation.
- Calibrer le seuil sur un jeu de validation Ă©tiquetĂ© (courbe prĂ©cision/taux dâescalade).
- Surveiller la dĂ©rive du taux dâescalade en production (un taux qui chute brutalement peut signaler un juge devenu complaisant, pas un systĂšme devenu meilleur).
b) Détection hors périmÚtre (out-of-scope)
Lâagent reconnaĂźt que la demande sort de son domaine autorisĂ© : sujet non couvert, langue non supportĂ©e, demande de conseil rĂ©glementĂ© (juridique, mĂ©dical, financier), tentative de contournement.
ImplĂ©mentation typique : un classifieur de pĂ©rimĂštre en amont (petit modĂšle rapide) + une consigne dâauto-dĂ©tection dans le prompt systĂšme de lâagent (dĂ©fense en profondeur â deux couches valent mieux quâune).
c) Niveau de risque
Certaines actions sont escaladĂ©es par nature, indĂ©pendamment de la confiance : virement au-delĂ dâun montant, suppression de donnĂ©es, communication externe engageante, modification de production. On dĂ©finit une matrice de risque :
| Impact \ Réversibilité | Réversible | Irréversible |
|---|---|---|
| Faible | Autonome | Autonome + journalisation renforcée |
| Moyen | Autonome + revue a posteriori | Approbation humaine préalable |
| ĂlevĂ© | Approbation humaine prĂ©alable | Approbation humaine + double validation |
2.3 Les trois patrons dâescalade
Patron 1 â Pause-and-ask (pause et question)
Lâagent suspend son exĂ©cution, pose une question prĂ©cise Ă lâhumain, attend la rĂ©ponse, puis reprend. Synchrone du point de vue de la tĂąche.
- Usage : ambiguïté bloquante en cours de tùche, décision ponctuelle (choix entre deux options).
- Exigence technique : lâĂ©tat de lâagent doit ĂȘtre sĂ©rialisable (sauvegardable) pour survivre Ă lâattente â lâhumain peut rĂ©pondre dans 4 heures.
- Risque : accumulation de tĂąches suspendues si les humains ne rĂ©pondent pas â prĂ©voir un timeout (voir SLA, §2.5).
Patron 2 â Queue-for-review (file de revue)
Lâagent termine son travail mais le rĂ©sultat est placĂ© dans une file de validation avant publication/exĂ©cution. Asynchrone, lâagent est libĂ©rĂ© immĂ©diatement.
- Usage : production de contenu à enjeu (réponses clients, documents contractuels), actions batch.
- Avantage : lâhumain traite par lots, avec une interface de revue dĂ©diĂ©e (diff, approuver/rejeter/corriger).
- MĂ©trique clĂ© : le taux dâapprobation sans modification. Sâil dĂ©passe durablement ~98 % â , envisager de passer certaines catĂ©gories en autonome (avec Ă©chantillonnage de contrĂŽle).
Patron 3 â Fallback-to-human (transfert Ă lâhumain)
Lâagent abandonne la tĂąche et la transfĂšre intĂ©gralement Ă un humain, avec tout le contexte accumulĂ©. Câest le filet de sĂ©curitĂ© terminal.
- Usage : échec répété, situation hors périmÚtre, détresse utilisateur détectée, risque juridique.
- RĂšgle dâor du transfert : lâhumain ne doit jamais repartir de zĂ©ro. Le paquet de transfert contient : la demande initiale, lâhistorique des actions de lâagent, les hypothĂšses testĂ©es, la raison prĂ©cise de lâescalade. Un transfert sans contexte dĂ©truit la valeur du travail agent.
2.4 Intégration du feedback humain
Lâescalade nâest pas seulement un filet de sĂ©curitĂ© â câest un capteur dâapprentissage. Chaque correction humaine est une donnĂ©e.
Boucle de feedback en quatre temps :
- Capture : chaque décision humaine (approbation, rejet, correction) est journalisée de façon structurée :
{task_id, sortie_agent, action_humaine, sortie_corrigĂ©e, motif}. - AgrĂ©gation : analyse pĂ©riodique des corrections par catĂ©gorie â quels types de tĂąches sont le plus souvent corrigĂ©s ? Quels motifs reviennent ?
- Injection : les enseignements retournent dans le systÚme par trois canaux, du plus léger au plus lourd :
- Prompt systÚme : ajout de rÚgles et contre-exemples tirés des corrections récurrentes.
- Exemples few-shot / base de connaissances : les paires (cas â correction validĂ©e) alimentent la rĂ©cupĂ©ration de contexte (RAG â Retrieval-Augmented Generation, gĂ©nĂ©ration augmentĂ©e par rĂ©cupĂ©ration).
- Fine-tuning (ajustement fin du modĂšle) : uniquement si le volume de corrections est suffisant et le comportement cible stable. â CoĂ»teux, Ă réévaluer Ă chaque Ă©volution de modĂšle de base.
- Mesure : le taux de correction par catĂ©gorie doit baisser aprĂšs injection. Sinon, la boucle ne boucle pas â câest du théùtre de feedback.
Point dâinsistance pĂ©dagogique : beaucoup dâorganisations capturent le feedback mais ne lâinjectent jamais. La question dâaudit Ă poser : « montrez-moi la derniĂšre modification de prompt motivĂ©e par une correction humaine, et sa date ».
2.5 Gestion des SLA dâescalade
Une escalade sans SLA (Service Level Agreement â accord de niveau de service) est une escalade vers un trou noir.
Composants dâune politique de SLA :
-
Timeout dâescalade : si aucun humain ne rĂ©pond dans le dĂ©lai, une action par dĂ©faut sâapplique. Trois stratĂ©gies de timeout :
- Fail-safe (sĂ©curitĂ© par dĂ©faut) : la tĂąche est annulĂ©e ou mise en attente prolongĂ©e â pour les actions risquĂ©es.
- Fail-operational : lâagent applique lâoption conservatrice prĂ©-approuvĂ©e â pour les tĂąches Ă faible enjeu.
- RĂ©-escalade : la demande monte dâun niveau (n+1 hiĂ©rarchique, Ă©quipe dâastreinte).
-
Files de priorité : toutes les escalades ne se valent pas. File typique à trois niveaux :
| PrioritĂ© | Exemple | DĂ©lai cible â (indicatif) |
|---|---|---|
| P1 â Critique | Transaction bloquĂ©e, client en dĂ©tresse, risque lĂ©gal | < 15 min |
| P2 â Standard | Validation de contenu, ambiguĂŻtĂ© non bloquante | < 4 h |
| P3 â DiffĂ©rable | Revue par Ă©chantillonnage, amĂ©lioration continue | < 48 h |
-
Routage par compĂ©tence : lâescalade juridique va au juriste, pas au support gĂ©nĂ©raliste. Le paquet dâescalade porte des Ă©tiquettes (domaine, langue, urgence) exploitĂ©es par le routeur.
-
Tableaux de bord : taux dâescalade, temps de premiĂšre rĂ©ponse humaine, taux de dĂ©passement de SLA, rĂ©partition par motif. Ce sont des mĂ©triques dâarchitecture, pas seulement dâexploitation.
Bloc 3 â Gestion dâerreurs dans les systĂšmes multi-agents
3.1 La propagation dâerreurs : lâeffet domino
Dans un systĂšme multi-agents, une erreur locale devient systĂ©mique par cascade Ă travers lâorchestrateur :
Sous-agent C échoue
â
Agent B attend C â timeout â B Ă©choue
â
L'orchestrateur reçoit deux Ă©checs corrĂ©lĂ©s â abandonne la mission entiĂšre
â
Résultat : 45 minutes de travail des agents A, D, E jetées à la poubelle
Trois principes anti-cascade :
- Isolation (bulkheads â cloisons Ă©tanches) : lâĂ©chec dâune branche ne doit pas invalider les branches indĂ©pendantes. Lâorchestrateur agrĂšge les rĂ©sultats partiels : « 4 analyses sur 5 livrĂ©es, lâanalyse fiscale a Ă©chouĂ© ».
- Timeouts systématiques : tout appel inter-agents porte un délai maximal. Un agent qui attend indéfiniment est une panne silencieuse.
- Typologie dâerreurs : distinguer les erreurs rĂ©essayables (timeout rĂ©seau, rate limit â limite de dĂ©bit dâAPI, surcharge temporaire) des erreurs fatales (entrĂ©e invalide, permission refusĂ©e, contrainte mĂ©tier violĂ©e). RĂ©essayer une erreur fatale gaspille du budget et retarde lâescalade.
class AgentError(Exception):
retryable: bool
class RateLimitError(AgentError): retryable = True
class ToolTimeoutError(AgentError): retryable = True
class InvalidInputError(AgentError): retryable = False
class PolicyViolationError(AgentError): retryable = False
3.2 Stratégies de retry (nouvelle tentative)
a) La condition prĂ©alable : lâidempotence des outils
Un outil est idempotent si lâexĂ©cuter deux fois produit le mĂȘme effet quâune fois. Sans idempotence, le retry est dangereux : rejouer « crĂ©er une facture » aprĂšs un timeout ambigu peut crĂ©er un doublon (le timeout ne dit pas si la premiĂšre tentative a abouti cĂŽtĂ© serveur).
Technique standard : la clĂ© dâidempotence â le client gĂ©nĂšre un identifiant unique par intention dâopĂ©ration ; le serveur dĂ©duplique.
import uuid
def create_invoice(client_id: str, amount: float) -> Invoice:
idem_key = f"inv-{uuid.uuid4()}" # générée UNE fois par intention
for attempt in range(3):
try:
return api.post("/invoices",
json={"client_id": client_id, "amount": amount},
headers={"Idempotency-Key": idem_key})
except TransientError:
sleep(backoff(attempt)) # backoff exponentiel + jitter
raise ToolFailedError("create_invoice", idem_key)
def backoff(attempt: int) -> float:
"""Backoff exponentiel avec jitter (gigue aléatoire) pour éviter
que tous les agents ne rĂ©essaient au mĂȘme instant (thundering herd)."""
import random
return min(30, (2 ** attempt)) * random.uniform(0.5, 1.5)
RĂšgle dâarchitecte : dans la spĂ©cification de chaque outil exposĂ© aux agents, documenter explicitement
idempotent: true/false. Lâorchestrateur nâautorise le retry automatique que sur les outils marquĂ©s idempotents.
b) Checkpointing dâĂ©tat (points de sauvegarde)
Pour les tĂąches longues, sauvegarder lâĂ©tat Ă chaque jalon permet de reprendre aprĂšs Ă©chec sans tout rejouer :
class CheckpointedMission:
def run(self, mission_id: str):
state = self.store.load(mission_id) or MissionState.initial()
for step in self.plan.steps:
if step.id in state.completed_steps:
continue # déjà fait : on saute
result = self.execute(step, state)
state.record(step.id, result)
self.store.save(mission_id, state) # checkpoint aprĂšs CHAQUE jalon
Le checkpoint contient : Ă©tapes accomplies, artefacts produits, budget consommĂ©, et le contexte minimal nĂ©cessaire Ă la reprise (pas tout lâhistorique de conversation â le rĂ©sumer).
c) Rollback partiel (annulation partielle)
Quand une Ă©tape Ă©choue au milieu dâune sĂ©quence avec effets de bord, on annule les effets de la sĂ©quence en cours sans dĂ©truire le travail des sĂ©quences validĂ©es. Patron : compensation (chaque action possĂšde une action inverse enregistrĂ©e). InspirĂ© du patron Saga des architectures microservices :
Ătape 1 : rĂ©server stock â compensation : libĂ©rer stock
Ătape 2 : dĂ©biter compte â compensation : recrĂ©diter compte
Ătape 3 : crĂ©er expĂ©dition â ĂCHEC
â Rejouer les compensations 2 puis 1 (ordre inverse), Ă©tat cohĂ©rent restaurĂ©.
3.3 Le patron Circuit Breaker (disjoncteur logiciel)
ProblĂšme : un service externe (API dâoutil, sous-agent) tombe en panne. Sans protection, chaque tĂąche continue de lâappeler, subit le timeout complet, gaspille tokens et latence, et surcharge le service dĂ©jĂ agonisant.
Solution : un disjoncteur avec trois états, comme un disjoncteur électrique :
N échecs consécutifs
âââââââââââ ââââââââââââââââââââââș âââââââââââ
â FERMĂ â â OUVERT â
â(closed) â âââââ succĂšs âââââ â (open) â
âââââââââââ â ââââââŹâââââ
appels passent â â aprĂšs dĂ©lai de refroidissement
normalement â ⌠(cooldown)
âââââââŽââââââââââââ
â SEMI-OUVERT âââââ Ă©chec âââââș retour Ă OUVERT
â (half-open) â
âââââââââââââââââââ
laisse passer UN appel d'essai
- FERMà (closed) : fonctionnement normal ; on compte les échecs consécutifs.
- OUVERT (open) : aprĂšs N Ă©checs (ex. : 5 â , Ă calibrer), les appels sont rejetĂ©s immĂ©diatement sans ĂȘtre tentĂ©s. Le systĂšme Ă©choue vite (fail fast) et le service en panne souffle.
- SEMI-OUVERT (half-open) : aprĂšs un dĂ©lai de refroidissement (ex. : 30 s â ), un unique appel dâessai passe. SuccĂšs â retour Ă FERMĂ. Ăchec â retour Ă OUVERT.
import time
class CircuitBreaker:
def __init__(self, failure_threshold=5, cooldown_s=30):
self.failure_threshold = failure_threshold
self.cooldown_s = cooldown_s
self.failures = 0
self.state = "closed"
self.opened_at = None
def call(self, fn, *args, **kwargs):
if self.state == "open":
if time.time() - self.opened_at >= self.cooldown_s:
self.state = "half_open" # tentative d'essai autorisée
else:
raise CircuitOpenError("appel rejeté : circuit ouvert")
try:
result = fn(*args, **kwargs)
except TransientError:
self._on_failure()
raise
self._on_success()
return result
def _on_failure(self):
self.failures += 1
if self.state == "half_open" or self.failures >= self.failure_threshold:
self.state = "open"
self.opened_at = time.time()
def _on_success(self):
self.failures = 0
self.state = "closed"
SpĂ©cificitĂ© multi-agents : placer un disjoncteur par dĂ©pendance (un par outil externe, un par sous-agent), jamais un disjoncteur global. Quand un disjoncteur sâouvre, lâorchestrateur doit le savoir pour activer la dĂ©gradation gracieuse (§3.4) plutĂŽt que de laisser les tĂąches sâĂ©craser contre lui.
3.4 Dégradation gracieuse
Principe : face Ă une panne partielle, rĂ©duire la capacitĂ© plutĂŽt quâĂ©chouer complĂštement. Le systĂšme livre un service diminuĂ© mais honnĂȘte.
Ăchelle de dĂ©gradation (Ă concevoir a priori, pas dans la panique) :
| Niveau | Situation | Comportement |
|---|---|---|
| 0 â Nominal | Tout fonctionne | Service complet |
| 1 â DĂ©gradĂ© lĂ©ger | Un outil dâenrichissement en panne | RĂ©ponse sans lâenrichissement, mention explicite du manque |
| 2 â DĂ©gradĂ© fort | Sous-agent spĂ©cialisĂ© indisponible | RĂ©ponse gĂ©nĂ©rique par lâagent principal + avertissement + proposition dâescalade |
| 3 â Minimal | ModĂšle principal indisponible | Bascule sur modĂšle de secours plus petit, pĂ©rimĂštre restreint â |
| 4 â Refus honnĂȘte | Rien ne fonctionne | Message dâindisponibilitĂ© + capture de la demande pour traitement diffĂ©rĂ© |
Deux rĂšgles absolues :
- Transparence : ne jamais livrer une rĂ©ponse dĂ©gradĂ©e en la prĂ©sentant comme complĂšte. « Lâanalyse fiscale nâa pas pu ĂȘtre rĂ©alisĂ©e (service indisponible) ; voici les 4 autres volets » â câest acceptable. Le silence sur le manque ne lâest pas.
- DĂ©gradation â baisse de sĂ©curitĂ© : les garde-fous (filtres, validations, escalades) ne se dĂ©gradent jamais. On dĂ©grade la capacitĂ©, pas le contrĂŽle.
3.5 Dead Letter Queue (DLQ â file de lettres mortes)
Principe : une tĂąche qui a Ă©puisĂ© ses retries et ne peut ĂȘtre traitĂ©e nâest jamais silencieusement supprimĂ©e. Elle est dĂ©posĂ©e dans une file dĂ©diĂ©e â la file de lettres mortes â avec tout son contexte de diagnostic.
Contenu dâune entrĂ©e DLQ bien formĂ©e :
{
"task_id": "task-8842",
"correlation_id": "corr-a1b9f3",
"deposited_at": "2026-07-02T14:31:07Z",
"attempts": 3,
"last_error": {"type": "ToolTimeoutError", "tool": "search_registry", "timeout_s": 60},
"agent_chain": ["orchestrator", "agent-juridique", "sub-agent-registre"],
"payload": { "...la tĂąche originale intacte..." },
"state_checkpoint_ref": "ckpt://missions/8842/step-3"
}
Exploitation de la DLQ :
- Alerte : un dépÎt en DLQ déclenche une notification (la DLQ qui grossit en silence est un anti-patron classique).
- Rejeu : aprĂšs correction de la cause racine, les entrĂ©es sont rejouĂ©es â dâoĂč lâimportance des clĂ©s dâidempotence et des checkpoints.
- Analyse de tendance : la composition de la DLQ est un miroir des faiblesses du systĂšme. 80 % de
ToolTimeoutErrorsur le mĂȘme outil = problĂšme dâinfrastructure, pas dâagents.
3.6 Observabilité : traçage distribué et identifiants de corrélation
Dans un systĂšme oĂč une demande traverse un orchestrateur, trois agents et sept appels dâoutils, rĂ©pondre à « pourquoi cette rĂ©ponse est-elle fausse ? » sans traçage relĂšve de lâarchĂ©ologie.
a) Identifiant de corrélation (correlation ID)
Un identifiant unique est gĂ©nĂ©rĂ© Ă lâentrĂ©e de la demande et propagĂ© Ă travers chaque agent, chaque appel dâoutil, chaque message de file. Toute ligne de journal le porte.
import contextvars, uuid
correlation_id = contextvars.ContextVar("correlation_id")
def handle_request(request):
cid = request.headers.get("X-Correlation-ID") or f"corr-{uuid.uuid4().hex[:8]}"
correlation_id.set(cid)
log.info("request.received", cid=cid, task=request.task)
return orchestrator.run(request, cid=cid) # propagation explicite aux sous-agents
RĂšgle : grep corr-a1b9f3 logs/* doit reconstituer lâintĂ©gralitĂ© du parcours dâune demande. Si un composant « perd » lâidentifiant, lâobservabilitĂ© est cassĂ©e Ă cet endroit prĂ©cis.
b) Traçage distribué (distributed tracing)
Au-delĂ des logs : chaque opĂ©ration devient un span (segment chronomĂ©trĂ©) rattachĂ© Ă une trace (lâarbre complet de la demande). Standard de facto : OpenTelemetry (spĂ©cification ouverte dâobservabilitĂ©).
Trace corr-a1b9f3 (durée totale : 42,3 s)
âââ span orchestrator.run [42,3 s]
âââ span planner.plan [3,1 s] â
âââ span agent-juridique.analyze [18,2 s] â
â âââ span tool.search_registry [12,0 s] â â goulot identifiĂ©
â âââ span llm.generate [5,9 s] â
âââ span agent-fiscal.analyze [17,8 s] â TIMEOUT
â âââ span tool.tax_api [15,0 s] â â cause racine
âââ span redactor.merge [2,4 s] â (mode dĂ©gradĂ© niveau 1)
Attributs de span spĂ©cifiques aux agents LLM Ă capturer : modĂšle utilisĂ©, tokens entrĂ©e/sortie, coĂ»t estimĂ©, nom de lâoutil, dĂ©cision dâescalade, Ă©tat du disjoncteur, version du prompt systĂšme. Ces attributs transforment le traçage en outil dâoptimisation de coĂ»ts et de qualitĂ©, pas seulement de dĂ©bogage.
Question dâaudit dâarchitecture (Ă poser telle quelle en entretien de certification) : « Montrez-moi la trace complĂšte dâune demande qui a Ă©chouĂ© la semaine derniĂšre, du point dâentrĂ©e jusquâĂ la DLQ. » Si lâĂ©quipe ne peut pas, lâobservabilitĂ© est dĂ©clarative, pas rĂ©elle.
SynthĂšse : la grille de lâarchitecte
| Domaine | Question de conception | Artefact livrable |
|---|---|---|
| DĂ©composition | Verticale, horizontale, rĂ©cursive â et pourquoi ? | Diagramme dâarchitecture + justification des 3 critĂšres |
| Escalade | Quels dĂ©clencheurs, quels patrons, quels SLA ? | Politique dâescalade + matrice de risque |
| Erreurs | Que se passe-t-il quand X tombe ? (pour chaque X) | Carte des modes de panne + échelle de dégradation |
| Retry | Quels outils sont idempotents ? | SpĂ©cification dâoutils annotĂ©e |
| ObservabilitĂ© | Peut-on rejouer lâhistoire dâune demande ? | SchĂ©ma de propagation du correlation ID |
Message final aux futurs certifiĂ©s : un systĂšme multi-agents nâest pas jugĂ© sur son comportement quand tout va bien â nâimporte quelle dĂ©mo y arrive. Il est jugĂ© sur son comportement quand un outil timeout, quand un agent hallucine, quand un humain ne rĂ©pond pas. La certification Ă©value prĂ©cisĂ©ment cela : lâarchitecture des mauvais jours.
Notes pour lâinstructeur
- DĂ©mo interactive (webpage/index.html) : utilisez le simulateur de circuit breaker en direct â basculez les Ă©checs et faites constater les transitions dâĂ©tats par la classe. Câest le concept le plus abstrait de la session ; la simulation le rend tangible.
- Erreur frĂ©quente des participants : confondre queue-for-review (lâagent finit, lâhumain valide aprĂšs) et pause-and-ask (lâagent sâarrĂȘte, lâhumain dĂ©bloque pendant). Insistez avec un exemple chronologique.
- Lien certification : les chapitres 8â10 pĂšsent lourd dans lâexamen Claude Certified Architect â (pondĂ©ration susceptible dâĂ©voluer â vĂ©rifier le syllabus officiel en vigueur). Les questions croisent souvent deux chapitres (ex. : « un sous-agent Ă©choue 5 fois : circuit breaker ou escalade ? » â rĂ©ponse : les deux, dans cet ordre).
- Timing : si le temps manque, sacrifiez le §3.2c (rollback partiel/Saga) plutĂŽt que lâobservabilitĂ© â le traçage est systĂ©matiquement Ă©valuĂ©.