Function calling et tool use : la mécanique des agents IA

Comment un LLM passe-t-il du chatbot qui parle à l’agent IA qui agit ? La réponse tient en deux mots : function calling. Cette primitive technique, parfois appelée tool use, est ce qui sépare une démo conversationnelle d’un agent qui réserve réellement votre RDV, consulte vos données CRM ou exécute du code Python. Sans elle, MCP n’existerait pas, A2A serait inutile, les frameworks comme LangChain ou CrewAI seraient vides.

Cet article est le tutoriel technique de référence sur le function calling en 2026. Vous allez comprendre la mécanique exacte, voir le code concret pour OpenAI, Anthropic et Mistral, maîtriser les patterns de production (parallel calling, error handling, structured outputs), et éviter les pièges courants. Cet article approfondit le pilier sur l’architecture des agents IA et complète notre guide MCP et guide A2A.

En bref

  • Function calling est une capacité native des LLM modernes qui leur permet d’invoquer des fonctions externes structurées plutôt que de simplement générer du texte.
  • Le modèle n’exécute pas les fonctions lui-même : il génère un output structuré décrivant quelle fonction appeler et avec quels arguments. Votre code exécute la fonction et renvoie le résultat au modèle.
  • Tous les LLM majeurs 2026 supportent : OpenAI (function calling + structured outputs), Anthropic Claude (tool use), Google Gemini, Mistral, DeepSeek, Llama 4.
  • Patterns essentiels : parallel function calling, structured outputs strict, error handling, multi-step loops, streaming.
  • Spécificités fournisseurs : OpenAI ajoute strict: true pour garantie schéma 100%, Anthropic cache les schémas 24h, Gemini recommande 10-20 tools max.
  • Pour aller au-delà du tutoriel et structurer le déploiement dans votre organisation, Proactive Academy propose une formation aux agents IA pour développeurs.

La mécanique fondamentale : 4 étapes de la boucle agent

Comprenons d’abord le pattern de base avant de regarder le code.

Le flow conversationnel d’un agent IA

Selon Pre.ai Blog (mars 2026) : « The model doesn’t execute functions. It generates structured output describing which function to call and what arguments to pass. Your application parses this output, executes the function, and feeds the result back to the model ».

Le pattern se déroule en 4 étapes claires :

  1. Définition : vous déclarez vos fonctions disponibles au LLM (nom, description, schéma des paramètres en JSON)
  2. Décision : le LLM reçoit le prompt utilisateur et décide quelle fonction appeler (ou répond directement si aucun outil n’est utile)
  3. Exécution : votre code (pas le LLM) exécute la fonction réelle avec les arguments suggérés
  4. Restitution : vous renvoyez le résultat au LLM, qui le synthétise dans une réponse utilisateur

Cette boucle peut se répéter. Selon OFox AI : « Les tâches complexes peuvent impliquer 5-10 tool calls avant que le modèle ait suffisamment d’information pour répondre ».

La boucle d’agent : 4 étapes du function calling Comment un LLM passe du raisonnement à l’action via votre code 👤 ÉTAPE 1 Prompt utilisateur « Météo à Paris ? » 🧠 ÉTAPE 2 LLM décide tool call get_weather(city= »Paris ») ⚙️ ÉTAPE 3 Votre code exécute API météo → 23°C 💬 ÉTAPE 4 Réponse utilisateur ↻ Boucle si 5-10 tool calls 📋 Message structuré JSON entre LLM et votre code {« tool_name »: « get_weather », « arguments »: {« city »: « Paris », « unit »: « celsius »}} Le LLM génère du JSON validé, pas du texte libre — c’est ça qui rend l’agent fiable ✅ Compatible avec tous les LLM majeurs 2026 OpenAI GPT-5.5 strict:true Claude 4.x tool_use natif Mistral Large 3 compat OpenAI Gemini 3.1 Pro 10-20 tools max

Pourquoi cette architecture compte

Le LLM est le cerveau qui raisonne. Votre code est le système nerveux qui agit. Cette séparation est essentielle pour trois raisons :

  • Sécurité : le LLM ne peut pas exécuter de code arbitraire. Vous gardez le contrôle de ce qui s’exécute vraiment.
  • Fiabilité : vous validez les arguments avant exécution, gérez les erreurs, retentez si nécessaire.
  • Observabilité : chaque appel passe par votre code, vous loggez tout, vous monitorez les coûts et les latences.

Maintenant, regardons le code concret.

Code concret 1 : function calling avec OpenAI

