Appeler l'API avec les Tools
Mis à jour le 29 juillet 2026
Envoyer les tools à l’API Mistral
Vous avez défini vos fonctions et leurs spécifications JSON. Il faut maintenant envoyer tout cela à l’API Mistral et, surtout, comprendre ce que le modèle renvoie. Nous sommes aux étapes 2 et 3 du processus : le modèle reçoit la requête utilisateur accompagnée des outils, et produit un appel de fonction — ou refuse d’en produire un, ce qui est tout aussi instructif.
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 contient un objet message qui peut prendre deux formes, et votre code doit être écrit pour les deux dès le premier jour. Dans le premier cas, le modèle répond directement parce qu’aucune fonction n’est nécessaire : content porte du texte, tool_calls vaut None.
# 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 ?"
Dans le second, le modèle veut appeler une fonction. La situation s’inverse : content est vide ou nul, et tool_calls porte la charge utile.
# 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}")
Trois informations sortent de ce bloc, et la troisième est celle qu’on oublie. Le nom de la fonction sert au dispatch, les arguments parsés serviront à l’appel, mais le tool_call_id est ce qui permettra au modèle d’associer votre résultat au bon appel lorsque vous le lui renverrez. Perdez-le, et le tour de conversation devient irrécupérable.
Quand le modèle demande des informations manquantes
Il arrive que l’utilisateur ne fournisse pas assez d’informations. Le modèle a alors une porte de sortie élégante : ne pas appeler de fonction du tout et 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 ?"
Ce comportement est exactement celui que vous recherchez. Le modèle sait que transaction_id figure dans required ; plutôt que d’inventer un identifiant plausible et de déclencher une requête sur des données inexistantes, il rend la main à l’utilisateur. C’est aussi la meilleure démonstration de l’utilité du champ required vu à la leçon précédente.
Gestion du message système
Le message système vous permet de cadrer ce comportement plutôt que de l’espérer.
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 ?"
}
]
Lisez-le ligne à ligne : la première phrase fixe le rôle, la deuxième le ton et la langue de l’assistant, la troisième précise quand recourir aux fonctions, la dernière donne une instruction explicite sur un cas limite — l’information manquante. Ce sont les trois usages pour lesquels un message système est recommandé, et ils suffisent la plupart du temps.
Code complet de l’étape 2-3
Voici le code complet et réutilisable pour envoyer une requête avec des tools. Il encapsule tout ce qui précède et normalise la réponse en un dictionnaire à deux formes, ce qui vous évitera de disperser des if message.tool_calls dans toute votre application.
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
}
Remarquez que la compréhension de liste itère sur tous les tool_calls, et non sur le premier seulement. Le modèle peut en produire plusieurs d’un coup ; une leçon ultérieure y sera consacrée, mais votre fonction utilitaire est déjà prête à les recevoir.
Points clés à retenir
- Envoyez les
toolset lesmessagesàclient.chat.complete()pour activer le function calling - Vérifiez toujours si
message.tool_callsexiste avant d’essayer de parser - Le
tool_call_idest 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