Appels Parallèles de Fonctions
Quand le modèle appelle plusieurs fonctions d’un coup
Dans certaines situations, le modèle détermine qu’il a besoin de plusieurs informations simultanément pour répondre à la question de l’utilisateur. Plutôt que de faire des appels séquentiels (appeler la première fonction, attendre le résultat, appeler la deuxième), il peut générer plusieurs tool_calls dans une seule réponse.
Par exemple, si l’utilisateur demande « Quel est le statut et la date du paiement T1001 ? », le modèle peut appeler retrieve_payment_status et retrieve_payment_date en parallèle.
Comment ça fonctionne
Quand le modèle décide de faire des appels parallèles, message.tool_calls contient plusieurs éléments :
response = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools
)
message = response.choices[0].message
# Plusieurs tool_calls dans la même réponse
for tc in message.tool_calls:
print(f"Fonction : {tc.function.name}")
print(f"Arguments : {tc.function.arguments}")
print(f"ID : {tc.id}")
print("---")
# Sortie possible :
# Fonction : retrieve_payment_status
# Arguments : {"transaction_id": "T1001"}
# ID : call_abc123
# ---
# Fonction : retrieve_payment_date
# Arguments : {"transaction_id": "T1001"}
# ID : call_def456
Traiter les appels parallèles
Vous devez exécuter chaque fonction et renvoyer chaque résultat avec le bon tool_call_id :
message = response.choices[0].message
messages.append(message)
# Exécuter toutes les fonctions demandées
for tool_call in message.tool_calls:
function_name = tool_call.function.name
function_params = json.loads(tool_call.function.arguments)
# Exécuter la fonction
result = available_functions[function_name](**function_params)
# Ajouter le résultat avec le bon tool_call_id
messages.append({
"role": "tool",
"name": function_name,
"content": result,
"tool_call_id": tool_call.id
})
# Renvoyer tous les résultats au modèle
final_response = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools
)
print(final_response.choices[0].message.content)
# → "Le paiement T1001 a été effectué le 15 mars 2026 et son statut est : payé."
Chaque message tool est associé à un tool_call_id unique. Le modèle utilise ces identifiants pour faire correspondre chaque résultat au bon appel.
Exécution véritablement parallèle en Python
Dans le code ci-dessus, les fonctions sont exécutées séquentiellement dans la boucle for. Si vos fonctions font des appels réseau (API externe, base de données), vous pouvez gagner du temps avec une exécution véritablement parallèle :
import asyncio
import json
async def execute_tools_parallel(tool_calls, available_functions):
"""Exécute les tool_calls en parallèle et retourne les résultats."""
async def execute_one(tool_call):
function_name = tool_call.function.name
function_params = json.loads(tool_call.function.arguments)
# Si vos fonctions sont async :
result = await available_functions[function_name](**function_params)
return {
"role": "tool",
"name": function_name,
"content": result,
"tool_call_id": tool_call.id
}
# Exécuter toutes les fonctions en parallèle
tasks = [execute_one(tc) for tc in tool_calls]
return await asyncio.gather(*tasks)
Pour des fonctions synchrones, vous pouvez utiliser concurrent.futures :
from concurrent.futures import ThreadPoolExecutor
def execute_tools_threaded(tool_calls, available_functions):
"""Exécute les tool_calls avec un pool de threads."""
def execute_one(tool_call):
function_name = tool_call.function.name
function_params = json.loads(tool_call.function.arguments)
result = available_functions[function_name](**function_params)
return {
"role": "tool",
"name": function_name,
"content": result,
"tool_call_id": tool_call.id
}
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(execute_one, tool_calls))
return results
Contrôler les appels parallèles
Vous pouvez contrôler si le modèle a le droit de faire des appels parallèles via le paramètre parallel_tool_calls :
# Permettre les appels parallèles (par défaut)
response = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools,
parallel_tool_calls=True # Le modèle décide
)
# Forcer les appels séquentiels
response = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools,
parallel_tool_calls=False # Un seul tool_call par réponse
)
Utilisez parallel_tool_calls=False quand :
- L’ordre d’exécution des fonctions est important
- Le résultat d’une fonction est nécessaire pour appeler la suivante
- Vous voulez un contrôle plus fin sur le flux
Le pattern de flux parallèle
Comparaison des patterns :
# Séquentiel (2 appels API au modèle)
user → assistant(fc.1) → tool(r.1) → assistant(fc.2) → tool(r.2) → assistant(réponse)
# Parallèle (1 seul appel API supplémentaire)
user → assistant(fc.1, fc.2) → tool(r.1), tool(r.2) → assistant(réponse)
Le pattern parallèle est plus efficace : une seule requête au modèle suffit pour obtenir tous les tool_calls, et une seule requête supplémentaire pour la réponse finale.
Points clés à retenir
- Le modèle peut générer plusieurs tool_calls dans une seule réponse
- Chaque résultat doit être renvoyé avec le bon
tool_call_id - Pour des fonctions réseau, utilisez
asyncioouThreadPoolExecutorpour une exécution parallèle réelle - Le paramètre
parallel_tool_callscontrôle ce comportement (Truepar défaut) - Le pattern parallèle réduit le nombre de requêtes API et améliore la latence