Aller au contenu principal

Chatbot avec plusieurs outils

Mis à jour le 29 juillet 2026

Objectifs

  • Construire un chatbot complet avec plusieurs outils
  • Gérer une conversation multi-tours avec des appels d’outils
  • Implémenter un agent conversationnel autonome

Architecture du chatbot

Tout ce que vous avez appris jusqu’ici converge dans cette leçon : un assistant conversationnel qui dispose de plusieurs outils, garde la mémoire de l’échange et décide tour après tour de ce dont il a besoin. Son fonctionnement repose sur deux boucles imbriquées. La boucle externe est celle du dialogue : l’utilisateur pose une question, l’assistant finit par répondre, et l’on recommence. La boucle interne est celle des outils : Claude décide s’il a besoin d’un outil, on l’exécute, on lui renvoie le résultat, et il enchaîne soit sur une nouvelle demande d’outil, soit sur sa réponse. Ce n’est qu’en sortant de cette boucle interne, quand le modèle produit enfin du texte, que la réponse s’affiche et que la main revient à l’utilisateur.

Définir plusieurs outils

Notre assistant personnel disposera de trois capacités : la météo, l’heure et le calcul. Les fonctions restent volontairement simples — la météo est simulée par un dictionnaire — car l’objet de la leçon est l’orchestration, pas l’intégration d’API réelles. Observez tout de même deux détails. La fonction météo renvoie du JSON sérialisé plutôt qu’un dictionnaire Python, parce que le champ content d’un tool_result attend du texte. Et la fonction de calcul s’appuie sur une évaluation d’expression directe : acceptable dans un notebook d’apprentissage, c’est une faille d’exécution de code arbitraire en production, où l’on emploie un évaluateur restreint. Le commentaire dans le code vous le rappelle.

import json
from datetime import datetime

# Fonctions des outils
def get_weather(city):
    """Simule une API météo."""
    weather_data = {
        "Paris": {"temp": 18, "condition": "Nuageux"},
        "Lyon": {"temp": 22, "condition": "Ensoleille"},
        "Marseille": {"temp": 25, "condition": "Ensoleille"},
    }
    data = weather_data.get(city, {"temp": 15, "condition": "Inconnu"})
    return json.dumps(data, ensure_ascii=False)

def get_time(timezone="Europe/Paris"):
    """Retourne l'heure actuelle."""
    return datetime.now().strftime("%H:%M:%S")

def calculate(expression):
    """Évalue une expression mathematique."""
    try:
        result = eval(expression)  # Attention : eval est dangereux en production
        return str(result)
    except Exception as e:
        return f"Erreur : {e}"

# Definitions des outils
tools = [
    {
        "name": "get_weather",
        "description": "Recupere la meteo actuelle d'une ville francaise.",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "Le nom de la ville"
                }
            },
            "required": ["city"]
        }
    },
    {
        "name": "get_time",
        "description": "Retourne l'heure actuelle.",
        "input_schema": {
            "type": "object",
            "properties": {
                "timezone": {
                    "type": "string",
                    "description": "Le fuseau horaire (défaut: Europe/Paris)"
                }
            },
            "required": []
        }
    },
    {
        "name": "calculate",
        "description": "Évalue une expression mathematique.",
        "input_schema": {
            "type": "object",
            "properties": {
                "expression": {
                    "type": "string",
                    "description": "L'expression mathematique a evaluer (ex: '2 + 3 * 4')"
                }
            },
            "required": ["expression"]
        }
    }
]

# Mapping nom -> fonction
tool_functions = {
    "get_weather": lambda **kwargs: get_weather(kwargs["city"]),
    "get_time": lambda **kwargs: get_time(kwargs.get("timezone", "Europe/Paris")),
    "calculate": lambda **kwargs: calculate(kwargs["expression"]),
}

Le dictionnaire tool_functions mérite une mention particulière. Il fait le pont entre le nom que Claude renvoie et la fonction à exécuter, et il absorbe au passage les paramètres optionnels : get_time fonctionne même si le modèle omet le fuseau horaire, puisque le lambda fournit la valeur de repli.

La boucle du chatbot

