Applied AI · AvancĂ© 🔮 · Session 2
📝 Guide du professeur
← Retour au programme 📄 Source .md

Guide enseignant — Niveau AvancĂ©, Session 2

« Tool use avancé »

Programme : Applied AI — Yann Isola Public : Architectes de solutions prĂ©parant la certification Claude Certified Architect DurĂ©e : 2 heures PrĂ©requis : Session 1 (architecture API Messages, gestion du contexte), Python intermĂ©diaire, JSON Schema de base


Objectifs pédagogiques

À la fin de la session, chaque participant doit ĂȘtre capable de :

  1. DĂ©crire le cycle de vie complet d’un appel d’outil (tool use) : message utilisateur → bloc tool_use → exĂ©cution cĂŽtĂ© client → message tool_result → poursuite du modĂšle.
  2. Concevoir une dĂ©finition d’outil de qualitĂ© certification : nommage, description-prompt, input_schema documentĂ© champ par champ.
  3. Choisir le bon tool_choice (auto, any, tool ciblĂ©) selon le cas d’usage.
  4. Forcer une sortie structurée via le pattern du « faux outil » (fake tool).
  5. Distinguer et traiter les erreurs syntaxiques et sémantiques, y compris via is_error: true.
  6. Appliquer les patterns avancĂ©s : chaĂźnage d’outils, sĂ©lection conditionnelle, mise en cache des rĂ©sultats.
  7. Répondre aux questions de certification sur stop_reason: "tool_use" et les cas limites du flux de messages.

Plan de la session (120 min)

Bloc Durée Contenu Format
0 5 min Accueil, rappel Session 1, objectifs PléniÚre
1 20 min Le cycle de vie tool_use, pas à pas Démo live + slides
2 20 min Anatomie d’une dĂ©finition d’outil Slides + code review
3 10 min tool_choice : les trois modes Slides + mini-démo
4 15 min Sortie structurée : le pattern « faux outil » Démo live
5 15 min Gestion des erreurs (syntaxiques, sémantiques, is_error) Slides + démo
— 5 min Pause —
6 10 min Multi-outils et patterns avancés Slides
7 15 min Atelier : débogueur de flux (page web interactive) TP guidé
8 5 min Points de certification et piÚges PléniÚre

Les exercices (fichier exercises.md) sont donnĂ©s en travail dirigĂ© ou Ă  la maison selon le temps restant. Le quiz clĂŽture la session ou sert d’évaluation asynchrone.


Bloc 1 — Le cycle de vie tool_use (20 min)

Message clé à faire passer

Le modĂšle n’exĂ©cute jamais rien. Il demande une exĂ©cution en produisant un bloc de contenu tool_use. C’est VOTRE code, avec VOS identifiants et VOS permissions, qui exĂ©cute. Le modĂšle ne voit que ce que vous lui renvoyez.

C’est le point de sĂ©curitĂ© le plus important de toute la session — et une question quasi certaine en certification.

Le cycle en 5 étapes (à dessiner au tableau)

┌─────────────┐   1. RequĂȘte + tools[]        ┌─────────────┐
│             │ ────────────────────────────â–ș │             │
│  VOTRE CODE │   2. stop_reason:"tool_use"   │   MODÈLE    │
│  (client)   │ ◄──────────────────────────── │  (Claude)   │
│             │                               │             │
│  3. Vous    │   4. tool_result (role:user)  │             │
│  exĂ©cutez   │ ────────────────────────────â–ș │             │
│  l'outil    │   5. RĂ©ponse finale           │             │
│             │ ◄──────────────────────────── │             │
└─────────────┘   stop_reason:"end_turn"      └─────────────┘
  1. RequĂȘte : vous envoyez les messages + le tableau tools (dĂ©finitions).
  2. Décision du modÚle : si le modÚle veut utiliser un outil, la réponse contient un bloc tool_use et le champ stop_reason vaut "tool_use".
  3. Exécution cÎté client : votre code lit name et input du bloc, exécute la fonction correspondante.
  4. Renvoi du résultat : vous ajoutez à la conversation un message role: "user" contenant un bloc tool_result avec le tool_use_id correspondant.
  5. Poursuite : le modĂšle intĂšgre le rĂ©sultat et rĂ©pond (ou demande un autre outil — boucle).