OpenAI est historiquement le premier à avoir popularisé le function calling (juin 2023). C’est aussi la syntaxe la plus largement compatible avec les autres fournisseurs.

Définir un tool

Vous décrivez votre fonction en JSON Schema. Selon OpenAI officiel :

python

from openai import OpenAI
client = OpenAI()

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Obtient la météo actuelle pour une ville donnée",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "Nom de la ville (ex: Paris, Lyon)"
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "Unité de température"
                }
            },
            "required": ["city"],
            "additionalProperties": False
        },
        "strict": True  # Garantit que le modèle respecte 100% le schéma
    }
}]

Le paramètre strict: True est essentiel en production. Selon TeachMeIDEA (mai 2026) : « Les structured outputs s’appliquent aussi aux tool calls. Quand vous activez strict: true sur une définition de fonction, les arguments retournés par le modèle sont garantis matcher le schéma ».

Invoquer le modèle et exécuter le tool

python

def execute_weather(city: str, unit: str = "celsius") -> dict:
    # Votre vraie implémentation (appel API météo externe)
    return {"city": city, "temperature": 23, "unit": unit, "condition": "ensoleillé"}

# Étape 1 : prompt utilisateur
messages = [{"role": "user", "content": "Quel temps fait-il à Paris ?"}]

response = client.chat.completions.create(
    model="gpt-5.5",
    messages=messages,
    tools=tools
)

# Étape 2 : le modèle a décidé d'appeler get_weather
tool_call = response.choices[0].message.tool_calls[0]
function_name = tool_call.function.name  # "get_weather"
arguments = json.loads(tool_call.function.arguments)  # {"city": "Paris"}

# Étape 3 : votre code exécute la fonction
result = execute_weather(**arguments)

# Étape 4 : on renvoie le résultat au modèle
messages.append(response.choices[0].message)
messages.append({
    "role": "tool",
    "tool_call_id": tool_call.id,
    "content": json.dumps(result)
})

final_response = client.chat.completions.create(
    model="gpt-5.5",
    messages=messages
)

print(final_response.choices[0].message.content)
# "Il fait 23°C et ensoleillé à Paris."

Voilà la boucle complète. Une vingtaine de lignes de code pour transformer un LLM en agent qui peut consulter la météo.

Code concret 2 : tool use avec Anthropic Claude

La syntaxe Anthropic diffère légèrement mais le pattern est identique. Selon Anthropic officiel :

python

import anthropic

client = anthropic.Anthropic()

tools = [{
    "name": "get_weather",
    "description": "Obtient la météo actuelle pour une ville donnée",
    "input_schema": {
        "type": "object",
        "properties": {
            "city": {"type": "string"},
            "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
        },
        "required": ["city"]
    }
}]

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "Quel temps fait-il à Paris ?"}]
)

# Détection du tool use
if response.stop_reason == "tool_use":
    tool_use = next(block for block in response.content if block.type == "tool_use")
    result = execute_weather(**tool_use.input)
    
    # Renvoi du résultat au modèle
    final_response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        tools=tools,
        messages=[
            {"role": "user", "content": "Quel temps fait-il à Paris ?"},
            {"role": "assistant", "content": response.content},
            {"role": "user", "content": [{
                "type": "tool_result",
                "tool_use_id": tool_use.id,
                "content": json.dumps(result)
            }]}
        ]
    )

Différence Anthropic vs OpenAI

Trois différences pratiques à connaître :

  • Schema field name : Anthropic utilise input_schema, OpenAI utilise parameters
  • Stop reason : Anthropic retourne stop_reason == "tool_use", OpenAI vérifie tool_calls directement
  • Caching automatique : Anthropic cache les schémas 24h automatiquement, économisant tokens. Selon BuildMVPFast (mars 2026) : « Anthropic cache les schémas pendant 24 heures automatiquement ».

Astuce optimisation Anthropic : utilisez tool_choice: "any" au lieu de auto quand vous savez qu’un tool call est nécessaire. Économie typique : ~33 tokens par requête.

Code concret 3 : function calling avec Mistral

Mistral est compatible avec le standard OpenAI pour le function calling. C’est le seul fournisseur à avoir adopté cette compatibilité explicite, ce qui facilite massivement les migrations.

python

from mistralai import Mistral

client = Mistral(api_key="your_key")

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Obtient la météo actuelle pour une ville donnée",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string"},
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
            },
            "required": ["city"]
        }
    }
}]

response = client.chat.complete(
    model="mistral-large-3",
    messages=[{"role": "user", "content": "Quel temps fait-il à Paris ?"}],
    tools=tools,
    tool_choice="auto"
)