Le code ci-dessous assemble le tout. Le system prompt annonce les capacités disponibles et rappelle de ne recourir aux outils que lorsque c’est pertinent — sans cette consigne, un assistant bien équipé a tendance à sur-solliciter ses outils. La liste conversation accumule strictement tous les messages : questions de l’utilisateur, réponses de l’assistant, blocs tool_use et blocs tool_result. Toute amputation de cet historique brise l’appariement des identifiants et fait échouer la requête suivante. Quant à la ligne d’affichage [Outil : ...], elle n’a aucun rôle fonctionnel, mais elle rend le comportement de l’agent lisible pendant la mise au point : conservez-la, elle vous fera gagner du temps.

from anthropic import Anthropic

client = Anthropic()

def chatbot():
    """Chatbot interactif avec outils."""
    conversation = []
    system_prompt = """Vous etes un assistant personnel utile.
Vous avez acces a des outils pour la meteo, l'heure et les calculs.
Utilisez-les quand c'est pertinent, sinon repondez directement.
Repondez en francais."""

    print("Chatbot demarre ! Tapez 'quit' pour quitter.\n")

    while True:
        user_input = input("Vous : ")
        if user_input.lower() == "quit":
            print("Au revoir !")
            break

        # Ajouter le message utilisateur
        conversation.append({"role": "user", "content": user_input})

        # Envoyer a Claude
        response = client.messages.create(
            model="claude-sonnet-5",
            max_tokens=4096,
            system=system_prompt,
            tools=tools,
            messages=conversation
        )

        # Boucle d'outils : tant que Claude veut utiliser un outil
        while response.stop_reason == "tool_use":
            # Ajouter la réponse de Claude
            conversation.append({
                "role": "assistant",
                "content": response.content
            })

            # Exécuter chaque outil demande
            tool_results = []
            for block in response.content:
                if block.type == "tool_use":
                    print(f"  [Outil : {block.name}({block.input})]")
                    func = tool_functions[block.name]
                    result = func(**block.input)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": result
                    })

            # Renvoyer les résultats
            conversation.append({
                "role": "user",
                "content": tool_results
            })

            # Nouvelle requête
            response = client.messages.create(
                model="claude-sonnet-5",
                max_tokens=4096,
                system=system_prompt,
                tools=tools,
                messages=conversation
            )

        # Afficher la réponse finale
        assistant_text = ""
        for block in response.content:
            if hasattr(block, "text"):
                assistant_text += block.text

        print(f"Claude : {assistant_text}\n")
        conversation.append({
            "role": "assistant",
            "content": response.content
        })

# Lancer le chatbot
chatbot()

Exemple de conversation

Une session type fait apparaître les trois comportements que vous devez savoir reconnaître.

Vous : Quelle est la meteo a Paris et combien font 15% de 2500 ?
  [Outil : get_weather({"city": "Paris"})]
  [Outil : calculate({"expression": "2500 * 0.15"})]
Claude : Il fait actuellement 18 degres a Paris avec un ciel nuageux.
         Et 15% de 2500 font 375.

Vous : Merci ! Quelle heure est-il ?
  [Outil : get_time({})]
Claude : Il est actuellement 14:32:45.

Vous : Qui a ecrit Les Miserables ?
Claude : Les Miserables a ete ecrit par Victor Hugo, publie en 1862.

La première question déclenche deux outils en parallèle dans un seul tour, puisque les deux informations sont indépendantes l’une de l’autre. La deuxième illustre l’appel d’un outil sans paramètre obligatoire. Pour la dernière, enfin, Claude répond directement sans utiliser d’outil, car aucun n’est nécessaire — c’est exactement le comportement recherché.

Points clés à retenir

  1. La boucle d’outils : continuez à faire des requêtes tant que stop_reason == "tool_use"
  2. Historique complet : gardez l’intégralité de la conversation (messages + tool_use + tool_result)
  3. Outils multiples dans une réponse : Claude peut demander plusieurs outils simultanément
  4. Choix intelligent : Claude n’utilise les outils que quand c’est pertinent
  5. Réponse finale : quand Claude a toutes les infos, il formule une réponse textuelle naturelle

Prolongement : un quatrième outil

Ajoutez au chatbot un quatrième outil, search_product, qui interroge un catalogue fictif, afin qu’il puisse traiter une demande comme « Est-ce que vous avez des écouteurs Bluetooth à moins de 50 euros ? ». La difficulté tient à la cohabitation d’un critère textuel et d’un critère numérique dans le même schéma.

Voir un indice

Définissez un outil avec deux paramètres : query (string) et max_price (number, optionnel). La fonction peut retourner une liste de produits fictifs filtrés par prix.