Aller au contenu principal

Function calling : connecter l'IA à vos systèmes

Mis à jour le 29 juillet 2026

Function calling : connecter l’IA à vos systèmes

Le function calling permet au modèle d’appeler des fonctions que vous avez définies. Retenez d’emblée la répartition des rôles, car tout en découle : le modèle ne les exécute pas. Il indique laquelle appeler et avec quels arguments ; c’est votre code qui exécute et qui renvoie le résultat. Votre base de données, vos identifiants et votre logique métier ne quittent jamais votre infrastructure.

Le flux complet, étape par étape

L’échange se déroule en quatre temps :

  1. Vous déclarez des fonctions disponibles avec leurs paramètres
  2. Le modèle analyse la requête et décide d’appeler une fonction
  3. Votre code exécute la fonction et obtient le résultat
  4. Vous renvoyez le résultat au modèle pour qu’il formule sa réponse

La déclaration mérite un soin particulier : la description de la fonction et celle de chaque paramètre constituent la seule documentation dont dispose le modèle pour décider. Une description vague produit des appels approximatifs. Notez également la manière de refermer la boucle — le résultat repart avec le call_id reçu, qui sert d’accusé de réception et permet à l’API d’apparier la réponse à la demande.

from openai import OpenAI
import json

client = OpenAI()

# Étape 1 : Déclarer les fonctions disponibles
tools = [{
    "type": "function",
    "name": "get_meteo",
    "description": "Obtenir la météo actuelle d'une ville",
    "parameters": {
        "type": "object",
        "properties": {
            "ville": {
                "type": "string",
                "description": "Le nom de la ville"
            },
            "unite": {
                "type": "string",
                "enum": ["celsius", "fahrenheit"],
                "description": "Unité de température"
            }
        },
        "required": ["ville"]
    }
}]

# Étape 2 : Le modèle décide d'appeler la fonction
response = client.responses.create(
    model="gpt-5.6-terra",
    input="Quel temps fait-il à Marseille ?",
    tools=tools
)

# Étape 3 : Exécuter la fonction
for item in response.output:
    if item.type == "function_call":
        print(f"Fonction : {item.name}")
        print(f"Arguments : {item.arguments}")
        args = json.loads(item.arguments)

        # Votre implémentation réelle
        resultat_meteo = {"temperature": 22, "condition": "ensoleillé"}

        # Étape 4 : Renvoyer le résultat au modèle
        final = client.responses.create(
            model="gpt-5.6-terra",
            input=[
                {"role": "user", "content": "Quel temps fait-il à Marseille ?"},
                {
                    "type": "function_call_output",
                    "call_id": item.call_id,
                    "output": json.dumps(resultat_meteo)
                }
            ],
            tools=tools
        )
        print(f"Réponse : {final.output_text}")

# Résultat :
# Fonction : get_meteo
# Arguments : {"ville": "Marseille"}
# Réponse : Il fait actuellement 22 C a Marseille avec un temps ensoleillé.

Laisser le modèle choisir parmi plusieurs outils

L’intérêt réel apparaît lorsque vous exposez tout un catalogue de capacités. Dans un assistant e-commerce, vous déclarerez la recherche de produit, la vérification de stock et la prise de commande, puis vous laisserez le modèle arbitrer. Confronté à « est-ce que le laptop gaming XPS est disponible ? », il ne lance pas une recherche mais interroge directement le stock : c’est votre description des fonctions, et non une règle codée en dur, qui a produit ce choix.

tools = [
    {
        "type": "function",
        "name": "rechercher_produit",
        "description": "Rechercher un produit dans le catalogue",
        "parameters": {
            "type": "object",
            "properties": {
                "requête": {"type": "string", "description": "Termes de recherche"},
                "categorie": {"type": "string", "description": "Catégorie (optionnel)"}
            },
            "required": ["requête"]
        }
    },
    {
        "type": "function",
        "name": "verifier_stock",
        "description": "Vérifier la disponibilité d'un produit",
        "parameters": {
            "type": "object",
            "properties": {
                "produit_id": {"type": "string", "description": "ID du produit"}
            },
            "required": ["produit_id"]
        }
    },
    {
        "type": "function",
        "name": "passer_commande",
        "description": "Passer une commande pour un produit",
        "parameters": {
            "type": "object",
            "properties": {
                "produit_id": {"type": "string"},
                "quantite": {"type": "integer", "description": "Nombre d'unités"}
            },
            "required": ["produit_id", "quantite"]
        }
    }
]

# Le modèle choisit automatiquement la bonne fonction
response = client.responses.create(
    model="gpt-5.6-terra",
    input="Est-ce que le laptop gaming XPS est disponible ?",
    tools=tools
)

