Aller au contenu principal

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 :

  1. L’utilisateur demande : “Quel jour sera-t-on dans 103 jours ?”
  2. Claude repond avec un bloc tool_use demandant get_current_datetime
  3. Votre serveur appelle la fonction et renvoie le resultat
  4. Claude realise qu’il a besoin d’encore plus d’informations et demande add_duration_to_datetime
  5. Votre serveur appelle cette fonction et renvoie le resultat
  6. 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

AmeliorationBenefice
Gestion flexible des messagesLes 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 completsOn conserve tous les blocs, pas juste le texte
Utilitaire d’extraction de texteMoyen 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_datetime pour connaitre la date actuelle
  • Votre serveur renvoie "2024-03-11 10:22:00"

Tour 2 :

  • Claude demande add_duration_to_datetime avec 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_reminder avec 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.