---
title: "L'appel d'outils"
description: "Donner au modèle des fonctions qu'il peut demander, et traiter ses demandes."
url: https://meridal.fr/apprendre/appel-d-outils/
language: fr
written: 2026-10-07
verified: 2026-10-07
license: CC BY 4.0
---

[Apprendre](https://meridal.fr/apprendre/) › [Construction et programmation](https://meridal.fr/apprendre/dossier/construire/)

S'exercer · Niveau 3, Construire · 20 minutes

# L'appel d'outils

Donner au modèle des fonctions qu'il peut demander : il ne les exécute pas, il demande ; votre programme décide, exécute et lui rend le résultat. Le mécanisme de tout agent, sur un exemple métier, chez quatre éditeurs.

Écrit le 7 octobre 2026 · Vérifié le 7 octobre 2026

## Vous saurez

- Décrire une fonction pour qu'un modèle sache quand la demander
- Recevoir sa demande, exécuter la fonction et lui rendre le résultat
- Savoir ce qui reste sous votre contrôle, et pourquoi
- Commencer par des outils qui lisent, avant ceux qui écrivent

## Dans le parcours

[Premiers pas en développement](https://meridal.fr/apprendre/parcours/dev-debut/)

Étape 5 sur 8

- [← Avant Réponse dans un format imposé](https://meridal.fr/apprendre/format-impose/)
- [↓ Plus concret Premier appel à un modèle](https://meridal.fr/apprendre/premier-appel/)
- [→ Ensuite Un RAG minimal en Python](https://meridal.fr/apprendre/rag-minimal-python/)

L'essentiel

1. Le modèle n'exécute jamais une fonction : il demande à l'appeler, avec des arguments ; votre programme décide.
2. Une fonction se décrit par un nom, une phrase qui dit à quoi elle sert, et ses paramètres en schéma JSON.
3. Le résultat repart au modèle, qui s'en sert pour répondre, ou demande un autre appel.
4. Tout ce qu'un outil rapporte est une donnée, jamais une consigne : une page lue peut contenir un piège.
5. Un outil qui écrit, paie ou envoie demande l'accord d'une personne avant d'agir.

## Objectif

Donner à un modèle une fonction qui lit le statut d'une facture, le laisser demander l'appel, exécuter la fonction dans votre programme, et obtenir sa réponse finale. Les cinq temps sont les mêmes chez les quatre éditeurs : la demande avec les outils, la demande d'appel, l'exécution par votre code, le renvoi du résultat, la réponse.

Les factures sont fabriquées. Les appels au modèle suivent la documentation de chaque éditeur, lue le 7 octobre 2026, et n'ont pas encore été exécutés par Meridal ; la fonction locale, elle, a tourné.

Les bases : [le premier appel](https://meridal.fr/apprendre/premier-appel/) et [le format imposé](https://meridal.fr/apprendre/format-impose/).

## Données fournies

Une fonction qui lit le statut d'une facture dans un tableau fabriqué, à la place de votre logiciel de facturation. Elle ne fait que lire.

`outils.py`

```
FACTURES = {  # données fabriquées : à la place, votre logiciel de facturation
    "2026-0412": {"statut": "payée", "le": "2026-10-02"},
    "2026-0413": {"statut": "en attente", "échéance": "2026-10-31"},
}

def statut_facture(numero):
    """Le statut de paiement d'une facture. Une lecture seulement : l'outil ne modifie rien."""
    f = FACTURES.get(numero)
    if f is None:
        return f"Aucune facture {numero}."
    return ", ".join(f"{cle} : {valeur}" for cle, valeur in f.items())
```

`essai_outil.py`

```
from outils import statut_facture

for numero in ("2026-0412", "2026-0413", "2026-9999"):
    print(numero, "→", statut_facture(numero))
```

La sortie`uv run essai_outil.py`

```
2026-0412 → statut : payée, le : 2026-10-02
2026-0413 → statut : en attente, échéance : 2026-10-31
2026-9999 → Aucune facture 2026-9999.
```

Essayé le 7 octobre 2026 avec Python 3.12.7.

## Consignes

![Votre programme envoie la question et les outils décrits ; le modèle demande l'appel de statut_facture avec le numéro 2026-0412 ; votre code décide, puis exécute ; le résultat, « statut : payée, le : 2026-10-02 », repart au modèle comme une donnée ; le modèle répond, ou demande un autre appel. Un outil qui écrit, paie ou envoie attend l'accord d'une personne.](https://meridal.fr/assets/apprendre/medias/apprendre-appel-outils.svg)

*Les cinq temps d'un appel d'outil, entre votre programme et le modèle.*

OpenAI Anthropic Google Mistral

La demande d'appel arrive comme un élément `function_call` ; le résultat repart en `function_call_output`, avec le même `call_id`. OpenAI conseille d'activer toujours le mode strict, qui exige que chaque objet refuse les champs en plus.

`demander.py`

```
from dotenv import load_dotenv
load_dotenv()

import json

from openai import OpenAI

from outils import statut_facture

client = OpenAI()
tools = [{
    "type": "function",
    "name": "statut_facture",
    "description": "Donne le statut de paiement d'une facture à partir de son numéro.",
    "parameters": {
        "type": "object",
        "properties": {
            "numero": {"type": "string", "description": "Numéro de la facture, par exemple 2026-0412"},
        },
        "required": ["numero"],
        "additionalProperties": False,
    },
    "strict": True,
}]
input_list = [{"role": "user", "content": "La facture 2026-0412 est-elle payée ?"}]

response = client.responses.create(model="gpt-6-luna", tools=tools, input=input_list)
input_list += response.output
for item in response.output:
    if item.type == "function_call" and item.name == "statut_facture":
        numero = json.loads(item.arguments)["numero"]
        input_list.append({"type": "function_call_output", "call_id": item.call_id,
                           "output": statut_facture(numero)})

response = client.responses.create(model="gpt-6-luna", tools=tools, input=input_list)
print(response.output_text)
```

La sortie`uv run demander.py`

**Pas encore exécuté par Meridal.** Ce qu'on attend : une phrase qui dit que la facture 2026-0412 est payée, le 2 octobre 2026.

La demande d'appel arrive dans un bloc `tool_use` ; le résultat repart dans un bloc `tool_result`, au message suivant, un par appel.

`demander.py`

```
from dotenv import load_dotenv
load_dotenv()

import anthropic

from outils import statut_facture

client = anthropic.Anthropic()
tools = [{
    "name": "statut_facture",
    "description": "Donne le statut de paiement d'une facture à partir de son numéro.",
    "input_schema": {
        "type": "object",
        "properties": {
            "numero": {"type": "string", "description": "Numéro de la facture, par exemple 2026-0412"},
        },
        "required": ["numero"],
    },
}]
messages = [{"role": "user", "content": "La facture 2026-0412 est-elle payée ?"}]

response = client.messages.create(model="claude-opus-5-5", max_tokens=1024, tools=tools, messages=messages)
messages.append({"role": "assistant", "content": response.content})
resultats = [{"type": "tool_result", "tool_use_id": b.id, "content": statut_facture(**b.input)}
             for b in response.content if b.type == "tool_use"]
messages.append({"role": "user", "content": resultats})

final = client.messages.create(model="claude-opus-5-5", max_tokens=1024, tools=tools, messages=messages)
print("".join(b.text for b in final.content if b.type == "text"))
```

La sortie`uv run demander.py`

**Pas encore exécuté par Meridal.** Ce qu'on attend : une phrase qui dit que la facture 2026-0412 est payée, le 2 octobre 2026.

La demande d'appel est une étape `function_call` ; le résultat repart en `function_result`, rattaché à l'échange précédent par `previous_interaction_id`.

`demander.py`

```
from dotenv import load_dotenv
load_dotenv()

from google import genai

from outils import statut_facture

client = genai.Client()
declaration = {
    "type": "function",
    "name": "statut_facture",
    "description": "Donne le statut de paiement d'une facture à partir de son numéro.",
    "parameters": {
        "type": "object",
        "properties": {
            "numero": {"type": "string", "description": "Numéro de la facture, par exemple 2026-0412"},
        },
        "required": ["numero"],
    },
}

interaction = client.interactions.create(model="gemini-3.8-flash", input="La facture 2026-0412 est-elle payée ?", tools=[declaration])
appel = next(s for s in interaction.steps if s.type == "function_call")
resultat = statut_facture(**appel.arguments)

final = client.interactions.create(
    model="gemini-3.8-flash",
    input=[{"type": "function_result", "name": appel.name, "call_id": appel.id,
             "result": [{"type": "text", "text": resultat}]}],
    tools=[declaration],
    previous_interaction_id=interaction.id,
)
print(final.output_text)
```

La sortie`uv run demander.py`

**Pas encore exécuté par Meridal.** Ce qu'on attend : une phrase qui dit que la facture 2026-0412 est payée, le 2 octobre 2026.

Les demandes d'appel arrivent dans `tool_calls` ; chaque résultat repart en message de rôle `tool`, avec l'identifiant de l'appel.

`demander.py`

```
from dotenv import load_dotenv
load_dotenv()

import json
import os

from mistralai.client import Mistral

from outils import statut_facture

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
tools = [{
    "type": "function",
    "function": {
        "name": "statut_facture",
        "description": "Donne le statut de paiement d'une facture à partir de son numéro.",
        "parameters": {
        "type": "object",
        "properties": {
            "numero": {"type": "string", "description": "Numéro de la facture, par exemple 2026-0412"},
        },
        "required": ["numero"],
        },
    },
}]
messages = [{"role": "user", "content": "La facture 2026-0412 est-elle payée ?"}]

response = client.chat.complete(model="mistral-large-latest", messages=messages, tools=tools)
messages.append(response.choices[0].message)
for appel in response.choices[0].message.tool_calls or []:
    arguments = json.loads(appel.function.arguments)
    messages.append({"role": "tool", "name": appel.function.name,
                     "content": statut_facture(**arguments), "tool_call_id": appel.id})

final = client.chat.complete(model="mistral-large-latest", messages=messages, tools=tools)
print(final.choices[0].message.content)
```

La sortie`uv run demander.py`

**Pas encore exécuté par Meridal.** Ce qu'on attend : une phrase qui dit que la facture 2026-0412 est payée, le 2 octobre 2026.

## Critères de réussite

- Le modèle demande `statut_facture` avec le numéro 2026-0412
- Votre programme exécute la fonction, et non le modèle
- La réponse finale dit que la facture est payée, et quand
- Pour la facture 2026-9999, la réponse dit qu'elle n'existe pas, sans inventer de statut

## Indications

**Chez Anthropic, une erreur 400 avec `tool_choice`**

**Cause**Claude Opus 5.5 et Sonnet 5.5 refusent `tool_choice` à `any` ou `tool`.

**À faire**Laissez `auto`, la valeur par défaut.

**Le modèle répond sans appeler la fonction**

**Cause**La description ne dit pas assez quand s'en servir, ou la question ne la demande pas.

**À faire**Réécrivez la description comme une consigne : ce que fait la fonction, et quand l'appeler.

**Plusieurs appels arrivent d'un coup**

**Cause**Les modèles peuvent demander plusieurs fonctions dans la même réponse.

**À faire**Rendez un résultat par appel, tous dans le message suivant ; ou interdisez les appels parallèles pour les outils qui ont des effets.

**La fonction échoue**

**Cause**Un numéro inconnu, un service indisponible.

**À faire**Rendez l'erreur au modèle comme un résultat, en clair ; chez Anthropic, avec `is_error` à vrai.

## Correction commentée

**Ouvrir la correction**

**Ce qui se passe**Premier appel : le modèle lit la question et la description de la fonction, et répond par une demande d'appel, `statut_facture` avec `numero` à 2026-0412. Votre programme exécute la fonction et obtient « statut : payée, le : 2026-10-02 ». Second appel : le modèle reçoit ce résultat et rédige la réponse.

**Ce qui change d'un éditeur à l'autre**Les noms : `parameters` ou `input_schema`, `function_call`, `tool_use` ou `tool_calls`, et la façon de rattacher le résultat à la demande. Le mécanisme ne change pas.

**Ce qui reste à vous**La décision d'exécuter. C'est ce qui permet de refuser un appel, de demander une confirmation, ou de journaliser chaque action.

## Extensions

- **Les résultats sont des données** : un outil qui lit une page web ou un courriel peut rapporter un texte qui se fait passer pour une consigne. Anthropic demande de traiter tout résultat d'outil comme non fiable ; OpenAI, de faire passer les contenus extérieurs par les messages de l'utilisateur, jamais par les consignes du développeur.
- **Un outil qui agit** : avant de payer, d'écrire ou d'envoyer, une personne valide ; Google demande de confirmer avec l'utilisateur les appels lourds de conséquences, comme une commande.
- **Peu d'outils à la fois** : moins de vingt pour OpenAI, dix à vingt pour Google ; chaque description se paie en tokens lus.
- **Valider avant d'exécuter** : vérifier le numéro reçu, son format, et que la personne a le droit de le consulter, avant d'appeler le logiciel.
- **La suite** : un agent qui enchaîne plusieurs outils, et les serveurs MCP qui les partagent entre assistants : [serveurs MCP existants](https://meridal.fr/apprendre/serveurs-mcp-existants/).

## Lectures suivantes

↑ Plus général

Rien avant : c'est un point de départ.

← Avant [Réponse dans un format imposé S'exercer · Niveau 3 · 20 minutes](https://meridal.fr/apprendre/format-impose/)

Vous êtes ici

L'appel d'outils

→ Ensuite [Un RAG minimal en Python S'exercer · Niveau 3 · 25 minutes](https://meridal.fr/apprendre/rag-minimal-python/)

Premiers pas en développement · étape 6 sur 8

↓ Plus concret [Premier appel à un modèle S'exercer · Niveau 3 · 15 minutes](https://meridal.fr/apprendre/premier-appel/)

[Tout le dossier « Construction et programmation »](https://meridal.fr/apprendre/dossier/construire/)

Les pages qui renvoient ici

- [Bibliothèques d'agents](https://meridal.fr/apprendre/bibliotheques-d-agents/)
- [Choix d'une API et d'une bibliothèque](https://meridal.fr/apprendre/choix-d-une-api-et-d-une-bibliotheque/)
- [Projet : un agent de réponses clients avec file de validation](https://meridal.fr/apprendre/projet-agent-de-reponses-clients/)
- [Projet : un serveur MCP sur un logiciel de suivi des clients fictif](https://meridal.fr/apprendre/projet-serveur-mcp-sur-un-logiciel-clients/)
- [Réponse dans un format imposé](https://meridal.fr/apprendre/format-impose/)
- [Serveurs MCP existants](https://meridal.fr/apprendre/serveurs-mcp-existants/)

**Limites et sources**

Les appels suivent la documentation des éditeurs du 7 octobre 2026 et n'ont pas encore été exécutés par Meridal ; la fonction locale a tourné avec Python 3.12.7.

Les factures et leurs statuts sont fabriqués.

1. [OpenAI, Function calling](https://developers.openai.com/api/docs/guides/function-calling) ; [Anthropic, Tool use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) ; [Google, Function calling](https://ai.google.dev/gemini-api/docs/function-calling) ; [Mistral, Function calling](https://docs.mistral.ai/studio/conversations/function-calling).
2. Le relevé complet : `apprendre/recherches/2026-10-07-dev-appels-modeles.json`.

Écrit par Meridal, avec l'aide de l'IA ; chaque fait vérifié à sa source et relu avant publication. Texte et schémas sous licence [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.fr) : reprenez-les en citant Meridal. Le code sous licence MIT.

Service correspondant

Ne plus recopier d'un logiciel à l'autre

1 100 € à 3 400 €, puis sur devis

[Voir le service](https://meridal.fr/offre/#automatisation)

## Un premier échange de 30 minutes

En visio et sans frais : vous décrivez votre situation, nous vous indiquons ce qui est réalisable, avec quels outils et pour quel ordre de prix. Vous pouvez aussi nous écrire ; chaque message reçoit une réponse écrite.

[Réserver 30 minutes](https://meridal.fr/contact/#reserver) [Écrire un message](https://meridal.fr/contact/)