DĂ©mo live — API brute (JSON)

Montrer la requĂȘte HTTP (Application Programming Interface — interface de programmation applicative — au format REST) :

POST https://api.anthropic.com/v1/messages
{
  "model": "claude-sonnet-4-5",
  "max_tokens": 1024,
  "tools": [
    {
      "name": "get_weather",
      "description": "RĂ©cupĂšre la mĂ©tĂ©o actuelle pour une ville donnĂ©e. Retourne la tempĂ©rature en Celsius et les conditions. À utiliser dĂšs que l'utilisateur pose une question sur la mĂ©tĂ©o actuelle ou demande s'il doit prendre un parapluie, un manteau, etc.",
      "input_schema": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string",
            "description": "Nom de la ville, en français, sans le pays. Exemple : \"Paris\", \"GenÚve\"."
          },
          "unit": {
            "type": "string",
            "enum": ["celsius", "fahrenheit"],
            "description": "Unité de température. Par défaut : celsius."
          }
        },
        "required": ["city"]
      }
    }
  ],
  "messages": [
    {"role": "user", "content": "Il fait combien Ă  Lyon ?"}
  ]
}

Réponse du modÚle (à décortiquer ligne par ligne) :

{
  "id": "msg_01AbC...",
  "role": "assistant",
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "text",
      "text": "Je vérifie la météo à Lyon."
    },
    {
      "type": "tool_use",
      "id": "toolu_01XyZ...",
      "name": "get_weather",
      "input": {"city": "Lyon", "unit": "celsius"}
    }
  ]
}

Points d’attention à verbaliser :

Puis le renvoi du résultat :

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01XyZ...",
      "content": "18°C, nuageux avec éclaircies"
    }
  ]
}

PiĂšge certification : le tool_result est envoyĂ© dans un message role: "user", PAS role: "tool" (contrairement Ă  l’API d’OpenAI). Le message assistant contenant le tool_use doit ĂȘtre renvoyĂ© tel quel dans l’historique, sinon erreur 400.

DĂ©mo live — SDK Python

SDK = Software Development Kit (kit de développement logiciel).

import anthropic

client = anthropic.Anthropic()  # clé lue dans ANTHROPIC_API_KEY

tools = [{
    "name": "get_weather",
    "description": (
        "RécupÚre la météo actuelle pour une ville donnée. "
        "Retourne la température en Celsius et les conditions. "
        "À utiliser dĂšs que l'utilisateur pose une question mĂ©tĂ©o."
    ),
    "input_schema": {
        "type": "object",
        "properties": {
            "city": {"type": "string",
                     "description": "Nom de la ville, ex. \"Paris\"."},
            "unit": {"type": "string", "enum": ["celsius", "fahrenheit"],
                     "description": "Unité. Défaut : celsius."},
        },
        "required": ["city"],
    },
}]

def get_weather(city: str, unit: str = "celsius") -> str:
    # Ici : appel à une vraie API météo, avec VOTRE clé d'API météo.
    return f"18°{'C' if unit == 'celsius' else 'F'}, nuageux"

messages = [{"role": "user", "content": "Il fait combien Ă  Lyon ?"}]

# Boucle agentique minimale
while True:
    response = client.messages.create(
        model="claude-sonnet-4-5",   # ⚠ nom de modĂšle volatile, vĂ©rifier la doc
        max_tokens=1024,
        tools=tools,
        messages=messages,
    )

    if response.stop_reason != "tool_use":
        break  # réponse finale

    # 1. Renvoyer le message assistant TEL QUEL dans l'historique
    messages.append({"role": "assistant", "content": response.content})

    # 2. Exécuter chaque bloc tool_use (il peut y en avoir plusieurs !)
    results = []
    for block in response.content:
        if block.type == "tool_use":
            if block.name == "get_weather":
                output = get_weather(**block.input)
                results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": output,
                })

    # 3. Les résultats repartent dans UN message user
    messages.append({"role": "user", "content": results})

print(response.content[0].text)

À souligner :


