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 :
- Appeler
get_current_datetimepour obtenir la date actuelle - Appeler
add_duration_to_datetimepour ajouter 103 jours à cette date - 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_messagedevient polymorphe : chaîne, liste de blocs ou objetMessagecompletchatretourne désormais le message entier — le texte seul ne contient nistop_reasonni blocstool_usetext_from_messagefiltre les blocstextpour 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