# Le code suivant est identique à OpenAI
tool_call = response.choices[0].message.tool_calls[0]

Conséquence stratégique : si vous démarrez avec OpenAI, basculer vers Mistral demande changement de la clé API et du nom de modèle. Rien d’autre. Cette compatibilité explicite est précieuse pour les organisations qui veulent garder leur flexibilité multi-fournisseurs.

Le tableau comparatif des fournisseurs

Selon Vellum (mars 2026), les capacités varient légèrement par fournisseur :

FonctionnalitéOpenAIAnthropicGeminiMistral
JSON mode
Function / tool calling
Structured outputs strict
Parallel function calling✅ (défaut)
Streaming avec tools
Compatible standard OpenAInatifFormat customFormat custom✅ Compatible

Lecture pratique : pour la majorité des cas business B2B, les 4 fournisseurs sont équivalents fonctionnellement. Le choix se fait sur d’autres critères (pricing, souveraineté, écosystème), pas sur le function calling lui-même.

Pattern essentiel : la boucle multi-étapes (agent loop)

Un agent réel ne fait pas un seul appel. Il enchaîne des tool calls jusqu’à pouvoir répondre.

python

def agent_loop(user_query: str, tools: list, available_functions: dict, max_iterations: int = 10):
    """Boucle d'agent qui enchaîne les tool calls jusqu'à réponse finale."""
    messages = [{"role": "user", "content": user_query}]
    
    for iteration in range(max_iterations):
        response = client.chat.completions.create(
            model="gpt-5.5",
            messages=messages,
            tools=tools
        )
        
        message = response.choices[0].message
        messages.append(message)
        
        # Si pas de tool call, on a la réponse finale
        if not message.tool_calls:
            return message.content
        
        # Exécuter tous les tool calls (potentiellement en parallèle)
        for tool_call in message.tool_calls:
            function_name = tool_call.function.name
            function_args = json.loads(tool_call.function.arguments)
            
            try:
                result = available_functions[function_name](**function_args)
            except Exception as e:
                result = {"error": str(e), "type": "execution_error"}
            
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result)
            })
    
    return "Maximum iterations atteint sans réponse finale"

Cette boucle est le squelette de tout agent IA. Les frameworks comme LangChain, CrewAI ou Google ADK ajoutent par-dessus de l’orchestration, de la mémoire, de la gestion d’état, mais à la base, c’est cette boucle qui tourne.

Pattern essentiel : parallel function calling

Selon OFox AI (mars 2026) : « OpenAI enables parallel tool calls by default ».

Au lieu d’appeler 5 fonctions séquentiellement (5 × 200ms = 1 seconde), vous pouvez les appeler en parallèle (max ~250ms).

python

import concurrent.futures

def execute_parallel_tools(tool_calls, available_functions):
    """Exécute plusieurs tool calls en parallèle."""
    with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
        futures = {}
        for tool_call in tool_calls:
            function_name = tool_call.function.name
            function_args = json.loads(tool_call.function.arguments)
            future = executor.submit(available_functions[function_name], **function_args)
            futures[future] = tool_call
        
        results = []
        for future in concurrent.futures.as_completed(futures):
            tool_call = futures[future]
            try:
                result = future.result(timeout=30)
            except Exception as e:
                result = {"error": str(e)}
            results.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result)
            })
        
        return results

Gain de latence typique : 3-5x sur les agents avec plusieurs appels par étape. Décisif pour l’expérience utilisateur sur les agents conversationnels temps réel.

Pattern essentiel : structured outputs avec strict schema

OpenAI propose un mode où les arguments retournés sont garantis matcher votre schéma. C’est un game changer pour la fiabilité production.

python

from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()

class WeatherQuery(BaseModel):
    city: str
    unit: str = "celsius"
    include_forecast: bool = False

# Avec strict: True, le modèle retourne EXACTEMENT le schéma WeatherQuery
response = client.beta.chat.completions.parse(
    model="gpt-5.5",
    messages=[{"role": "user", "content": "Météo à Paris avec prévisions"}],
    response_format=WeatherQuery
)

parsed = response.choices[0].message.parsed
# parsed est typé : WeatherQuery(city="Paris", unit="celsius", include_forecast=True)

Avantage massif : plus besoin de valider les arguments avec try/except json.JSONDecodeError. Le modèle ne peut PAS retourner un JSON invalide.

Selon TeachMeIDEA (mai 2026) : « En définissant un schéma Pydantic, en appelant parse, en vérifiant les refus, et en pinnant votre version de modèle, vous éliminez une catégorie entière de bugs production ».

