Appels d’outils
Les appels d'outils (function calling) permettent au modèle de demander l'exécution de vos fonctions : consulter une base, appeler une API, déclencher une action. Le modèle ne les exécute jamais lui-même : il vous dit quoi appeler, avec quels arguments.
Le cycle complet#
1. Vous envoyez : messages + description des outils
2. Le modèle répond : finish_reason = "tool_calls" + arguments JSON
3. VOUS exécutez la fonction dans votre code
4. Vous renvoyez : messages + tool_calls + résultat (role: "tool")
5. Le modèle répond en langage naturel
Exemple de bout en bout avec des données fictives#
import json
from openai import OpenAI
client = OpenAI(api_key="zir_ai_...", base_url="https://api.cloudzir.com/v1")
# 1. Décrire les outils disponibles
outils = [
{
"type": "function",
"function": {
"name": "stock_produit",
"description": "Retourne le stock disponible d'un produit dans un entrepôt.",
"parameters": {
"type": "object",
"properties": {
"reference": {"type": "string", "description": "Référence du produit, ex. DEMO-001"},
"entrepot": {"type": "string", "enum": ["alger", "oran", "constantine"]},
},
"required": ["reference"],
},
},
}
]
# 2. Exemple local avec données fictives
def stock_produit(reference: str, entrepot: str = "alger") -> dict:
return {"reference": reference, "entrepot": entrepot, "disponible": 42}
messages = [{"role": "user", "content": "Combien de DEMO-001 reste-t-il à Oran ?"}]
reponse = client.chat.completions.create(
model="mina-full", messages=messages, tools=outils
)
message = reponse.choices[0].message
# 3. Exécuter ce que le modèle demande
if message.tool_calls:
messages.append(message) # garder la demande dans l'historique
for appel in message.tool_calls:
arguments = json.loads(appel.function.arguments)
resultat = stock_produit(**arguments)
messages.append({
"role": "tool",
"tool_call_id": appel.id,
"content": json.dumps(resultat, ensure_ascii=False),
})
# 4. Redemander la réponse finale
finale = client.chat.completions.create(
model="mina-full", messages=messages, tools=outils
)
print(finale.choices[0].message.content)
# → « Il reste 42 unités de DEMO-001 à l'entrepôt d'Oran. »import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.CLOUDZIR_API_KEY,
baseURL: "https://api.cloudzir.com/v1",
});
const outils = [{
type: "function",
function: {
name: "stock_produit",
description: "Retourne le stock disponible d'un produit dans un entrepôt.",
parameters: {
type: "object",
properties: {
reference: { type: "string" },
entrepot: { type: "string", enum: ["alger", "oran", "constantine"] },
},
required: ["reference"],
},
},
}];
const implementations = {
stock_produit: ({ reference, entrepot = "alger" }) => ({ reference, entrepot, disponible: 42 }),
};
const messages = [{ role: "user", content: "Combien de DEMO-001 reste-t-il à Oran ?" }];
let r = await client.chat.completions.create({ model: "mina-full", messages, tools: outils });
const msg = r.choices[0].message;
if (msg.tool_calls?.length) {
messages.push(msg);
for (const appel of msg.tool_calls) {
const args = JSON.parse(appel.function.arguments);
const resultat = implementations[appel.function.name](args);
messages.push({
role: "tool",
tool_call_id: appel.id,
content: JSON.stringify(resultat),
});
}
r = await client.chat.completions.create({ model: "mina-full", messages, tools: outils });
console.log(r.choices[0].message.content);
}Réponse du modèle lors d'un appel d'outil#
{
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_a1b2c3",
"type": "function",
"function": {
"name": "stock_produit",
"arguments": "{\"reference\":\"DEMO-001\",\"entrepot\":\"oran\"}"
}
}]
},
"finish_reason": "tool_calls"
}]
}
arguments est une chaîne JSON, pas un objet : il faut la parser.
Contrôler le déclenchement#
tool_choice | Comportement |
|---|---|
"auto" (défaut) | Le modèle décide : outil ou texte. |
"none" | Interdit les outils pour cet appel. |
"required" | Force l'usage d'un outil (au choix du modèle). |
{"type":"function","function":{"name":"x"}} | Force cet outil précis. |
Écrire de bonnes descriptions#
La qualité des appels dépend presque entièrement de vos descriptions :
- Description de la fonction : dites quand l'utiliser, pas seulement ce qu'elle fait. « Utiliser quand l'utilisateur demande une disponibilité produit » vaut mieux que « Renvoie le stock ».
- Chaque paramètre mérite sa
descriptionet, si possible, unenum: cela supprime les valeurs inventées. - Marquez
requiredce qui l'est vraiment : sinon le modèle omettra des champs. - Peu d'outils à la fois : au-delà d'une dizaine, la précision baisse et le prompt coûte cher (les définitions sont facturées à chaque appel).
Sécurité#
Un appel d'outil est une suggestion. Contrôlez toujours, dans votre code :
l'identité de l'utilisateur, ses droits, et la validité des arguments. Ne câblez jamais
directement un tool_call sur une action destructive (suppression, paiement,
envoi) sans confirmation explicite.
- Validez les arguments contre un schéma avant exécution : le modèle peut produire des valeurs hors bornes.
- Limitez le nombre d'allers-retours (par exemple 5 maximum) pour éviter les boucles.
- Renvoyez des messages d'erreur d'outil en clair : le modèle sait souvent corriger son appel au tour suivant.
Modèles recommandés#
| Modèle | Appels d'outils |
|---|---|
mina-full | Recommandé : enchaînements complexes, plusieurs outils. |
mina-low | Bon pour 1 à 3 outils simples. |
mina-flash | Possible sur un outil unique et bien décrit. |
Format Anthropic#
Sur /anthropic, les mêmes capacités passent par tools avec
input_schema, les blocs tool_use et tool_result. Voir
Utilisation de l'API Anthropic.