Bloc 2 — Anatomie d’une dĂ©finition d’outil (20 min)

La description EST un prompt

Insister lourdement : la description n’est pas de la documentation pour les humains, c’est un prompt injectĂ© dans le contexte du modĂšle. Elle conditionne :

Bonne heuristique de rédaction (3-4 phrases) :

  1. Ce que fait l’outil (une phrase, verbe d’action).
  2. Quand l’utiliser (dĂ©clencheurs explicites, y compris formulations indirectes).
  3. Quand ne PAS l’utiliser (frontiùres avec les autres outils).
  4. Ce qu’il retourne (format, unitĂ©s, cas vides).

Contre-exemple vs bon exemple (code review collective)

Mauvais :

{
  "name": "search",
  "description": "Recherche",
  "input_schema": {
    "type": "object",
    "properties": {"q": {"type": "string"}},
    "required": ["q"]
  }
}

ProblÚmes à faire identifier par le groupe : nom générique (recherche de quoi ?), description inutile, paramÚtre q non documenté (quelle syntaxe ? quelle langue ? opérateurs booléens ?).

Bon :

{
  "name": "search_client_contracts",
  "description": "Recherche en texte intĂ©gral dans la base des contrats clients signĂ©s. À utiliser quand l'utilisateur mentionne un contrat, une clause, un client ou une Ă©chĂ©ance contractuelle. Ne couvre PAS les devis ni les factures (utiliser search_invoices). Retourne au maximum 10 contrats avec id, titre, client et extrait pertinent ; retourne une liste vide si aucun rĂ©sultat.",
  "input_schema": {
    "type": "object",
    "properties": {
      "query": {
        "type": "string",
        "description": "Termes de recherche en langage naturel. Pas d'opérateurs booléens. Exemple : \"clause de résiliation Acme 2025\"."
      },
      "client_id": {
        "type": "string",
        "description": "Optionnel. Identifiant client (format CLI-XXXX) pour restreindre la recherche Ă  un seul client."
      },
      "max_results": {
        "type": "integer",
        "minimum": 1,
        "maximum": 10,
        "description": "Nombre maximal de résultats. Défaut : 5."
      }
    },
    "required": ["query"]
  }
}

Principes de conception (Ă  retenir pour la certification)

  1. Responsabilité unique : un outil = une action. Pas de manage_database(action, ...) fourre-tout : le modÚle se trompe plus souvent sur les outils polymorphes.
  2. Nommage clair : snake_case, verbe + objet (create_invoice, get_user_profile). Le nom seul doit suffire Ă  deviner la fonction.
  3. Documenter les cas limites : que se passe-t-il si vide, si trop de rĂ©sultats, si l’entitĂ© n’existe pas ? Le modĂšle gĂšre mieux ce qu’il anticipe.
  4. Minimum de champs required : chaque champ obligatoire est une occasion d’erreur ou d’hallucination de valeur. Rendre optionnel tout ce qui a un dĂ©faut raisonnable — et documenter ce dĂ©faut.
  5. enum et contraintes JSON Schema (minimum, maximum, format) plutÎt que des consignes en prose : le schéma contraint mieux que la description.

JSON Schema — rappel express

JSON Schema (JavaScript Object Notation Schema — schĂ©ma de description de donnĂ©es JSON) : input_schema doit ĂȘtre un objet ("type": "object") au niveau racine. Sous-ensemble utile en certification : type, properties, required, enum, description, items (tableaux), objets imbriquĂ©s, minimum/maximum, format (indicatif, non strictement validĂ©).


Bloc 3 — tool_choice : les trois modes (10 min)

Mode Syntaxe Comportement Cas d’usage
auto (défaut) {"type": "auto"} Le modÚle décide : outil ou réponse texte Assistant conversationnel général
any {"type": "any"} Le modùle DOIT appeler un outil, mais choisit lequel Routeur d’intentions, dispatch
tool {"type": "tool", "name": "extract_data"} Le modÚle DOIT appeler CET outil Extraction structurée, pipeline déterministe

Points de certification :

Mini-dĂ©mo : mĂȘme question (« Analyse ce ticket support ») lancĂ©e avec auto puis tool forcĂ© sur un outil d’extraction, comparer les sorties.


