L’appel de fonctions (également appelé appel d’outils ou utilisation d’outils) permet de donner à un modèle l’accès à votre code. Vous décrivez des fonctions telles que « rechercher une commande » ou « calculer les frais de livraison ». Le modèle décide laquelle appeler et avec quels arguments ; votre code l’exécute et renvoie le résultat. Tous les agents, d’un bot d’assistance en magasin à Claude Code, reposent sur cette boucle.
Sur GetVibe API, l’appel de fonctions fonctionne via le endpoint standard /v1/chat/completions au format OpenAI et via /v1/messages au format Anthropic. Voici une boucle complète que nous avons exécutée avec trois modèles provenant de trois fournisseurs différents.
Fonctionnement de la boucle
- Vous envoyez des messages ainsi qu’une liste de
tools. Chaque outil possède un nom, une description et un schéma JSON pour ses paramètres. - Au lieu de texte, le modèle renvoie des
tool_calls: la fonction à appeler et les arguments à utiliser.finish_reasonvauttool_calls. - Votre code exécute les fonctions et ajoute un message avec le rôle
toolpour chaque appel, en transmettant le mêmetool_call_id. - Vous renvoyez à nouveau l’historique. Le modèle demande alors d’autres appels ou répond à l’utilisateur sous forme de texte.
Le modèle n’exécute jamais rien lui-même. Il demande uniquement un appel. Ce qui est réellement exécuté, ainsi que les permissions utilisées, dépend de votre code.
Un exemple complet : un assistant d’assistance pour un magasin
Deux fonctions : le statut d’une commande et un devis de livraison. L’utilisateur pose les deux questions en même temps, et le modèle appelle les deux fonctions en un seul tour.
import json
import os
from openai import OpenAI
client = OpenAI(base_url="https://getvibeapi.com/v1", api_key=os.environ["GETVIBE_API_KEY"])
MODEL = "gpt-6-luna"
def get_order_status(order_id: str) -> dict:
# votre recherche dans la base de données ou le CRM doit être effectuée ici
return {"order_id": order_id, "status": "out for delivery", "eta": "2026-10-24"}
def get_shipping_quote(zip_code: str) -> dict:
return {"zip_code": zip_code, "price_usd": 7.5, "days": 2}
TOOLS = [
{"type": "function", "function": {
"name": "get_order_status",
"description": "Look up an order by its ID",
"parameters": {"type": "object",
"properties": {"order_id": {"type": "string", "description": "Order ID, e.g. A-1042"}},
"required": ["order_id"]}}},
{"type": "function", "function": {
"name": "get_shipping_quote",
"description": "Courier shipping price and delivery time to a US ZIP code",
"parameters": {"type": "object",
"properties": {"zip_code": {"type": "string"}},
"required": ["zip_code"]}}},
]
FUNCTIONS = {"get_order_status": get_order_status, "get_shipping_quote": get_shipping_quote}
messages = [
{"role": "system", "content": "Vous êtes un assistant d’assistance pour un magasin. Soyez bref."},
{"role": "user", "content": "Où est ma commande A-1042 ? Et combien coûte la livraison au 94110 ?"},
]
for _ in range(5): # limiter le nombre de tours
resp = client.chat.completions.create(model=MODEL, messages=messages, tools=TOOLS)
msg = resp.choices[0].message
if not msg.tool_calls:
print(msg.content)
break
messages.append(msg.model_dump(exclude_none=True))
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = FUNCTIONS[call.function.name](**args)
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
Nous avons exécuté ce code sans modification avec gpt-6-luna, deepseek-v4.1-flash et claude-haiku-5.5, en modifiant uniquement MODEL. Au premier tour, les trois modèles ont appelé les deux fonctions : get_order_status avec A-1042 et get_shipping_quote avec 94110. Au deuxième tour, les trois ont répondu en texte avec le statut de livraison, la date estimée, le prix de 7.50 et le délai de livraison de deux jours.
Les éléments importants de ce code
- Ajoutez l’intégralité du message d’appel d’outil du modèle à l’historique. Sans cela, votre message
toolrépond à un appel que le modèle ne peut pas voir, et de nombreux modèles se contentent de rappeler la même fonction, ce qui fait tourner la boucle en rond. - Un message
toolpar appel, avec letool_call_idcorrespondant. Le modèle peut demander plusieurs appels à la fois, comme ici. - Les arguments arrivent sous forme de chaîne.
argumentscontient du JSON à l’intérieur d’une chaîne. Analysez-le et validez-le, car le modèle peut se tromper de valeur. - Limitez le nombre de tours. Sans limite, un bug dans une fonction peut transformer la boucle en processus sans fin, et générer une facture de tokens illimitée.
tool_choice : lorsqu’un appel est obligatoire
Par défaut, le modèle décide s’il doit appeler un outil ou répondre sous forme de texte ("auto"). tool_choice modifie ce comportement :
"none"— n’appeler aucun outil ;"required"— appeler au moins un outil ;{"type": "function", "function": {"name": "..."}}— appeler cette fonction spécifique.
La dernière forme est pratique pour l’extraction de données. Le schéma des paramètres de la fonction définit la structure de la réponse :
curl https://getvibeapi.com/v1/chat/completions \
-H "Authorization: Bearer $GETVIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4.1-flash",
"messages": [{"role": "user", "content": "Jane Doe, [email protected], souhaite une démonstration jeudi"}],
"tools": [{
"type": "function",
"function": {
"name": "save_lead",
"description": "Save a sales lead to the CRM",
"parameters": {
"type": "object",
"properties": {"name": {"type": "string"}, "email": {"type": "string"}, "request": {"type": "string"}},
"required": ["name", "email", "request"]
}
}
}],
"tool_choice": {"type": "function", "function": {"name": "save_lead"}}
}'
La réponse était un appel à save_lead avec name « Jane Doe », email « [email protected] » et request « Souhaite une démonstration jeudi ».
Une réserve : certains modèles Claude, notamment Claude Sonnet 5.5 et Claude Opus 5.5, n’acceptent pas un choix d’outil forcé. GetVibe transforme une telle demande en auto avec les mêmes outils. Vous obtenez ainsi une réponse au lieu d’une erreur, mais le modèle décide alors lui-même s’il doit appeler l’outil. Pour obtenir un JSON strict avec ces modèles, utilisez les sorties structurées.
Le format Anthropic : /v1/messages
Avec le SDK Anthropic, les outils sont légèrement différents : le schéma des paramètres est placé dans input_schema, et l’appel revient sous la forme d’un bloc de contenu tool_use.
curl https://getvibeapi.com/v1/messages \
-H "x-api-key: $GETVIBE_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-haiku-5.5",
"max_tokens": 512,
"tools": [{
"name": "get_order_status",
"description": "Look up an order by its ID",
"input_schema": {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]}
}],
"messages": [{"role": "user", "content": "Où est la commande A-1042 ?"}]
}'
La réponse contenait stop_reason tool_use ainsi qu’un bloc tool_use appelant get_order_status avec order_id A-1042. Vous renvoyez le résultat sous la forme d’un bloc tool_result dans le message utilisateur suivant, avec le même tool_use_id.
Quel modèle utiliser pour les outils
- Bots à fort volume et outils simples — DeepSeek V4.1 Flash ($0.0112 entrée · $0.0445 sortie par million de tokens) ou Claude Haiku 5.5 ($0.0267 entrée · $0.1334 sortie par million de tokens).
- Agents utilisant de nombreux outils et élaborant de longs plans — GPT-6 Luna ($0.0055 entrée · $0.0274 sortie par million de tokens) ou Claude Sonnet 5.5.
Les boucles d’agents renvoient le même long préfixe à chaque tour. La mise en cache des prompts est donc particulièrement importante ici. Vous trouverez plus de détails sur les champs de la requête dans la documentation sur les appels d’outils.
