Aller au contenu principal

Appeler l'API avec les Tools

Envoyer les tools à l’API Mistral

Vous avez défini vos fonctions et leurs spécifications JSON. Maintenant, il faut envoyer tout cela à l’API Mistral et comprendre ce que le modèle retourne. C’est l’étape 2-3 du processus : le modèle reçoit la requête utilisateur avec les outils et génère un appel de fonction.

La première requête

import os
import json
from mistralai import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

# Les outils définis dans la leçon précédente
tools = [
    {
        "type": "function",
        "function": {
            "name": "retrieve_payment_status",
            "description": "Récupère le statut d'un paiement à partir de l'identifiant de transaction.",
            "parameters": {
                "type": "object",
                "properties": {
                    "transaction_id": {
                        "type": "string",
                        "description": "Identifiant unique de la transaction (ex: T1001)"
                    }
                },
                "required": ["transaction_id"]
            }
        }
    }
]

# Message de l'utilisateur
messages = [
    {"role": "user", "content": "Quel est le statut du paiement T1001 ?"}
]

# Appel à l'API avec les tools
response = client.chat.complete(
    model="mistral-large-latest",
    messages=messages,
    tools=tools
)

Comprendre la réponse du modèle

La réponse du modèle contient un objet message qui peut avoir deux formes :

Cas 1 — Le modèle répond directement (pas besoin de fonction) :

# response.choices[0].message.content contient la réponse texte
# response.choices[0].message.tool_calls est None
print(response.choices[0].message.content)
# → "Bonjour ! Comment puis-je vous aider ?"

Cas 2 — Le modèle veut appeler une fonction :

# response.choices[0].message.content est None ou vide
# response.choices[0].message.tool_calls contient les appels
message = response.choices[0].message

if message.tool_calls:
    for tool_call in message.tool_calls:
        print(f"Fonction : {tool_call.function.name}")
        print(f"Arguments : {tool_call.function.arguments}")
        print(f"ID : {tool_call.id}")

Sortie typique :

Fonction : retrieve_payment_status
Arguments : {"transaction_id": "T1001"}
ID : call_abc123xyz

Parser la réponse et extraire les tool_calls

Voici le code complet pour extraire proprement les informations d’un tool_call :

message = response.choices[0].message

if message.tool_calls and len(message.tool_calls) > 0:
    tool_call = message.tool_calls[0]

    # Extraire le nom de la fonction
    function_name = tool_call.function.name

    # Parser les arguments JSON en dictionnaire Python
    function_params = json.loads(tool_call.function.arguments)

    # Récupérer l'identifiant unique de l'appel
    tool_call_id = tool_call.id

    print(f"Le modèle veut appeler : {function_name}")
    print(f"Avec les paramètres : {function_params}")
    print(f"ID de l'appel : {tool_call_id}")
else:
    # Le modèle a répondu directement
    print(f"Réponse : {message.content}")

Le tool_call_id est crucial — vous devrez le renvoyer avec le résultat pour que le modèle puisse associer le résultat au bon appel de fonction.

Quand le modèle demande des informations manquantes

Si l’utilisateur ne fournit pas assez d’informations, le modèle peut choisir de ne pas appeler de fonction et de poser une question clarificatrice :

messages = [
    {"role": "user", "content": "Quel est le statut de mon paiement ?"}
]

response = client.chat.complete(
    model="mistral-large-latest",
    messages=messages,
    tools=tools
)

message = response.choices[0].message
print(message.content)
# → "J'ai besoin de l'identifiant de transaction pour vérifier le statut.
#     Pourriez-vous me fournir le numéro de transaction ?"

C’est un comportement souhaitable : le modèle sait que transaction_id est requis et demande l’information plutôt que d’inventer un identifiant.

Gestion du message système

Un message système permet de donner du contexte au modèle et de guider son comportement :

messages = [
    {
        "role": "system",
        "content": "Vous êtes un assistant de suivi de paiements. "
                   "Répondez en français. "
                   "Utilisez les outils disponibles pour répondre aux questions "
                   "sur les transactions. Si l'utilisateur ne fournit pas "
                   "l'identifiant de transaction, demandez-le poliment."
    },
    {
        "role": "user",
        "content": "Quel est le statut du paiement T1001 ?"
    }
]

Le message système est recommandé pour :

  • Définir le ton et la langue de l’assistant
  • Préciser quand utiliser les fonctions
  • Donner des instructions spécifiques sur la gestion des erreurs

Code complet de l’étape 2-3

Voici le code complet et réutilisable pour envoyer une requête avec des tools :

import os
import json
from mistralai import Mistral

def call_mistral_with_tools(
    messages: list,
    tools: list,
    model: str = "mistral-large-latest",
    tool_choice: str = "auto"
) -> dict:
    """Envoie une requête à Mistral avec des tools et retourne la réponse parsée."""
    client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

    response = client.chat.complete(
        model=model,
        messages=messages,
        tools=tools,
        tool_choice=tool_choice
    )

    message = response.choices[0].message

    if message.tool_calls and len(message.tool_calls) > 0:
        return {
            "type": "tool_call",
            "message": message,
            "tool_calls": [
                {
                    "id": tc.id,
                    "function_name": tc.function.name,
                    "arguments": json.loads(tc.function.arguments)
                }
                for tc in message.tool_calls
            ]
        }

    return {
        "type": "text",
        "message": message,
        "content": message.content
    }

Points clés à retenir

  • Envoyez les tools et les messages à client.chat.complete() pour activer le function calling
  • Vérifiez toujours si message.tool_calls existe avant d’essayer de parser
  • Le tool_call_id est obligatoire pour renvoyer le résultat au modèle
  • Les arguments sont une chaîne JSON qu’il faut parser avec json.loads()
  • Le modèle peut choisir de répondre directement s’il n’a pas besoin de fonction ou s’il manque des informations