Bloc 4 — Sortie structurĂ©e : le pattern « faux outil » (15 min)

Le problĂšme

Vous voulez du JSON garanti et conforme Ă  un schĂ©ma (pour insĂ©rer en base, alimenter un pipeline
). Demander « rĂ©ponds en JSON » dans le prompt fonctionne souvent, mais sans garantie : texte parasite autour, champ manquant, clĂ© renommĂ©e.

La solution

DĂ©finir un outil qui n’exĂ©cute rien — il ne sert qu’à contraindre la forme de la sortie — et forcer son appel avec tool_choice.

extraction_tool = {
    "name": "record_ticket_analysis",
    "description": "Enregistre l'analyse structurée d'un ticket de support client.",
    "input_schema": {
        "type": "object",
        "properties": {
            "sentiment": {
                "type": "string",
                "enum": ["positif", "neutre", "negatif"],
                "description": "Sentiment global du client."
            },
            "urgence": {
                "type": "integer", "minimum": 1, "maximum": 5,
                "description": "Urgence de 1 (faible) Ă  5 (critique)."
            },
            "categorie": {
                "type": "string",
                "enum": ["facturation", "technique", "commercial", "autre"],
                "description": "Catégorie principale du ticket."
            },
            "resume": {
                "type": "string",
                "description": "Résumé en une phrase, max 25 mots."
            }
        },
        "required": ["sentiment", "urgence", "categorie", "resume"]
    }
}

response = client.messages.create(
    model="claude-sonnet-4-5",  # ⚠ volatile
    max_tokens=1024,
    tools=[extraction_tool],
    tool_choice={"type": "tool", "name": "record_ticket_analysis"},
    messages=[{"role": "user", "content": f"Analyse ce ticket :\n{ticket}"}],
)

data = next(b for b in response.content if b.type == "tool_use").input
# data est un dict Python déjà parsé : {"sentiment": "negatif", "urgence": 4, ...}

À souligner :


Bloc 5 — Gestion des erreurs (15 min)

Taxonomie : syntaxique vs sémantique

Type Définition Exemple RemÚde
Syntaxique La sortie viole le format attendu JSON tronqué (max_tokens trop bas), champ requis absent, type incorrect Réessayer (retry), augmenter max_tokens, valider puis re-demander
Sémantique Le format est valide mais le contenu est faux Mauvaise catégorie, ville hallucinée, montant erroné Affiner le prompt : meilleure description, exemples, enum plus stricts, découpage de la tùche

RĂšgle mnĂ©motechnique : syntaxique → retry ; sĂ©mantique → réécriture. Un retry sur une erreur sĂ©mantique redonnera souvent la mĂȘme erreur (le modĂšle est cohĂ©rent avec sa comprĂ©hension) ; réécrire le prompt sur une erreur syntaxique traite le symptĂŽme, pas la cause (souvent max_tokens ou schĂ©ma trop complexe).

Cause frĂ©quente d’erreur syntaxique Ă  connaĂźtre pour la certification : stop_reason: "max_tokens" au milieu d’un bloc tool_use → le JSON d’input est tronquĂ©. DĂ©tection : vĂ©rifier stop_reason AVANT de parser.

Renvoyer une erreur au modĂšle : is_error: true

Quand VOTRE outil Ă©choue (exception, 404, timeout), ne cassez pas la boucle : renvoyez l’erreur au modĂšle, il sait souvent se rattraper (reformuler, changer de paramĂštre, essayer un autre outil, ou expliquer l’échec Ă  l’utilisateur).

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01XyZ...",
      "content": "Erreur : ville \"Lyno\" introuvable. Villes proches : Lyon, Lens.",
      "is_error": true
    }
  ]
}

En Python :

try:
    output = get_weather(**block.input)
    result = {"type": "tool_result", "tool_use_id": block.id,
              "content": output}
except CityNotFoundError as e:
    result = {"type": "tool_result", "tool_use_id": block.id,
              "content": f"Erreur : {e}. Vérifie l'orthographe de la ville.",
              "is_error": True}

Bonnes pratiques du message d’erreur :

Anecdote d’architecte

