Aller au contenu principal

Conversations multi-tours avec outils

Mis à jour le 30 juillet 2026

Jusqu’à présent, nos exemples d’outils tenaient en un seul échange : Claude demande un outil, on renvoie le résultat, il répond. Mais les questions réelles ne se plient pas toujours à ce schéma — certaines exigent que Claude enchaîne plusieurs outils, chacun dépendant du précédent. Cette leçon prépare le terrain : avant d’écrire la boucle multi-tours (leçon suivante), nous refactorisons nos fonctions utilitaires pour qu’elles supportent ce nouveau régime.

Le pattern multi-tour

Prenez la question « Quel jour serons-nous dans 103 jours ? ». Aucun outil ne peut y répondre seul, et Claude non plus : il ne connaît pas la date d’aujourd’hui (sa connaissance s’arrête à sa date d’entraînement), et le calcul calendaire exact n’est pas son fort. La résolution demande une séquence :

  1. Appeler get_current_datetime pour obtenir la date actuelle
  2. Appeler add_duration_to_datetime pour ajouter 103 jours à cette date
  3. Formuler la réponse finale à partir du résultat

L’étape 2 ne peut pas être planifiée d’avance : ses arguments dépendent du résultat de l’étape 1. C’est exactement la définition d’une conversation multi-tours avec outils — et c’est ce que notre code actuel, écrit pour un seul aller-retour, ne sait pas encore gérer.

Refactoriser les fonctions utilitaires

Premier chantier : add_user_message (et son pendant assistant) manipulait des chaînes de caractères. Or dans une boucle à outils, on ajoute à l’historique des objets plus variés — la réponse complète de Claude (un objet Message), ou une liste de blocs tool_result. Plutôt que de multiplier les fonctions, on rend l’utilitaire tolérant :

from anthropic.types import Message

def add_user_message(messages, message):
    user_message = {
        "role": "user",
        "content": message.content if isinstance(message, Message) else message
    }
    messages.append(user_message)

Le test isinstance(message, Message) fait le tri : si on lui passe un objet message complet, la fonction en extrait le contenu ; sinon elle prend la valeur telle quelle — chaîne simple ou liste de blocs. Ce petit polymorphisme évite d’avoir à se souvenir, à chaque appel, du format exact de ce qu’on ajoute : l’historique accepte tout ce que la conversation produit.

Mettre à jour la fonction chat

Deuxième chantier : notre fonction chat doit apprendre à transmettre les outils, et surtout à retourner la réponse complète :

def chat(messages, system=None, temperature=1.0, stop_sequences=[], tools=None):
    params = {
        "model": model,
        "max_tokens": 1000,
        "messages": messages,
        "temperature": temperature,
        "stop_sequences": stop_sequences,
    }
    if tools:
        params["tools"] = tools
    if system:
        params["system"] = system
    message = client.messages.create(**params)
    return message

Le changement décisif est à la dernière ligne : chat retourne désormais le message complet, plus seulement son texte. C’est indispensable, car la boucle multi-tours a besoin de tout ce que le texte ne contient pas — le stop_reason pour savoir si Claude attend un outil, les blocs tool_use avec leurs identifiants et leurs arguments. En résumant la réponse à son texte, notre ancienne version jetait précisément les informations qui pilotent la suite.

Extraire le texte

Retourner l’objet complet a un prix : afficher la réponse demande maintenant une étape d’extraction. On l’isole dans un utilitaire :

def text_from_message(message):
    return "\n".join(
        [block.text for block in message.content if block.type == "text"]
    )

La compréhension filtre les blocs de type text — car un message de Claude en mode outils mélange des blocs de types différents : du texte d’accompagnement (« Je vais vérifier la date… ») et des blocs tool_use. Cette fonction ne garde que la partie lisible par l’humain, en joignant les fragments s’il y en a plusieurs.

Avec ces trois ajustements — un historique tolérant aux formats, un chat qui transmet les outils et retourne tout, un extracteur de texte pour l’affichage — le code est prêt : la leçon suivante assemble la boucle qui enchaîne les tours automatiquement.

Points clés à retenir

  • Certaines questions exigent une séquence d’outils dont chaque étape dépend de la précédente
  • add_user_message devient polymorphe : chaîne, liste de blocs ou objet Message complet
  • chat retourne désormais le message entier — le texte seul ne contient ni stop_reason ni blocs tool_use
  • text_from_message filtre les blocs text pour l’affichage, car une réponse à outils mélange les types de blocs
  • Ces refactorisations sont le prérequis de la boucle multi-tours implémentée dans la leçon suivante