Guide Formateur â Session 3 (Niveau AvancĂ©)
Claude Agent SDK : construire des systĂšmes agentiques en production
Programme : Applied AI â Yann Isola Niveau : AvancĂ© â architectes solutions prĂ©parant la certification Claude Certified Architect DurĂ©e : 2 h 00 PrĂ©requis : Sessions 1â2 (architectures agentiques, orchestration multi-agents), Python intermĂ©diaire, notions dâAPI (Application Programming Interface â interface de programmation applicative) LLM (Large Language Model â grand modĂšle de langage).
1. Objectifs pédagogiques
Ă la fin de la session, les participants sauront :
- DĂ©crire lâarchitecture du Claude Agent SDK (Software Development Kit â kit de dĂ©veloppement logiciel) :
Agent,Runner, outils, handoffs, guardrails, hooks, variables de contexte. - Implémenter un agent avec outils typés via le décorateur
@tool. - Concevoir des handoffs entre agents et justifier ce choix face Ă lâappel dâoutil classique.
- Poser des guardrails dâentrĂ©e et de sortie avec gestion des rejets.
- Orchestrer des patterns multi-agents : coordinateur + sous-agents, pipeline, exécution parallÚle.
- GĂ©rer les erreurs : Ă©chec dâoutil, Ă©chec dâagent, timeout, stratĂ©gies de repli (fallback).
Lien certification : ces six objectifs couvrent les domaines « Agent Design » et « SDK Implementation » du rĂ©fĂ©rentiel Claude Certified Architect â (rĂ©fĂ©rentiel susceptible dâĂ©voluer â vĂ©rifier la version en vigueur sur le site officiel Anthropic).
2. Déroulé minuté (120 min)
| # | Séquence | Durée | Format |
|---|---|---|---|
| 0 | Accueil + rappel session 2 | 5 min | PléniÚre |
| 1 | Anatomie du SDK : Agent, Runner, boucle agentique | 20 min | Exposé + démo live |
| 2 | Outils : @tool, schémas, docstrings |
15 min | Live coding |
| 3 | Handoffs : transfert de contrÎle entre agents | 15 min | Exposé + démo |
| â | Pause | 10 min | â |
| 4 | Guardrails et hooks | 15 min | Exposé + live coding |
| 5 | Patterns multi-agents : coordinateur, pipeline, parallÚle | 20 min | Exposé + page interactive |
| 6 | Gestion dâerreurs + anti-patterns | 10 min | ExposĂ© + discussion |
| 7 | Lancement des exercices (Ă finir en autonomie) | 8 min | Ateliers |
| 8 | SynthĂšse + quiz dâancrage | 2 min | PlĂ©niĂšre |
3. Contenu détaillé
SĂ©quence 1 â Anatomie du SDK (20 min)
3.1.1 Pourquoi un SDK dédié ?
Point de dĂ©part : rappeler quâappeler une API LLM « Ă la main » impose de réécrire soi-mĂȘme la boucle agentique (envoi du message â dĂ©tection dâappel dâoutil â exĂ©cution â renvoi du rĂ©sultat â itĂ©ration). Le Claude Agent SDK est le framework Python officiel qui industrialise cette boucle et y ajoute les briques de production : validation, observabilitĂ©, dĂ©lĂ©gation multi-agents.
Message clĂ© Ă marteler : le SDK nâest pas une abstraction magique â câest la boucle agentique de la session 1, packagĂ©e, testĂ©e et outillĂ©e.
3.1.2 La classe Agent
Un agent se définit par quatre attributs :
from claude_agent_sdk import Agent
agent_support = Agent(
name="support-client", # identifiant unique
model="claude-sonnet-4-5", # â nom de modĂšle volatile
instructions=(
"Tu es un agent de support de la société Acme. "
"Réponds en français, cite toujours la source interne utilisée. "
"Si la demande concerne un remboursement, transfĂšre Ă l'agent facturation."
),
tools=[chercher_kb, creer_ticket], # liste de fonctions décorées @tool
)
name: sert au routage, aux logs et aux handoffs.model: le modĂšle cible. â Les identifiants de modĂšles changent rĂ©guliĂšrement (versions, snapshots) â toujours vĂ©rifier la documentation.instructions: le system prompt. Insister : câest le contrat comportemental de lâagent. Tout ce qui nây figure pas est laissĂ© Ă lâinterprĂ©tation du modĂšle.tools: la liste des capacitĂ©s. Un agent sans outil nâest quâun chatbot.
Question Ă poser Ă la salle : « OĂč mettriez-vous la rĂšgle âne jamais divulguer de donnĂ©es personnellesâ : dans instructions ou dans un guardrail ? » â RĂ©ponse attendue en sĂ©quence 4 : les deux ; les instructions orientent, le guardrail garantit.
3.1.3 Le Runner : la boucle
from claude_agent_sdk import Runner
resultat = Runner.run(
agent_support,
"Mon abonnement a été facturé deux fois ce mois-ci.",
)
print(resultat.final_output)
Dérouler au tableau ce que Runner.run() fait réellement :
- Envoie le message utilisateur +
instructions+ schĂ©mas dâoutils au modĂšle. - Le modĂšle rĂ©pond : soit un texte final, soit un ou plusieurs appels dâoutils.
- Le Runner exécute les outils, renvoie leurs résultats au modÚle.
- Boucle jusquâĂ obtention dâune rĂ©ponse finale (ou dĂ©clenchement dâun handoff, ou dĂ©passement du plafond
max_turns).
Schéma à dessiner (repris dans la page interactive) :
Utilisateur â [Guardrail entrĂ©e] â Agent (modĂšle)
â
ââââ appel outil ââ€âââ handoff ââââ Autre agent
⌠â
ExĂ©cution outil âŒ
â RĂ©ponse finale
âââ rĂ©sultat âââ (boucle)
â
[Guardrail sortie] â Utilisateur
PiĂšge de certification : Runner.run() est synchrone ; Runner.run_async() (asyncio) est requis pour lâexĂ©cution parallĂšle (sĂ©quence 5). Une question type demande de choisir la bonne variante selon le scĂ©nario.
SĂ©quence 2 â Outils : @tool (15 min)
3.2.1 Le décorateur
from claude_agent_sdk import tool
@tool
def chercher_kb(requete: str, max_resultats: int = 5) -> str:
"""Recherche dans la base de connaissances interne d'Acme.
Args:
requete: termes de recherche en langage naturel.
max_resultats: nombre maximal de documents retournés.
"""
docs = kb_client.search(requete, limit=max_resultats)
return "\n---\n".join(d.snippet for d in docs)
Trois mécanismes à expliciter :
- La docstring devient la description de lâoutil envoyĂ©e au modĂšle. Câest un artefact de prompt engineering, pas un commentaire : elle doit dire quand utiliser lâoutil, pas seulement ce quâil fait.
- Les annotations de type gĂ©nĂšrent le schĂ©ma JSON (JSON â JavaScript Object Notation, format dâĂ©change de donnĂ©es) :
strâ"type": "string",intâ"type": "integer", valeurs par dĂ©faut â paramĂštres optionnels. Types complexes : utiliser Pydantic ouTypedDict. - La valeur de retour est renvoyĂ©e au modĂšle telle quelle (convertie en texte). Retourner du contenu structurĂ© et concis â pas un dump JSON de 50 Ko.
3.2.2 Bonnes pratiques (Ă dicter)
- Un outil = une responsabilité. Pas de
faire_tout(action: str). - Nommer les paramĂštres du point de vue du modĂšle (
requete, pasq). - Toujours borner :
max_resultats, timeouts, pagination. - Les erreurs métier se retournent en texte (« Aucun résultat pour⊠») ; les erreurs techniques se lÚvent en exception (gérées en séquence 6).
Exercice Ă©clair (3 min) : faire critiquer cette docstring : """Cherche des trucs.""" â attendre : pas de cas dâusage, pas de description des paramĂštres, pas de limite.
SĂ©quence 3 â Handoffs (15 min)
3.3.1 Concept
Un handoff est un transfert de contrĂŽle : lâagent A dĂ©cide que lâagent B est mieux placĂ© et lui passe la conversation. DiffĂ©rence fondamentale avec lâappel dâoutil :
| Appel dâoutil | Handoff | |
|---|---|---|
| Qui garde la main ? | Lâagent appelant | Lâagent cible |
| Retour au premier agent ? | Oui, automatique | Non (sauf handoff retour explicite) |
| Contexte transmis | Arguments de lâoutil | Historique de conversation |
| Cas dâusage | CapacitĂ© ponctuelle | Changement de spĂ©cialitĂ© |
3.3.2 Implémentation
Le SDK exprime le handoff via la liste handoffs de lâagent et, dans les signatures dâoutils de routage, via lâannotation de type de retour pointant vers un agent :
from claude_agent_sdk import Agent, handoff
agent_facturation = Agent(
name="facturation",
model="claude-sonnet-4-5", # â volatile
instructions="Tu traites remboursements et litiges de facturation. "
"Tu as accĂšs Ă l'historique complet de la conversation.",
tools=[consulter_factures, initier_remboursement],
)
agent_triage = Agent(
name="triage",
model="claude-haiku-4-5", # â volatile â modĂšle lĂ©ger pour router
instructions="Analyse la demande et route vers le bon spécialiste. "
"Ne tente JAMAIS de rĂ©soudre toi-mĂȘme.",
handoffs=[handoff(agent_facturation), handoff(agent_support)],
)
Points Ă souligner :
- Le modÚle « voit » chaque handoff comme un pseudo-outil (
transfer_to_facturation). Câest le modĂšle qui dĂ©cide de router â dâoĂč lâimportance desinstructionsdu triage. - Lâagent cible hĂ©rite de lâhistorique : pas besoin de re-poser les questions au client.
handoff()accepte des options :on_handoff=(callback), filtre dâhistorique, message dâaccueil.
3.3.3 Anti-pattern : le sous-agent amnésique
Mauvais (Ă projeter) :
# â Le coordinateur dĂ©lĂšgue sans contexte
Runner.run(agent_redacteur, "Rédige la section 2.")
# â l'agent ne sait ni de quel document il s'agit, ni le ton, ni le plan
Bon :
# Contexte complet dans le prompt de délégation
Runner.run(agent_redacteur, f"""
Mission : rédiger la section 2 du rapport « {titre} ».
Plan global : {plan}
Sections déjà rédigées (résumé) : {resume_sections}
Ton : formel, public : direction financiĂšre. Longueur : 400â600 mots.
Livrable : Markdown uniquement, sans préambule.
""")
RĂšgle Ă faire noter : un sous-agent ne partage pas votre mĂ©moire de travail. Tout ce quâil doit savoir doit ĂȘtre dans son prompt ou dans le contexte transmis. Câest la source n° 1 dâĂ©chec des systĂšmes multi-agents en production.
SĂ©quence 4 â Guardrails et hooks (15 min)
3.4.1 Guardrails
Validateurs exécutés avant (input guardrail) ou aprÚs (output guardrail) le passage par le modÚle.
from claude_agent_sdk import input_guardrail, output_guardrail, GuardrailTripwire
@input_guardrail
def bloquer_donnees_carte(ctx, agent, message: str):
"""Rejette tout message contenant un numéro de carte bancaire."""
if re.search(r"\b\d{4}[ -]?\d{4}[ -]?\d{4}[ -]?\d{4}\b", message):
return GuardrailTripwire(
triggered=True,
message="Ne transmettez jamais de numéro de carte. "
"Utilisez le portail sécurisé.",
)
return GuardrailTripwire(triggered=False)
@output_guardrail
def verifier_pas_de_promesse(ctx, agent, sortie: str):
"""EmpĂȘche l'agent de promettre un remboursement non validĂ©."""
verdict = petit_modele_classifieur(sortie) # LLM léger en juge
return GuardrailTripwire(triggered=verdict == "promesse_engageante")
Ă expliciter :
- Un guardrail déclenché (tripwire) interrompt le run et lÚve une exception dédiée (
InputGuardrailTripwireTriggered/OutputGuardrailTripwireTriggered) que lâapplication intercepte. - Les input guardrails peuvent tourner en parallĂšle du premier appel modĂšle (optimisation latence) : si le tripwire se dĂ©clenche, lâappel est annulĂ©.
- Un guardrail peut lui-mĂȘme appeler un LLM (pattern « LLM-as-judge ») â utiliser un modĂšle rapide et peu coĂ»teux pour ne pas doubler la latence.
DĂ©fense en profondeur (schĂ©ma Ă dessiner) : instructions (souple) â guardrails (dur) â permissions dâoutils (dur) â audit via hooks (a posteriori).
3.4.2 Hooks
Callbacks de cycle de vie pour lâobservabilitĂ© et le contrĂŽle :
from claude_agent_sdk import RunHooks
class HooksAudit(RunHooks):
async def on_tool_start(self, ctx, agent, tool):
logger.info("agent=%s outil=%s args=%s", agent.name, tool.name, ctx.tool_args)
async def on_tool_end(self, ctx, agent, tool, result):
metrics.timing(f"tool.{tool.name}.latency", ctx.elapsed_ms)
async def on_handoff(self, ctx, source, cible):
logger.info("handoff %s â %s", source.name, cible.name)
resultat = Runner.run(agent_triage, message, hooks=HooksAudit())
Hooks principaux : on_agent_start, on_agent_end, on_tool_start, on_tool_end, on_handoff. Cas dâusage : logs dâaudit (conformitĂ©), mĂ©triques (latence, coĂ»t), injection de contexte, kill-switch.
Distinction certification : guardrail = contrÎle bloquant sur le contenu ; hook = observation/instrumentation du cycle de vie. Un hook ne devrait pas porter la logique de sécurité principale.
3.4.3 Variables de contexte
Ătat typĂ© partagĂ© entre agents, outils, guardrails et hooks dâun mĂȘme run â jamais envoyĂ© au modĂšle (contrairement au prompt) :
from dataclasses import dataclass
from claude_agent_sdk import Agent, Runner, RunContextWrapper
@dataclass
class ContexteClient:
client_id: str
tier: str # "standard" | "premium"
langue: str
@tool
def consulter_factures(ctx: RunContextWrapper[ContexteClient]) -> str:
"""Liste les factures du client authentifié."""
return facturation_api.factures(ctx.context.client_id) # jamais demandé au modÚle !
agent = Agent[ContexteClient](name="support", ...)
resultat = Runner.run(agent, message, context=ContexteClient("C-4812", "premium", "fr"))
Message sĂ©curitĂ© : lâidentitĂ© du client vient du contexte applicatif (session authentifiĂ©e), jamais dâun paramĂštre que le modĂšle remplit â sinon un prompt injection peut lire les factures dâautrui. Câest un grand classique dâexamen.
SĂ©quence 5 â Patterns multi-agents (20 min)
Projeter la page interactive (webpage/index.html) et dérouler le simulateur de flux.
3.5.1 Coordinateur + sous-agents
Le coordinateur dĂ©compose, dĂ©lĂšgue, agrĂšge. Dans lâenvironnement dâexĂ©cution Claude, la dĂ©lĂ©gation passe par lâoutil Task : le coordinateur doit donc lâavoir dans ses outils autorisĂ©s.
options_coordinateur = {
"allowedTools": ["Read", "Grep", "Task"], # âŹ
"Task" = droit de déléguer
"maxTurns": 40,
}
Point dâexamen : un coordinateur dont allowedTools nâinclut pas "Task" ne peut pas crĂ©er de sous-agents â il tentera de tout faire lui-mĂȘme, silencieusement. SymptĂŽme typique : « mon architecture multi-agents nâutilise quâun agent ». Cause : permission manquante, pas bug du modĂšle.
3.5.2 Pipeline
ChaĂźne sĂ©quentielle : sortie de lâagent N = entrĂ©e de lâagent N+1.
brut = Runner.run(agent_extracteur, document).final_output
analyse = Runner.run(agent_analyste, f"Données extraites :\n{brut}").final_output
rapport = Runner.run(agent_redacteur, f"Analyse :\n{analyse}\nRédige le rapport.").final_output
Avantages : chaque Ă©tape testable isolĂ©ment, modĂšles dimensionnĂ©s par Ă©tape (extracteur lĂ©ger, analyste puissant). InconvĂ©nient : latence cumulĂ©e, erreur amont propagĂ©e â dâoĂč lâintĂ©rĂȘt dâun guardrail de sortie entre les Ă©tapes.
3.5.3 ParallĂšle
TĂąches indĂ©pendantes â exĂ©cution concurrente avec asyncio :
import asyncio
from claude_agent_sdk import Runner
async def analyser_dossier(chunks: list[str]):
taches = [Runner.run_async(agent_analyste, c) for c in chunks]
resultats = await asyncio.gather(*taches, return_exceptions=True)
ok = [r.final_output for r in resultats if not isinstance(r, Exception)]
echecs = [r for r in resultats if isinstance(r, Exception)]
return ok, echecs
Ă souligner : return_exceptions=True â un Ă©chec ne doit pas annuler les Nâ1 succĂšs. Puis un agent agrĂ©gateur fusionne les ok et signale les echecs.
Arbitrage coĂ»t/latence : le parallĂšle divise la latence perçue mais multiplie les tokens consommĂ©s simultanĂ©ment (attention aux limites de dĂ©bit â rate limits â , variables selon le palier de compte).
3.5.4 Grille de choix (Ă faire recopier)
| Besoin | Pattern |
|---|---|
| SpĂ©cialitĂ©s disjointes, routage Ă lâentrĂ©e | Triage + handoffs |
| Ătapes dĂ©pendantes, transformation progressive | Pipeline |
| Sous-tùches indépendantes, volume | ParallÚle + agrégateur |
| DĂ©composition dynamique dĂ©cidĂ©e Ă lâexĂ©cution | Coordinateur + Task |
SĂ©quence 6 â Gestion dâerreurs (10 min)
Trois familles :
- Erreur dâoutil. Exception dans le code de lâoutil. Par dĂ©faut le SDK renvoie lâerreur au modĂšle, qui peut retenter ou contourner. Pour maĂźtriser le message : dĂ©corer dâun try/except et retourner un texte actionnable (« Le service factures est indisponible, rĂ©essaie dans 30 s ou informe lâutilisateur »).
- Ăchec dâagent. Boucle infinie ou dĂ©rive â borner avec
max_turns; sortie invalide â output guardrail + une relance contrĂŽlĂ©e, puis fallback (rĂ©ponse dĂ©gradĂ©e, escalade humaine). - Timeout. Toujours envelopper :
asyncio.wait_for(Runner.run_async(...), timeout=120). PrĂ©voir lâidempotence des outils Ă effet de bord (un remboursement retentĂ© ne doit pas partir deux fois â clĂ© dâidempotence).
try:
res = await asyncio.wait_for(Runner.run_async(agent, msg), timeout=120)
except asyncio.TimeoutError:
res = reponse_degradee("Analyse trop longue, version abrégée fournie.")
except OutputGuardrailTripwireTriggered:
res = escalade_humaine(msg)
Phrase de synthĂšse : en production, la question nâest pas « si » un agent Ă©choue, mais « quoi ensuite ». Une architecture certifiable dĂ©finit le comportement de chaque Ă©chec.
SĂ©quence 7 â Exercices (8 min)
Présenter les trois exercices (exercises/exercises.md) :
- Agent avec outils (
@tool, schĂ©mas, docstrings) â 45 min estimĂ©es. - Handoffs triage â spĂ©cialistes â 60 min.
- Guardrails entrĂ©e/sortie + hooks dâaudit â 60 min.
BarÚme indicatif et solutions commentées inclus dans le document exercices.
4. Matériel et logistique
- Python â„ 3.10,
pip install claude-agent-sdkâ (nom de paquet et version : vĂ©rifier la doc officielle au jour J). - ClĂ© API par participant (ou proxy mutualisĂ© de la salle) â prĂ©voir un budget tokens ; la session consomme peu (agents courts).
- Projecteur + la page
webpage/index.html(fonctionne hors-ligne). - Slides :
slides/slides.md(25+ slides, format Marp/reveal compatible).
5. PiÚges fréquents des participants
| PiĂšge | Correction Ă apporter |
|---|---|
| Docstrings dâoutils vides ou vagues | Rappeler : la docstring EST le prompt de lâoutil |
| Confusion handoff / appel dâoutil | Revenir au tableau comparatif (qui garde la main ?) |
| IdentitĂ© utilisateur passĂ©e en paramĂštre dâoutil | Variables de contexte + dĂ©monstration dâinjection |
Coordinateur sans "Task" dans allowedTools |
Faire reproduire le symptĂŽme, puis corriger |
asyncio.gather sans return_exceptions=True |
Simuler un échec sur 1 tùche parmi 5 |
| Sous-agent délégué sans contexte | Projeter le Bad vs Good de la séquence 3 |
6. Questions probables (FAQ formateur)
« Quelle différence entre guardrail et instructions systÚme ? » Les instructions influencent le modÚle (probabiliste) ; le guardrail est du code déterministe qui bloque. La conformité exige les deux.
« Peut-on faire un handoff retour ? » Oui â lâagent cible peut lister lâagent source dans ses propres handoffs. Attention aux boucles : borner avec max_turns et journaliser via on_handoff.
« Handoff ou agent-as-tool ? » Handoff = transfert dĂ©finitif de la conversation. Agent-as-tool = le coordinateur consulte un agent et garde la main. Si lâutilisateur doit continuer Ă dialoguer avec le spĂ©cialiste â handoff.
« Les variables de contexte sont-elles visibles du modĂšle ? » Non, jamais sĂ©rialisĂ©es dans le prompt. Câest prĂ©cisĂ©ment leur intĂ©rĂȘt (secrets, identifiants). Seul ce que les outils retournent atteint le modĂšle.
Fin du guide formateur â Session 3, niveau avancĂ©.