Gestion des erreurs en production

L’agent loop idéale gère 3 types d’erreurs différemment.

Erreur 1 : tool call invalide

Le modèle demande d’appeler une fonction inexistante. Solution : retourner une erreur structurée et laisser le modèle se corriger.

python

if function_name not in available_functions:
    result = {
        "error": f"Function {function_name} not found",
        "available_functions": list(available_functions.keys())
    }

Erreur 2 : argument invalide

La fonction existe mais les arguments sont mauvais. Solution : validation Pydantic en amont + retour erreur explicite.

python

try:
    validated_args = MyArgsModel(**function_args)
    result = function(**validated_args.model_dump())
except ValidationError as e:
    result = {"error": "Invalid arguments", "details": e.errors()}

Erreur 3 : exécution échouée

La fonction lève une exception (API down, timeout, permission denied). Solution : catch + structurer la réponse + retry logic dans l’agent.

python

try:
    result = function(**args)
except APITimeout:
    result = {"error": "API timeout", "retry_recommended": True}
except PermissionDenied:
    result = {"error": "Permission denied", "retry_recommended": False}

Selon BuildMVPFast : « Voici ce qui casse vraiment en production : le modèle retourne un JSON valide qui matche votre schéma mais contient des valeurs absurdes ». Donc validez sémantiquement vos arguments, pas juste syntaxiquement.

Bonnes pratiques de production

Cinq règles issues des déploiements 2025-2026 :

Règle 1 : descriptions de tool détaillées

Plus la description est précise, mieux le modèle sélectionne le bon tool. Décrivez quand utiliser la fonction, pas juste ce qu’elle fait.

python

# ❌ Mauvais
"description": "Recherche dans la base"

# ✅ Bon
"description": "Recherche dans la base CRM Salesforce des contacts par nom ou email. Utilise cette fonction quand l'utilisateur demande des informations sur un client ou prospect spécifique. Ne pas utiliser pour des recherches de produits."

Règle 2 : limiter le nombre de tools actifs

Selon BuildMVPFast : « La documentation Gemini recommande 10-20 tools maximum. Plus de tools = pire taux de sélection + plus de coûts en tokens ».

Solution avancée : Tool Search d’Anthropic charge dynamiquement les tools pertinents, économisant 90%+ de tokens sur les agents avec 50+ tools.

Règle 3 : pinner les versions de modèle

Les modèles évoluent. Le comportement de function calling peut changer entre versions. Pinnez explicitement :

python

model="gpt-5.5-2026-04-23"  # Version pinnée
# Pas juste "gpt-5.5"

Règle 4 : monitorer les coûts par tool

Chaque tool call coûte des tokens (description + arguments + résultat). Loggez le coût par tool pour identifier les goulots.

Règle 5 : tester en condition réelle

Les benchmarks publics ne reflètent pas votre cas d’usage. Testez sur 50-100 exemples de votre data réelle avant de déployer.

Combien coûte un function calling typique

Pour une PME avec un agent qui fait 1 000 requêtes/jour avec 3 tool calls par requête en moyenne :

Calcul base GPT-5.5 :

  • 1 000 × 3 = 3 000 tool calls / jour
  • ~500 tokens input + 200 tokens output par appel
  • ~150 K tokens input + 60 K tokens output / jour
  • Coût : ~0.40 $/jour input + 0.90 $/jour output = ~1.30 $/jour, soit ~40 $/mois

Sur Anthropic Claude Sonnet 4.6 : ~50-80 $/mois (légèrement plus cher mais output 5x plus long autorisé).

Sur Mistral Large 3 : ~25-40 $/mois (typiquement 2x moins cher qu’OpenAI à perf comparable).

Pour un grand groupe avec 10 000+ requêtes/jour, multipliez par 10. TCO LLM annuel : 5-15 K€ pour les volumes moyens, 50-150 K€ pour les volumes massifs.

Comment former vos équipes au function calling

Le développeur backend doit maîtriser l’API d’au moins un fournisseur, la définition de JSON schemas, la boucle d’agent, les patterns d’erreur, le parallel calling. Comptez 2-3 jours de formation pratique avec code.

Le tech lead ou architecte doit comprendre la portabilité entre fournisseurs, l’arbitrage strict vs flexible, l’intégration avec MCP, les frameworks (LangChain, CrewAI) construits par-dessus. Comptez 3-5 jours avec accompagnement sur cas réel.

Le DSI ou Chief Data & AI Officer doit comprendre le rôle du function calling dans l’architecture agents, les implications coût et sécurité, la stratégie multi-fournisseurs. Comptez 1 jour atelier stratégique.