for item in response.output:
    if item.type == "function_call":
        print(f"Fonction choisie : {item.name}")
        print(f"Arguments : {item.arguments}")

# Résultat : Fonction choisie : verifier_stock
# Arguments : {"produit_id": "xps-gaming"}

Une même requête peut par ailleurs déclencher plusieurs appels d’un coup. Comparer la météo de deux villes en produit deux, indépendants l’un de l’autre : votre code doit donc parcourir response.output et traiter l’ensemble des function_call, jamais seulement le premier. C’est aussi une occasion d’exécuter ces appels en parallèle et de diviser la latence par deux.

response = client.responses.create(
    model="gpt-5.6-terra",
    input="Comparez la météo entre Paris et Lyon.",
    tools=[{
        "type": "function",
        "name": "get_meteo",
        "description": "Obtenir la météo d'une ville",
        "parameters": {
            "type": "object",
            "properties": {
                "ville": {"type": "string"}
            },
            "required": ["ville"]
        }
    }]
)

# Le modèle fait deux appels en parallèle
function_calls = [item for item in response.output if item.type == "function_call"]
print(f"Nombre d'appels : {len(function_calls)}")
for call in function_calls:
    args = json.loads(call.arguments)
    print(f"  - get_meteo({args['ville']})")

# Résultat :
# Nombre d'appels : 2
#   - get_meteo(Paris)
#   - get_meteo(Lyon)

La boucle d’agent

Un seul aller-retour suffit rarement : le résultat d’une fonction amène souvent le modèle à en appeler une autre. On enveloppe donc l’échange dans une boucle qui tourne tant que la réponse contient des appels de fonctions et qui s’arrête dès qu’elle n’en contient plus — signe que le modèle a formulé sa réponse finale. C’est le squelette de tout agent, et vous le retrouverez sous des habillages divers dans tous les frameworks d’agents.

def executer_fonction(nom: str, arguments: str) -> str:
    """Exécute une fonction et retourne le résultat."""
    args = json.loads(arguments)

    if nom == "get_meteo":
        # Simulation — remplacez par votre vraie API
        return json.dumps({"temp": 18, "condition": "nuageux"})
    elif nom == "get_heure":
        return json.dumps({"heure": "14:30", "timezone": "CET"})

    return json.dumps({"error": "Fonction inconnue"})

def agent_loop(question: str, tools: list) -> str:
    """Boucle d'agent : appelle des fonctions jusqu'a la réponse finale."""
    input_messages = [{"role": "user", "content": question}]

    while True:
        response = client.responses.create(
            model="gpt-5.6-terra",
            input=input_messages,
            tools=tools
        )

        # Vérifier s'il y a des appels de fonction
        function_calls = [i for i in response.output if i.type == "function_call"]

        if not function_calls:
            # Pas d'appel de fonction = réponse finale
            return response.output_text

        # Exécuter chaque fonction et ajouter les résultats
        for call in function_calls:
            resultat = executer_fonction(call.name, call.arguments)
            input_messages.append({
                "type": "function_call_output",
                "call_id": call.call_id,
                "output": resultat
            })

# Utilisation
reponse = agent_loop("Quel temps fait-il a Paris ?", tools)
print(reponse)

Une remarque de prudence sur ce pattern : la boucle while True n’a pas de garde-fou. En production, ajoutez un compteur d’itérations maximal, faute de quoi un modèle qui redemande indéfiniment le même outil consommera votre budget sans jamais conclure.

Verrouiller les arguments avec le mode strict

Le mode strict applique aux paramètres des fonctions la même garantie que le Structured Output appliquait aux réponses : les arguments respectent exactement le schéma déclaré. Sur une fonction qui crée un utilisateur, cela vous évite de recevoir un âge sous forme de chaîne, ou un champ supplémentaire que votre code n’attendait pas.

tools = [{
    "type": "function",
    "name": "creer_utilisateur",
    "description": "Créer un nouvel utilisateur",
    "strict": True,
    "parameters": {
        "type": "object",
        "properties": {
            "nom": {"type": "string"},
            "email": {"type": "string"},
            "age": {"type": "integer"}
        },
        "required": ["nom", "email", "age"],
        "additionalProperties": False
    }
}]

Points clés à retenir

  • Le function calling connecte le modèle à vos systèmes sans exposer vos données
  • Le modèle décide quelle fonction appeler, votre code l’exécute
  • Renvoyez le résultat avec function_call_output et le call_id
  • Le modèle peut appeler plusieurs fonctions en parallèle
  • Utilisez une boucle d’agent pour les conversations multi-tours avec fonctions
  • Activez strict: True pour garantir la conformité des arguments