Un pattern robuste en production : valider block.input avec Pydantic (bibliothĂšque Python de validation de donnĂ©es) AVANT d’exĂ©cuter, et renvoyer les erreurs de validation en is_error: true. Le modĂšle corrige ses paramĂštres au tour suivant dans la grande majoritĂ© des cas.


Bloc 6 — Multi-outils et patterns avancĂ©s (10 min)

Plusieurs outils dans un tour

Le modĂšle peut Ă©mettre plusieurs blocs tool_use dans une mĂȘme rĂ©ponse (appels parallĂšles) ou enchaĂźner sur plusieurs tours (appels sĂ©quentiels). Votre code doit :

Trois patterns d’architecture

  1. ChaĂźnage d’outils (tool chaining) : la sortie de l’outil A nourrit l’entrĂ©e de l’outil B, orchestrĂ© par le modĂšle sur plusieurs tours. Exemple : search_client → get_client_contracts → summarize_contract. Conception : faire en sorte que la sortie de A contienne exactement les identifiants dont B a besoin (ex. A retourne client_id, B prend client_id en paramĂštre).
  2. SĂ©lection conditionnelle : exposer des outils diffĂ©rents selon l’état de la session (utilisateur authentifiĂ© ou non, module actif
). Le tableau tools est envoyĂ© À CHAQUE requĂȘte : vous pouvez le faire varier dynamiquement. C’est un contrĂŽle d’accĂšs cĂŽtĂ© client, plus fiable que « ne l’utilise pas » en prompt.
  3. Cache de rĂ©sultats (tool result caching) : mĂ©moĂŻser cĂŽtĂ© client les appels identiques (mĂȘme outil, mĂȘmes paramĂštres) pendant une session. Économise latence et coĂ»ts d’API externes. Attention Ă  l’invalidation (TTL — Time To Live, durĂ©e de vie — courte pour les donnĂ©es volatiles). Ne pas confondre avec le prompt caching de l’API, qui cache le prĂ©fixe du contexte (dont les dĂ©finitions d’outils) cĂŽtĂ© Anthropic.

SĂ©curitĂ© — le point non nĂ©gociable


Bloc 7 — Atelier : dĂ©bogueur de flux (15 min)

Ouvrir webpage/index.html (fonctionne hors ligne). Deux modules :

  1. DĂ©bogueur de flux tool_use : les participants avancent pas Ă  pas dans une conversation complĂšte (requĂȘte → tool_use → exĂ©cution → tool_result → rĂ©ponse finale), voient le JSON exact Ă  chaque Ă©tape, et peuvent injecter des erreurs (JSON tronquĂ©, tool_use_id incorrect, erreur d’exĂ©cution) pour observer le traitement attendu.
  2. Validateur de schĂ©ma d’outil : coller une dĂ©finition d’outil, obtenir une validation contre la spĂ©cification + suggestions de qualitĂ© (description trop courte, champs non documentĂ©s, etc.).

Consigne d’atelier : chaque binĂŽme doit (a) dĂ©rouler le scĂ©nario nominal, (b) injecter les 3 erreurs et noter pour chacune si elle est syntaxique ou sĂ©mantique et le remĂšde, © faire passer au validateur le schĂ©ma de l’exercice 1 s’il est dĂ©jĂ  rĂ©digĂ©.


Bloc 8 — Points de certification et piùges (5 min)

Check-list à faire réciter :


Matériel et logistique

Erreurs fréquentes des participants (retours de terrain)

  1. Oublier de renvoyer le message assistant dans l’historique → erreur 400 incomprise. Faire reproduire l’erreur volontairement, c’est le meilleur vaccin.
  2. Chercher response.content[0] au lieu d’itĂ©rer sur les blocs.
  3. Confondre is_error: true (erreur d’exĂ©cution renvoyĂ©e AU modĂšle) avec une erreur HTTP de l’API Anthropic.
  4. Écrire des descriptions d’outils pour des humains (« Cette fonction permet de
 ») au lieu de prompts opĂ©rationnels.
  5. Mettre tous les champs en required « par sĂ©curitĂ© » — c’est l’inverse d’une sĂ©curitĂ©.