Appels parallèles
Plusieurs outils en une seule réponse
L’une des fonctionnalités les plus efficaces du function calling Grok est la capacité à retourner plusieurs appels d’outils dans une seule réponse. Plutôt que de faire un aller-retour pour chaque fonction, le modèle identifie toutes les informations dont il a besoin et génère tous les appels en parallèle.
Le paramètre parallel_tool_calls
Les appels parallèles sont activés par défaut. Le paramètre parallel_tool_calls contrôle ce comportement :
# Activé par défaut (pas besoin de le spécifier)
response = client.chat.completions.create(
model="grok-3",
messages=messages,
tools=tools,
parallel_tool_calls=True # C'est le défaut
)
Quand un utilisateur demande « Quel temps fait-il à Paris et à Lyon ? », le modèle retourne deux tool_calls dans la même réponse :
{
"tool_calls": [
{
"id": "call_001",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"Paris\"}"
}
},
{
"id": "call_002",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"Lyon\"}"
}
}
]
}
Traiter les appels parallèles
Votre code doit gérer un tableau de tool_calls, pas un seul :
import json
import asyncio
message = response.choices[0].message
if message.tool_calls:
# Exécuter toutes les fonctions
tool_results = []
for tool_call in message.tool_calls:
args = json.loads(tool_call.function.arguments)
result = execute_function(tool_call.function.name, args)
tool_results.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
})
# Renvoyer TOUS les résultats au modèle
messages = [
{"role": "user", "content": "Quel temps à Paris et Lyon ?"},
message,
*tool_results # Tous les résultats
]
final = client.chat.completions.create(
model="grok-3",
messages=messages,
tools=tools
)
Point critique : vous devez renvoyer tous les résultats d’outils, pas seulement le premier. Chaque résultat est lié à son tool_call_id correspondant.
Exécution réellement parallèle côté client
Le modèle retourne les appels en parallèle, mais c’est votre code qui doit les exécuter en parallèle. En Python, utilisez asyncio ou concurrent.futures :
import concurrent.futures
def execute_parallel(tool_calls):
results = []
with concurrent.futures.ThreadPoolExecutor() as executor:
futures = {}
for tc in tool_calls:
args = json.loads(tc.function.arguments)
future = executor.submit(execute_function, tc.function.name, args)
futures[future] = tc.id
for future in concurrent.futures.as_completed(futures):
call_id = futures[future]
result = future.result()
results.append({
"role": "tool",
"tool_call_id": call_id,
"content": json.dumps(result)
})
return results
L’exécution parallèle réduit considérablement la latence totale. Si chaque appel API prend 200ms, deux appels séquentiels = 400ms, mais deux appels parallèles = ~200ms.
Désactiver les appels parallèles
Dans certains cas, vous préférez des appels séquentiels :
response = client.chat.completions.create(
model="grok-3",
messages=messages,
tools=tools,
parallel_tool_calls=False
)
Quand désactiver le parallélisme :
- Dépendances entre appels : le résultat d’un outil est nécessaire pour appeler le suivant
- Rate limiting : votre API externe a des limites de requêtes simultanées
- Ordre important : les opérations doivent s’exécuter dans un ordre précis (ex: créer un utilisateur avant de lui assigner un rôle)
- Debugging : pour simplifier le suivi des appels pendant le développement
Appels parallèles avec des outils différents
Le modèle peut aussi appeler des outils différents en parallèle :
# Requête : "Réserve une table et dis-moi le temps qu'il fera ce soir"
# Le modèle retourne :
# - tool_call_1 : book_table(restaurant="Le Petit Bistrot", time="20:00")
# - tool_call_2 : get_weather(city="Paris", time="tonight")
C’est particulièrement utile pour les assistants polyvalents qui doivent rassembler des informations de sources différentes.
Gestion des erreurs en parallèle
Quand un appel échoue mais pas les autres, renvoyez quand même tous les résultats. Le modèle gère les erreurs individuelles :
results = []
for tc in message.tool_calls:
try:
result = execute_function(tc.function.name, args)
results.append({
"role": "tool",
"tool_call_id": tc.id,
"content": json.dumps(result)
})
except Exception as e:
results.append({
"role": "tool",
"tool_call_id": tc.id,
"content": json.dumps({"error": str(e)})
})
Points clés à retenir
- Les appels parallèles sont activés par défaut (
parallel_tool_calls: true) - Le modèle retourne plusieurs
tool_callsdans une seule réponse - Vous devez renvoyer tous les résultats, chacun lié à son
tool_call_id - Implémentez l’exécution parallèle côté client pour réduire la latence
- Désactivez le parallélisme quand il y a des dépendances entre appels
- Gérez les erreurs individuellement — ne bloquez pas tout pour un échec