Le modèle ne lance généralement pas directement une fonction. Il produit une proposition structurée : nom de l’outil et arguments. L’application valide cette proposition, exécute éventuellement l’outil, puis renvoie le résultat au modèle. Cette séparation est la frontière de sécurité essentielle.
Concevoir un bon outil
Donnez-lui une responsabilité étroite, un nom clair et un schéma précis. Utilisez types, enums et descriptions sans paramètres fourre-tout. Séparez lecture et écriture. Une fonction « gérer le compte » est difficile à autoriser ; « lire le profil » et « modifier l’adresse après confirmation » sont contrôlables.
Valider et autoriser côté serveur
Traitez les arguments comme une entrée non fiable. Validez types, longueurs et identifiants, puis vérifiez utilisateur, tenant et permission dans l’exécuteur. Le modèle ne doit jamais choisir seul une identité ou une clé secrète. Limitez les destinations réseau et les données retournées.
Gérer la boucle
Après l’exécution, transmettez un résultat court et structuré. Fixez nombre maximal d’étapes, délai et budget. Détectez les appels identiques. Rendez les écritures idempotentes et exigez une confirmation humaine avant paiement, publication, suppression ou envoi externe.
Observer et tester
Tracez outil demandé, validation, durée, résultat et raison d’arrêt, sans secrets. Testez arguments incomplets, injection, outil indisponible, timeout, doublon et résultat malformé. Comparez plusieurs modèles : la prise en charge du tool calling et la fidélité au schéma varient.
FAQ
Un schéma JSON sécurise-t-il l’action ?
Non. Il valide la forme, pas l’autorisation ni l’intention.
Le modèle peut-il appeler plusieurs outils ?
Selon l’API, oui en séquence ou parallèle. Contrôlez dépendances et effets.
Faut-il renvoyer toute la réponse de l’API au modèle ?
Non. Filtrez et résumez les champs strictement utiles.
Exemple complet avec validation d’arguments
from pydantic import BaseModel, Field
class WeatherArgs(BaseModel):
city: str = Field(min_length=1, max_length=80)
units: str = Field(pattern="^(celsius|fahrenheit)$")
TOOLS = {
"get_weather": lambda args: weather_client.get(
city=args.city, units=args.units, timeout=3
)
}
def execute_tool(call, user):
if call.name not in TOOLS:
raise PermissionError("outil non autorisé")
args = WeatherArgs.model_validate_json(call.arguments)
if not user.can_read_weather:
raise PermissionError("action interdite")
return TOOLS[call.name](args)La boucle d’orchestration doit limiter le nombre d’appels, ajouter un délai maximal et renvoyer le résultat de l’outil au modèle avec l’identifiant de l’appel. Ne considérez jamais les arguments générés comme fiables. Rendez les opérations d’écriture idempotentes et exigez une confirmation pour un paiement, une suppression ou un envoi externe. Dans les traces, conservez le nom de l’outil, la durée, le statut et une version expurgée des arguments.