Appels parallèles et séquentiels
Appels paralleles et sequentiels
Le modele peut emettre plusieurs appels de fonction en une seule reponse. Comprendre la difference entre appels paralleles et sequentiels est essentiel pour concevoir des workflows performants.
Appels paralleles
Quand le modele identifie plusieurs besoins independants, il emet tous les appels en meme temps :
from openai import OpenAI
import json
import asyncio
client = OpenAI()
tools = [
{
"type": "function",
"name": "get_meteo",
"description": "Obtenir la meteo actuelle d'une ville",
"parameters": {
"type": "object",
"properties": {
"ville": {"type": "string"}
},
"required": ["ville"],
"additionalProperties": false
},
"strict": true
},
{
"type": "function",
"name": "get_taux_change",
"description": "Obtenir le taux de change entre deux devises",
"parameters": {
"type": "object",
"properties": {
"source": {"type": "string"},
"cible": {"type": "string"}
},
"required": ["source", "cible"],
"additionalProperties": false
},
"strict": true
}
]
response = client.responses.create(
model="gpt-5.3",
input="Quel temps fait-il a Paris et Berlin, "
"et quel est le taux EUR/USD ?",
tools=tools
)
# Le modele emet 3 appels en parallele
appels = [item for item in response.output if item.type == "function_call"]
print(f"Nombre d'appels : {len(appels)}")
# => Nombre d'appels : 3
Executer les appels en parallele cote client
Puisque les appels sont independants, executez-les en parallele avec asyncio :
async def executer_fonction(name: str, arguments: str) -> dict:
args = json.loads(arguments)
if name == "get_meteo":
return await api_meteo(args["ville"])
elif name == "get_taux_change":
return await api_change(args["source"], args["cible"])
async def traiter_appels_paralleles(appels):
taches = [
executer_fonction(appel.name, appel.arguments)
for appel in appels
]
resultats = await asyncio.gather(*taches)
return [
{
"type": "function_call_output",
"call_id": appel.call_id,
"output": json.dumps(resultat)
}
for appel, resultat in zip(appels, resultats)
]
# Executer
resultats = asyncio.run(traiter_appels_paralleles(appels))
# Renvoyer au modele
response_finale = client.responses.create(
model="gpt-5.3",
input=response.output + resultats,
tools=tools
)
print(response_finale.output_text)
Desactiver les appels paralleles
Certains cas necessitent un seul appel a la fois. Utilisez parallel_tool_calls :
response = client.responses.create(
model="gpt-5.3",
input="Traite ces trois commandes",
tools=tools,
parallel_tool_calls=False # Un seul appel par reponse
)
Cas d’usage pour desactiver le parallele :
- Operations qui doivent s’executer dans un ordre precis
- Fonctions avec des effets de bord qui interferent entre elles
- Quand le resultat d’un appel conditionne le suivant
Appels sequentiels avec boucle
Pour les workflows ou chaque etape depend de la precedente, implementez une boucle :
def boucle_function_calling(prompt: str, tools: list, max_tours: int = 10):
"""Boucle complete de function calling sequentiel."""
messages_input = prompt
tour = 0
while tour < max_tours:
response = client.responses.create(
model="gpt-5.3",
input=messages_input,
tools=tools,
parallel_tool_calls=False
)
# Verifier s'il y a des appels de fonction
appels = [i for i in response.output if i.type == "function_call"]
if not appels:
# Le modele a fini, il repond en texte
return response.output_text
# Executer chaque appel
resultats = []
for appel in appels:
resultat = executer_fonction_sync(appel.name, appel.arguments)
resultats.append({
"type": "function_call_output",
"call_id": appel.call_id,
"output": json.dumps(resultat)
})
# Preparer le prochain tour
messages_input = response.output + resultats
tour += 1
return "Limite de tours atteinte"
Exemple concret : pipeline de traitement
Un cas typique ou le sequentiel est necessaire :
tools_pipeline = [
{
"type": "function",
"name": "valider_commande",
"description": "Verifier la validite d'une commande (stock, prix, client)",
"parameters": {
"type": "object",
"properties": {
"commande_id": {"type": "string"}
},
"required": ["commande_id"],
"additionalProperties": false
},
"strict": true
},
{
"type": "function",
"name": "calculer_livraison",
"description": "Calculer les frais et delais de livraison. "
"Necessite une commande validee.",
"parameters": {
"type": "object",
"properties": {
"commande_id": {"type": "string"},
"mode": {"type": "string", "enum": ["standard", "express"]}
},
"required": ["commande_id", "mode"],
"additionalProperties": false
},
"strict": true
},
{
"type": "function",
"name": "confirmer_commande",
"description": "Confirmer la commande et declencher le paiement. "
"Necessite validation et calcul livraison prealables.",
"parameters": {
"type": "object",
"properties": {
"commande_id": {"type": "string"},
"livraison_id": {"type": "string"}
},
"required": ["commande_id", "livraison_id"],
"additionalProperties": false
},
"strict": true
}
]
# Le modele appellera naturellement dans l'ordre :
# 1. valider_commande
# 2. calculer_livraison (avec le resultat de l'etape 1)
# 3. confirmer_commande (avec les resultats des etapes 1 et 2)
resultat = boucle_function_calling(
"Traite la commande CMD-42567 en livraison express",
tools_pipeline
)
Pattern hybride : parallele puis sequentiel
Dans certains workflows, vous combinez les deux approches :
# Tour 1 : collecte parallele de donnees
# Le modele appelle get_client ET get_stock en parallele
# Tour 2 : decision sequentielle basee sur les resultats
# Le modele appelle valider_commande avec les infos collectees
# Tour 3 : action finale
# Le modele appelle confirmer_commande
Ce pattern est naturel : le modele parallelise automatiquement quand les appels sont independants, puis enchaine sequentiellement quand une dependance apparait.
Points cles a retenir
- Le modele parallelise automatiquement les appels independants
- Utilisez
asyncio.gatherpour executer les appels paralleles cote client - Desactivez avec
parallel_tool_calls=Falsequand l’ordre compte - Implementez une boucle pour les workflows multi-etapes
- Les descriptions de fonctions guident le modele sur les dependances entre etapes