Conversations multi-tours avec outils
Quand on construit des applications avec plusieurs outils, il faut gerer les scenarios ou Claude doit appeler successivement plusieurs outils pour repondre a une seule question. Par exemple, si un utilisateur demande “Quel jour sera-t-on dans 103 jours ?”, Claude doit d’abord obtenir la date actuelle, puis ajouter 103 jours.
Cela cree un schema de conversation multi-tours ou Claude fait plusieurs demandes d’outils avant de fournir une reponse finale. Votre application doit gerer cela automatiquement.
Le schema multi-tours
Voici ce qui se passe en coulisses quand Claude a besoin de plusieurs outils :
- L’utilisateur demande : “Quel jour sera-t-on dans 103 jours ?”
- Claude repond avec un bloc tool_use demandant
get_current_datetime - Votre serveur appelle la fonction et renvoie le resultat
- Claude realise qu’il a besoin d’encore plus d’informations et demande
add_duration_to_datetime - Votre serveur appelle cette fonction et renvoie le resultat
- Claude a maintenant assez d’informations pour fournir la reponse finale
Construire une boucle de conversation
Pour gerer ce schema, il faut une boucle qui continue tant que Claude demande des outils :
def run_conversation(messages):
while True:
response = chat(messages)
# Ajouter la reponse de Claude a l'historique
add_assistant_message(messages, response)
# Si Claude ne demande pas d'outil, on sort de la boucle
if response.stop_reason != "tool_use":
break
# Sinon, executer les outils et renvoyer les resultats
tool_result_blocks = run_tools(response)
add_user_message(messages, tool_result_blocks)
return messages
Le point cle : on verifie response.stop_reason. Si c’est "tool_use", Claude attend des resultats d’outils. Si c’est "end_turn", Claude a termine et on peut sortir de la boucle.
Refactoriser les fonctions utilitaires
Avant d’implementer la boucle de conversation, il faut mettre a jour les fonctions utilitaires pour gerer correctement les blocs multiples.
Mise a jour des gestionnaires de messages
Les fonctions add_user_message et add_assistant_message doivent maintenant accepter des objets Message complets :
from anthropic.types import Message
def add_user_message(messages, message):
user_message = {
"role": "user",
# Si c'est un objet Message, prendre son contenu ; sinon, utiliser tel quel
"content": message.content if isinstance(message, Message) else message
}
messages.append(user_message)
Cela permet de passer soit une chaine, soit une liste de blocs, soit un objet Message complet.
Mise a jour de la fonction chat
La fonction chat doit accepter une liste d’outils et retourner l’objet Message complet (pas juste le texte) :
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,
}
# Ajouter les outils seulement s'ils sont fournis
if tools:
params["tools"] = tools
if system:
params["system"] = system
message = client.messages.create(**params)
return message # Retourne l'objet Message complet
Extraire le texte des messages
Puisqu’on retourne maintenant des objets Message complets, il faut un utilitaire pour extraire le texte quand necessaire :
def text_from_message(message):
return "\n".join(
[block.text for block in message.content if block.type == "text"]
)
Cette fonction trouve tous les blocs texte dans un message et les concatene. C’est utile quand on veut afficher la reponse finale a l’utilisateur.
Resume des ameliorations
| Amelioration | Benefice |
|---|---|
| Gestion flexible des messages | Les fonctions utilitaires acceptent differents formats de messages |
| Support des outils dans chat() | La fonction chat peut recevoir et transmettre les schemas d’outils |
| Retour d’objets Message complets | On conserve tous les blocs, pas juste le texte |
| Utilitaire d’extraction de texte | Moyen facile d’obtenir du texte lisible a partir de messages complexes |
Avec ces fondations en place, la boucle de conversation gere automatiquement les appels d’outils multiples, creant une experience fluide ou Claude peut utiliser autant d’outils que necessaire pour repondre aux questions des utilisateurs.
Exercice : Tracez le flux d'un rappel complet
Simulez le flux complet pour la demande : “Programme un rappel pour mon dentiste jeudi prochain a 15h”
Tour 1 :
- Claude demande
get_current_datetimepour connaitre la date actuelle - Votre serveur renvoie
"2024-03-11 10:22:00"
Tour 2 :
- Claude demande
add_duration_to_datetimeavec la date actuelle et la duree jusqu’a jeudi prochain 15h - Votre serveur renvoie
"2024-03-14 15:00:00"
Tour 3 :
- Claude demande
set_reminderavec la date calculee et le message “Rendez-vous dentiste” - Votre serveur renvoie
"Reminder set successfully"
Reponse finale :
- Claude repond : “C’est note ! Je vous rappellerai votre rendez-vous chez le dentiste jeudi 14 mars a 15h00.”
Combien d’appels API au total ? 4 (1 initial + 3 resultats d’outils). Et stop_reason vaut "tool_use" pour les 3 premiers, puis "end_turn" pour le dernier.