C’est précisément le périmètre de notre accompagnement formation agents IA pour développeurs, avec pratique sur code Python concret.

FAQ : function calling en pratique

Function calling vs tool use vs structured outputs : quelle différence ?

Function calling vs tool use vs structured outputs : quelle différence ?
Function calling : terme original OpenAI, désigne la mécanique générale
Tool use : synonyme utilisé par Anthropic Claude
Structured outputs : variante qui garantit le respect strict d’un schéma JSON, pas seulement l’invocation de fonctions
En pratique, function calling = tool use. Les structured outputs sont une amélioration de fiabilité qui s’applique aussi aux tool calls.

Combien de tools puis-je activer simultanément ?

Recommandation pratique : 10-20 maximum pour de bons taux de sélection. Au-delà, le modèle peine à choisir le bon tool, les tokens consommés explosent. Pour 50+ tools, utilisez Tool Search d’Anthropic ou un router intelligent qui charge dynamiquement les tools pertinents.

Function calling vs MCP : faut-il choisir ?

Non, ce sont des couches complémentaires. Function calling est la primitive technique du LLM. MCP est le protocole d’intégration au-dessus. La plupart des setups modernes utilisent les deux : function calling pour le mécanisme dans le LLM, MCP pour standardiser les outils côté serveur.
Voir notre guide complet sur MCP pour le détail.

Mon modèle hallucine des appels à des fonctions inexistantes. Comment corriger ?

3 actions à mener :
Améliorer les descriptions de vos tools pour mieux les délimiter
Activer strict: True si vous êtes sur OpenAI
Logger les hallucinations dans votre boucle d’agent et retourner une erreur structurée pour que le modèle se corrige
Si le problème persiste, changez de modèle. Claude Sonnet 4.6 et GPT-5.5 sont aujourd’hui les plus fiables sur le function calling

Comment gérer un agent qui tourne en boucle infinie ?

Toujours mettre un max_iterations dans votre boucle d’agent (typiquement 10-15). Si atteint, retournez une erreur explicite. Ajoutez aussi un timeout global (30 secondes pour interactif, 5 minutes pour async) et un circuit breaker pour couper les coûts si l’agent dérape.

Quel fournisseur choisir spécifiquement pour le function calling ?

Tous les fournisseurs majeurs sont quasi équivalents fonctionnellement. Le choix se fait sur les autres critères :
OpenAI : meilleur écosystème, structured outputs strict
Anthropic Claude : meilleur pour les tool calls fiables et longues séquences
Mistral : compatible OpenAI, souveraineté française, prix le plus bas
Gemini : meilleur pour multimodal + tools
Voir notre matrice de décision LLM pour l’arbitrage complet.

Le function calling fonctionne-t-il avec les LLM open-weight (Llama, Mistral, DeepSeek) ?

Oui, mais avec des nuances. Llama 4, Mistral, DeepSeek V4-Pro supportent nativement. Les modèles plus anciens ou plus petits ont une fiabilité moindre. Pour la production sur open-weight, utilisez Outlines ou lm-format-enforcer côté vLLM/Ollama pour garantir le respect du schéma.

Quels frameworks utilisent function calling sous le capot ?

Quasiment tous : LangChain, LangGraph, CrewAI, AutoGen, OpenAI Agents SDK, Google ADK, LlamaIndex. C’est la primitive commune. Si vous utilisez un framework agentique, vous faites du function calling sans le savoir nécessairement. Pour les détails frameworks, voir notre comparatif frameworks d’agents IA.

Ressources officielles function calling

Pour aller plus loin avec les sources primaires :
OpenAI Function Calling : developers.openai.com/api/docs/guides/function-calling
Anthropic Tool Use : docs.anthropic.com
Mistral Function Calling : docs.mistral.ai/capabilities/function_calling
Gemini Function Calling : ai.google.dev
Outlines (open-weight) : github.com/outlines-dev/outlines

Le function calling est la primitive invisible qui rend possible tout l’écosystème agents IA en 2026. Maîtriser sa mécanique, ses patterns de production et ses pièges courants n’est plus optionnel pour les équipes tech qui industrialisent leurs agents. C’est la fondation sur laquelle reposent MCP, A2A, les frameworks LangChain ou CrewAI, et tous les services agentiques de Salesforce, ServiceNow, Microsoft. Pour structurer cette compétence dans vos équipes, se former concrètement aux agents IA avec Proactive Academy reste le moyen le plus direct de passer de la lecture de tutoriels à une stack agent IA production-ready.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *