Function calling : relier un LLM aux outils de votre application

Comprenez le function calling avec un exemple complet en Python : schéma, appel et résultat. Comparez OpenAI, Anthropic, Gemini et les sorties structurées.

Publié le

Function calling : relier un LLM aux outils de votre application

Le function calling permet à un grand modèle de langage (LLM) de demander à votre logiciel d’effectuer une opération. Cette séparation des rôles repose sur 3 étapes : le modèle propose un appel structuré, votre code le valide et l’exécute, puis vous lui renvoyez le résultat. Connecter votre base de données consiste à construire cette interface, pas à confier son mot de passe au modèle.

Comment fonctionne le function calling ?

Le modèle formule une demande. Votre application exécute l’opération. Pour un développeur qui relie un LLM à une API ou à une base de données, c’est la distinction essentielle à comprendre avant d’écrire du code.

Pensez au bon de commande d’un restaurant. Le serveur peut demander un plat, mais le bon ne cuisine rien. La cuisine vérifie la commande, prépare ce qu’elle peut et transmet le résultat. Dans votre application, l’appel d’outil joue le rôle du bon, et votre fonction celui de la cuisine.

Vous fournissez au modèle un schéma d’outil : une description de l’opération disponible et des champs qu’elle accepte en entrée. Le modèle peut répondre en indiquant le nom de la fonction et ses arguments, c’est-à-dire les valeurs à placer dans ces champs. Votre application vérifie la demande, appelle la fonction, puis renvoie sa sortie pour que le modèle puisse répondre ou demander une autre opération. Le guide du function calling de Google confie explicitement l’exécution à votre application.

Un outil peut aussi bien lire qu’écrire. Consulter une commande, effectuer une recherche dans un ensemble de documents ou vérifier une disponibilité sont des opérations d’outil, même si elles ne modifient rien. Pour une première intégration, une consultation au périmètre limité constitue un bon point de départ : elle permet d’examiner tout le parcours sans introduire de transaction qu’il faudrait pouvoir annuler.

Maquette architecturale montrant un appel d’outil soumis à la validation de l’application, une requête en base de données et le retour du résultat au modèle
Votre application prend en charge la consultation de la base de données et renvoie le résultat au modèle.

Function calling et tool calling : quelle différence ?

Pour les fonctions que vous développez, ces termes désignent souvent le même mécanisme. OpenAI emploie les deux, et Anthropic précise que le « tool use » est aussi appelé « function calling ». Le terme plus large d’« outil » peut désigner des capacités hébergées par le fournisseur comme des fonctions que vous implémentez. Le lieu d’exécution compte davantage que l’étiquette : pour une fonction qui accède à votre propre base de données, c’est à votre application de l’exécuter.

La documentation des trois fournisseurs présentée plus loin rend cette distinction concrète. Leurs formats de messages diffèrent : utilisez toujours les formats de demande et de résultat d’un même fournisseur ensemble.

Un exemple minimal de function calling de bout en bout

Exposez une fonction de consultation du statut d’une commande, puis renvoyez uniquement le statut fourni par la base de données. L’exemple suivant utilise une commande volontairement fictive, ORD-1042, dans une base SQLite locale. Il s’agit d’un jeu de données pédagogique, pas d’une fiche client ni d’une réponse réellement observée du modèle.

Function calling avec OpenAI : effectuer l’aller-retour complet

La documentation du function calling d’OpenAI présente l’aller-retour avec l’API Responses : déclarer la fonction, recevoir un function_call, exécuter votre code et renvoyer un function_call_output portant le même call_id.

Documentation OpenAI sur les appels de fonctions
OpenAI : appels de fonctions

Installez le SDK Python avec python -m pip install openai, définissez OPENAI_API_KEY dans votre environnement et enregistrez ce code dans order_tool.py. L’exemple utilise gpt-6-astra, le modèle retenu dans l’exemple Responses de la documentation actuelle ; cela ne signifie pas qu’il soit le seul adapté. Le script envoie de véritables requêtes à l’API, tandis que la base de données reste en mémoire.

Python
import json
import re
import sqlite3

from openai import OpenAI

client = OpenAI()
model = "gpt-6-astra"

# Demo identity. In a web app, obtain this from your authenticated session.
tenant_id = "tenant_demo"
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE orders (tenant_id TEXT, order_id TEXT, status TEXT)")
db.execute(
    "INSERT INTO orders VALUES (?, ?, ?)",
    (tenant_id, "ORD-1042", "shipped"),
)

# The tool schema: a name, a purpose, and an input contract.
tool = {
    "type": "function",
    "name": "get_order_status",
    "description": (
        "Look up the current status of an existing order by order ID. "
        "Use for status questions, not refunds or address changes. "
        "Returns the order ID and status, or an error."
    ),
    "strict": True,
    "parameters": {
        "type": "object",
        "properties": {
            "order_id": {
                "type": "string",
                "description": "The order ID supplied by the user, such as ORD-1042.",
            }
        },
        "required": ["order_id"],
        "additionalProperties": False,
    },
}


def execute(name, raw_arguments):
    if name != "get_order_status":
        return {"error": "unknown_tool"}
    try:
        args = json.loads(raw_arguments)
    except (json.JSONDecodeError, TypeError):
        return {"error": "invalid_arguments"}
    if not isinstance(args, dict) or set(args) != {"order_id"}:
        return {"error": "invalid_arguments"}
    order_id = args["order_id"]
    if not isinstance(order_id, str) or not re.fullmatch(r"ORD-[0-9]+", order_id):
        return {"error": "invalid_order_id"}
    row = db.execute(
        "SELECT status FROM orders WHERE tenant_id = ? AND order_id = ?",
        (tenant_id, order_id),
    ).fetchone()
    if row is None:
        return {"error": "order_not_found"}
    return {"order_id": order_id, "status": row[0]}


history = [{"role": "user", "content": "What is the status of order ORD-1042?"}]
response = client.responses.create(
    model=model,
    input=history,
    tools=[tool],
    tool_choice={"type": "function", "name": "get_order_status"},
    parallel_tool_calls=False,
)

# Keep every output item, including any reasoning items.
history.extend(response.output)
calls = [item for item in response.output if item.type == "function_call"]
if len(calls) != 1:
    raise RuntimeError("Expected one tool call; inspect the response before continuing.")
call = calls[0]
result = execute(call.name, call.arguments)
history.append({
    "type": "function_call_output",
    "call_id": call.call_id,
    "output": json.dumps(result),
})

answer = client.responses.create(
    model=model,
    input=history,
    tools=[tool],
    tool_choice="none",
    instructions="Answer from the tool result. If it contains an error, report the failure.",
)
print(answer.output_text)
db.close()

Sur le parcours réussi, cet exemple au périmètre volontairement limité effectue 2 requêtes au modèle et 1 consultation de la base de données. Il impose la consultation prévue lors de la première requête et désactive les appels d’outils supplémentaires lors de la seconde. Ce choix rend le mécanisme facile à suivre ; un assistant généraliste devrait aussi gérer les demandes pour lesquelles aucun outil ne convient, des informations manquent ou une autre consultation est nécessaire.

Le tenant_id ne figure pas dans le schéma d’outil, car l’application sait déjà qui est connecté. La requête en base de données associe cette identité de confiance à l’identifiant de commande demandé. Faire fournir l’identité par le modèle dans un argument supprimerait la séparation que cet exemple cherche précisément à illustrer.

L’appel du modèle et le résultat que vous lui renvoyez

La demande et le résultat sont deux messages distincts, reliés par un identifiant d’appel. Voici à quoi peut ressembler un appel. Les identifiants et les valeurs servent d’illustration ; il ne s’agit pas d’une réponse capturée auprès de l’API.

JSON
{
  "type": "function_call",
  "id": "fc_demo",
  "call_id": "call_demo",
  "name": "get_order_status",
  "arguments": "{\"order_id\":\"ORD-1042\"}"
}

Une fois que la fonction de traitement a renvoyé le statut du jeu de données de démonstration, l’application ajoute ce résultat à la conversation. Avec une réponse réelle, recopiez la valeur de call.call_id reçue ; n’en inventez jamais une nouvelle.

JSON
{
  "type": "function_call_output",
  "call_id": "call_demo",
  "output": "{\"order_id\":\"ORD-1042\",\"status\":\"shipped\"}"
}

Le modèle dispose alors d’un élément concret pour répondre, par exemple : « La commande ORD-1042 a été expédiée. » Le statut vient de la base de données. Le modèle a transformé la demande en arguments, puis les données renvoyées en réponse.

Un même principe, trois formats chez les fournisseurs

Le principe d’exécution se retrouve d’un fournisseur à l’autre ; le format des échanges, lui, change. Vérifié dans les documentations en ligne des fournisseurs le 17 octobre 2026, le tableau ci-dessous distingue leurs exemples actuels sans considérer que des champs aux noms proches sont interchangeables.

Fournisseur et appellation dans sa documentationDemande d’opération du modèleRésultat renvoyé par l’application
OpenAI : function callingUn function_call de l’API Responses, avec name, arguments et call_idUn function_call_output avec le call_id correspondant
Anthropic : tool useUn bloc tool_use de l’assistant, avec name, input et idUn bloc tool_result qui référence tool_use_id
Google : Gemini function callingDans le guide Interactions actuel, une étape function_callUn function_result qui référence call_id

Le tool use chez Anthropic

La documentation sur l’utilisation d’outils d’Anthropic définit les entrées de votre fonction avec input_schema. Pour un outil côté client, votre code traite le bloc tool_use de l’assistant et envoie un tool_result dans le message utilisateur suivant, tout en conservant le message de l’assistant dans la conversation.

Documentation Anthropic expliquant les outils côté client et leurs résultats
Anthropic : utilisation d’outils

Cette documentation trace une limite claire : les outils côté client s’exécutent dans votre application, tandis que les outils côté serveur s’exécutent sur l’infrastructure d’Anthropic. Un outil de recherche hébergé ne prouve donc pas que Claude puisse exécuter directement votre fonction d’accès à une base privée. Cette implémentation reste à votre charge.

Le function calling avec Gemini

La documentation du function calling de Gemini, chez Google, sépare elle aussi la demande du modèle de l’exécution par l’application. Son guide actuel utilise l’API Interactions, avec des étapes function_call et function_result, ainsi que previous_interaction_id pour poursuivre l’interaction.

Documentation Google Gemini sur les appels de fonctions
Gemini : appels de fonctions

Suivez l’exemple correspondant à l’API que vous utilisez. Copier une déclaration issue d’une API et un message de résultat issu d’une autre ne rend pas votre code portable. Conservez l’opération commune, par exemple get_order_status, et adaptez ensemble sa déclaration et le format du résultat.

Pourquoi les appels d’outils font fonctionner la boucle d’un agent

Un agent qui utilise des outils choisit une opération, reçoit son résultat, puis décide de la suite, et répète ce cycle. Le function calling assure le passage entre cette décision et une opération que votre logiciel peut exécuter.

Un assistant de suivi de commandes pourrait, par exemple, consulter une expédition, examiner la référence du transporteur renvoyée, puis demander les informations de suivi avant de répondre. La seconde opération dépend du premier résultat. Cette boucle de retour est plus utile que de demander au modèle d’inventer toutes les étapes à l’avance.

Le guide d’architecture des agents d’Anthropic distingue les workflows fixes des agents qui pilotent eux-mêmes leur utilisation des outils. Il présente aussi les retours des outils et les conditions d’arrêt comme des éléments de cette boucle.

Parler de « base de tous les agents » reste un raccourci à manier avec précaution. Ces documentations n’établissent pas que tout système appelé « agent » utilise une API particulière d’appel de fonctions. Le principe à retenir est la boucle qui relie le modèle à l’action. Une simple consultation peut rester un workflow fixe ; elle n’a pas besoin d’un planificateur autonome.

Trois problèmes qui apparaissent en production

La qualité du schéma, le choix de l’outil et la validation des arguments sont trois problèmes distincts. En résoudre un ne prouve pas que les autres le sont aussi.

Un schéma ambigu laisse passer le mauvais sens

Un champ nommé id ne précise pas au modèle si vous attendez un numéro de commande, un identifiant client ou une référence d’expédition. Une chaîne libre nommée action l’invite à proposer des valeurs que votre fonction de traitement ne reconnaîtra peut-être pas. Améliorez le contrat avant d’ajouter des instructions autour.

Nommez les paramètres avec précision, décrivez leur signification et utilisez une énumération lorsque seul un ensemble fixe de valeurs est accepté. Limitez suffisamment le périmètre du premier outil pour pouvoir résumer son rôle en une phrase simple. Dans l’exemple, order_id est explicite et la description exclut les remboursements et les changements d’adresse.

Le mode strict d’OpenAI impose additionalProperties: false sur les objets et exige que toutes les propriétés figurent dans required ; une valeur susceptible d’être absente peut accepter null. Le guide détaille ces exigences du mode strict. Google documente également des restrictions de schéma : un schéma accepté par votre validateur local ne l’est donc pas automatiquement par toutes les API de modèles.

La limite : un schéma peut encadrer la représentation des données, mais un identifiant de commande valide peut tout de même désigner la mauvaise commande. Cette vérification revient à l’application.

Le mauvais outil est appelé malgré des arguments valides

Un client qui demande « Où en est mon remboursement ? » a besoin d’une consultation de statut, pas d’un nouveau remboursement. Si les deux outils indiquent « gère les remboursements », vous rendez le choix ambigu.

Décrivez quand chaque outil doit être utilisé et quand il ne doit pas l’être. N’exposez que les opérations adaptées à la tâche et à l’utilisateur concernés. Les recommandations d’Anthropic pour définir les outils préconisent précisément d’expliquer leur rôle, le sens des paramètres, leur périmètre d’utilisation et leurs limites.

Si votre application sait déjà quelle opération effectuer, utilisez un mécanisme de sélection d’outil pris en charge par l’API, ou appelez directement cette opération. L’exemple impose une consultation connue à des fins pédagogiques ; n’en déduisez pas que chaque message utilisateur doit déclencher un outil. Un simple bonjour ou une question incomplète sur une commande peut plutôt appeler une réponse dans la conversation.

La limite : des arguments bien formés ne disent pas si le modèle a choisi la bonne action métier. Évaluez le choix de l’outil sur des demandes utilisateur représentatives, y compris celles qui ne devraient déclencher aucun appel.

Des arguments non validés franchissent une limite qu’ils ne devraient pas franchir

La fonction de traitement constitue un point de contrôle des entrées. Vérifiez que la fonction demandée figure dans une liste explicite de fonctions autorisées, analysez ses arguments, validez leur structure et leur sens, puis contrôlez les accès à partir de l’identité authentifiée par votre application.

Pour l’exemple de commande, un test d’échec utile consiste à demander une commande appartenant à un autre tenant, c’est-à-dire à un autre compte client. La requête ne doit pas la renvoyer, même si son identifiant est une chaîne parfaitement valide. Testez aussi un identifiant de commande manquant, un champ inattendu et une demande portant sur une opération que la fonction de traitement n’implémente pas.

La présentation générale d’Anthropic indique que l’absence d’informations peut amener le modèle à déduire les valeurs des arguments. Ne supposez pas qu’un champ obligatoire prouve que l’utilisateur l’a renseigné. Pour une action qui porte à conséquence, comparez l’opération demandée et ses entrées à ce que l’utilisateur a réellement autorisé.

Si l’exécution échoue, renvoyez un échec clairement identifiable. Un délai d’attente dépassé ne prouve pas que l’opération n’a jamais eu lieu : avant de relancer une écriture, utilisez un mécanisme d’idempotence dans l’application, capable de reconnaître une même opération voulue et d’éviter d’en répéter les effets. Ce sont des recommandations d’implémentation, pas des garanties apportées par l’API du modèle.

Quand faut-il préférer les sorties structurées ?

Utilisez les sorties structurées pour mettre en forme une réponse à partir d’informations déjà disponibles. Utilisez un outil lorsqu’une opération externe est nécessaire. Un système qui classe les messages de support en renvoyant une catégorie et un degré d’urgence peut produire une sortie structurée. Un assistant qui a besoin du statut actuel d’une commande doit aller le chercher.

Le guide Structured Outputs d’OpenAI fait cette distinction entre connecter des fonctionnalités de l’application et formater la réponse du modèle. Il précise aussi que les schémas de fonctions stricts reposent sur Structured Outputs. Ce sont des capacités complémentaires, pas des produits qui s’excluent mutuellement.

Bifurcation architecturale orientant une question sur des données à jour vers un appel d’outil et une réponse déjà connue vers une sortie structurée
Si la réponse exige des données à jour, récupérez-les avec un outil avant de la mettre en forme.

Supposons que vous ayez déjà fourni les données d’une commande et que votre interface ait seulement besoin de {order_id, status, explanation}. Une réponse structurée suffit. Si les données manquent ou doivent être actualisées, appelez d’abord la fonction de consultation. Vous pouvez ensuite structurer la réponse finale également.

Un objet JSON affirmant qu’un e-mail a été envoyé reste un simple objet de sortie. L’envoi de l’e-mail exige une opération effectivement exécutée et un résultat vérifiable.

Function calling et MCP : quel rôle pour chacun ?

Le function calling transmet la demande d’opération du modèle. Model Context Protocol (MCP) fournit une interface commune pour exposer des outils et d’autres éléments de contexte à des applications compatibles. Vous pouvez relier directement une fonction à une application avant d’introduire un serveur d’outils distinct.

MCP et tool calling : quand devient-il utile de partager les outils ?

Si plusieurs applications ont besoin de la même consultation de commande, un serveur MCP peut exposer l’outil pour permettre sa découverte et son appel. La documentation de l’architecture MCP décrit tools/list pour la découverte et tools/call pour les demandes d’exécution. Le serveur a toujours besoin d’une implémentation et de contrôles d’accès.

Pour passer à cette étape, découvrez ce qu’est MCP et comment s’articulent son hôte, son client et son serveur. Une fois votre consultation directe fonctionnelle, le guide d’implémentation d’un serveur MCP montre comment regrouper ces capacités pour les réutiliser. L’introduction de MCP ne supprime pas la nécessité de valider l’opération demandée.

Ce que les promesses exagèrent

Un schéma validé prouve le respect d’une contrainte de format, pas l’accomplissement d’une tâche. L’opération peut être inadaptée, l’enregistrement inaccessible ou le service sous-jacent indisponible. Une réponse finale fluide ne tranche aucune de ces questions.

Une liste d’outils ne suffit pas non plus à créer un agent. Il faut encore définir quels outils sont disponibles, comment leurs résultats entrent dans la conversation, quand s’arrêter et quoi faire après une erreur. Un framework peut regrouper ces mécanismes, mais la politique d’exécution reste un choix de conception.

Le premier objectif utile reste modeste : la demande arrive à la bonne fonction de traitement, seules les données autorisées reviennent et la réponse reflète ce résultat. Rendez ce parcours observable avant d’élargir les pouvoirs du système.

Par quoi commencer pour mettre en production ?

Commencez par une opération en lecture seule qui répond à une question récurrente des utilisateurs. L’exemple du statut de commande est un bon modèle, car son résultat attendu et ses limites d’accès sont faciles à vérifier.

Pour les développeurs, passez à l’implémentation si la réponse dépend de données que votre application sait récupérer de manière fiable. Conservez un workflow fixe lorsque l’opération suivante est déjà connue. Avant de laisser le modèle choisir des opérations d’écriture, prévoyez des autorisations explicites, une gestion des échecs et un moyen d’éviter les effets répétés.

Pour les équipes d’exploitation, examinez ce qui a été exécuté et ce qui a été renvoyé. Journalisez l’outil sélectionné, le résultat de la validation, le statut d’exécution et les identifiants permettant de relier les appels, tout en excluant les arguments sensibles des journaux courants. Fixez des délais d’attente et un nombre maximal de cycles d’appels d’outils adaptés à la tâche ; ne laissez pas la boucle de conversation se poursuivre sans limite.

Pour les acheteurs, demandez à l’équipe d’implémentation de montrer une consultation refusée et une opération en échec, en plus d’une réponse réussie. Si le produit se contente de résumer des informations déjà fournies, le function calling peut être superflu. Une sortie structurée ou une simple génération de texte peut suffire.

Dès lundi : implémentez une consultation au périmètre limité, essayez un enregistrement autorisé, un enregistrement inaccessible et une demande incomplète, puis examinez le résultat renvoyé au modèle dans chaque cas. N’élargissez la liste d’outils qu’une fois capable d’expliquer pourquoi chaque demande a été acceptée ou rejetée.

Questions fréquentes

Qu’est-ce que le function calling ?

C’est un mécanisme qui permet à un modèle de demander une opération en nommant une fonction et en fournissant ses arguments. Pour une fonction que vous définissez, votre application l’exécute et renvoie le résultat dans la conversation.

Quelle différence entre tool calling et function calling pour un LLM ?

Pour les fonctions que vous développez, les fournisseurs emploient souvent ces termes de façon interchangeable. L’utilisation d’outils peut aussi inclure des capacités hébergées par le fournisseur : vérifiez donc où se déroule l’exécution au lieu de le déduire de l’appellation.

À quoi ressemble un appel de fonction concret ?

Un modèle demande get_order_status avec order_id défini sur ORD-1042. Votre code valide la demande, contrôle l’accès, consulte la commande et renvoie son statut. La commande et le statut présentés dans cet article sont des données fictives servant d’exemple.

Comment utiliser le function calling avec GPT-5 ?

Suivez le déroulement documenté de l’API Responses avec un modèle compatible disponible dans votre projet : envoyez les définitions des outils, traitez les éléments de sortie correspondant aux appels de fonctions et renvoyez les résultats avec les identifiants d’appel correspondants. Conservez les éléments de raisonnement lorsque vous poursuivez la conversation. L’exemple exécutable ci-dessus utilise le modèle présenté dans le guide actuel, sans présumer qu’un modèle GPT-5 particulier est activé sur votre compte.

Pour recevoir des guides pratiques de développement avec l’IA, abonnez-vous à la newsletter.

Publié
Catégorie
Build
Articles similaires
Serveur MCP ou API : que proposer dans votre produit ?

Serveur MCP ou API : que proposer dans votre produit ?

Serveur MCP ou API : comparez les usages, les accès et le déploiement pour choisir les interfaces de votre produit, avec un exemple de création de ticket.17 oct. 2026Build
Hermes Agent : avis, prix et intérêt pour l’entreprise

Hermes Agent : avis, prix et intérêt pour l’entreprise

Faut-il installer Hermes Agent en entreprise ? Mémoire, skills, modèles, coûts et limites : les points à vérifier pour lui confier des tâches récurrentes.17 oct. 2026Build
LLM local : faut-il choisir Ollama ou llama.cpp ?

LLM local : faut-il choisir Ollama ou llama.cpp ?

Ollama ou llama.cpp pour un LLM local ? Comparez la gestion des modèles, le GGUF, les API, le GPU et les requêtes simultanées pour choisir votre outil.16 oct. 2026Build
Devin AI : avis sur Desktop, prix et migration Windsurf

Devin AI : avis sur Desktop, prix et migration Windsurf

Devin Desktop vaut-il le changement ? Prix, Spaces, agents locaux et cloud, migration Windsurf : notre avis pour choisir face à Cursor et Claude Code.16 oct. 2026Build
Context window : ce que votre IA peut vraiment prendre en compte

Context window : ce que votre IA peut vraiment prendre en compte

Comprenez la fenêtre de contexte, les limites de tokens de Claude, GPT-6 et Gemini, et les méthodes pour garder des réponses fiables avec de longs prompts.16 oct. 2026Build
Fine tuning ou RAG : que faut-il vraiment adapter ?

Fine tuning ou RAG : que faut-il vraiment adapter ?

RAG ou fine tuning : choisissez selon les données et le comportement attendus, avec trois cas d’usage en entreprise, leurs coûts et les effets du cache.16 oct. 2026Build
Aider AI : avis, coûts et prise en main pour développeurs

Aider AI : avis, coûts et prise en main pour développeurs

Notre avis sur Aider AI : installation, Git, choix des modèles et coûts API, avec les critères pour choisir entre Aider, Claude Code, Codex et Cursor.16 oct. 2026Build
Claude Code GitHub Actions : automatisez les revues de PR

Claude Code GitHub Actions : automatisez les revues de PR

Configurez Claude Code dans GitHub Actions pour relire les PR et corriger les bugs, avec une facturation claire, des limites et des protections pour les forks.15 oct. 2026Build
Newsletter

Une lettre, chaque dimanche.Des systèmes qui tournent, pas des hot takes.

Hebdomadaire. Pas de spam. Désabonnement à tout moment.