Contrats d’outils pour les agents d’IA
Un outil est une frontière d’autorité présentée comme une fonction. La validation JSON peut démontrer que les arguments correspondent à une forme ; elle ne peut démontrer que l’action est voulue, autorisée, sûre à réessayer ou correctement terminée.
De la demande du modèle au résultat de l’outil
Le modèle demande un appel ; le code de l’application exécute la fonction. Le guide des appels de fonctions décrit une conversation comprenant la définition de l’outil, un appel, son résultat et une nouvelle réponse du modèle. Prenons un outil de recherche en lecture seule, search_notes :
- L’application fournit son nom, son rôle et le schéma des arguments : un objet avec une chaîne obligatoire
query, de longueur 1–200, sans propriété supplémentaire. L’implémentation ne recherche que dans l’index autorisé des notes publiques. - Le modèle renvoie un élément d’appel contenant un identifiant, un nom et des arguments. L’application attend l’élément complet au lieu d’exécuter des fragments d’arguments reçus en streaming.
- Elle analyse les arguments, valide le schéma, contrôle l’autorité et n’appelle qu’une fonction autorisée. Une demande invalide ou refusée produit une erreur sans lancer la recherche.
- Elle associe le résultat à l’identifiant de l’appel initial et le renvoie avec l’état de la conversation. Le modèle peut alors répondre ou demander une autre action permise.
Ces messages JSON abrégés utilisent des champs de style Responses ; ils illustrent le protocole sans constituer une requête API complète :
{"type":"function_call","call_id":"call_1","name":"search_notes","arguments":"{\"query\":\"KV cache\"}"}
{"type":"function_call_output","call_id":"call_1","output":"{\"status\":\"ok\",\"matches\":[]}"}
Un résultat vide signifie que cette recherche n’a rien trouvé, pas que le sujet n’existe pas. Un texte ordinaire ressemblant à ce JSON n’est pas un appel autorisé. Un adaptateur fournisseur ou un parseur local doit d’abord reconnaître une demande du protocole ; le harness décide encore si elle peut s’exécuter. Le résultat reste une donnée non fiable, pas une nouvelle instruction. La boucle d’agent régit l’action suivante et les conditions d’arrêt.
Dimensions du contrat
Contrat illustré
name: replace_text
purpose: replace one exact block in one repository file
input:
path: repository-relative path, no traversal
old_text: non-empty and must match exactly once
new_text: string
preconditions:
- path is inside approved write scope
- working tree version equals observed version
effects: reversible local write
idempotency: not retryable after success without re-reading
result:
status: succeeded | conflict | no-match | multiple-matches | denied
changed_file: path or null
approval: required when path is outside task scope
Ce contrat fournit à la boucle un échec discriminant. Un générique error: edit failed invite à réessayer aveuglément.
Enveloppe de résultat
Un résultat stable doit séparer le transport de l’issue métier :
{
"request_id": "req-123",
"status": "partial",
"completed": 8,
"failed": 2,
"retry_safe": false,
"artifact": "results/batch-123.json",
"errors": [{"code": "permission-denied", "item": "..."}]
}
Ne transformez jamais la réussite d’un élément en réussite du lot. Une « issue inconnue » diffère d’un échec car répéter une action visible de l’extérieur peut la dupliquer.
Règles de conception
- Évitez l’interpolation dans le shell. Transmettez directement un programme et un tableau d’arguments lorsque c’est possible. Cela retire l’analyse du shell, mais les arguments peuvent rester dangereux :
git, les CLI cloud et les gestionnaires de paquets ont tous des options destructrices ou exécutant du code. - Validez les chemins résolus. Vérifiez les frontières du dépôt après normalisation et résolution des liens symboliques ; refuser la chaîne littérale
../ne suffit pas. Laissez les chemins sensibles hors de l’autorité de l’outil plutôt que de vous en remettre seulement à une liste de refus. - Arrêtez les tentatives improductives. Utilisez un disjoncteur fondé sur le risque et l’idempotence de l’opération. Une lecture peut tolérer plusieurs tentatives ; une écriture visible de l’extérieur peut n’en tolérer aucune après une issue inconnue.
- Renvoyez des échecs structurés. Distinguez entrée mal formée, autorité refusée, échec transitoire de transport, rejet métier, exécution partielle et issue inconnue. L’action suivante en dépend.
Injection et résultats non fiables
Les pages Web, issues, documents et métadonnées d’outils peuvent contenir du texte conçu pour rediriger le modèle. Ce sont toujours des données. Un harness sûr étiquette la provenance non fiable, refuse aux sorties d’outils toute autorité d’exécution automatique, retire les secrets des contextes destinés au modèle et réapplique les politiques aux actions suivantes proposées. Les réponses typées réduisent l’ambiguïté ; elles n’assainissent pas la sémantique.
Compromis avec les modèles compacts
Les petits modèles produisent souvent plus fiablement des schémas superficiels, mais retirer des détails de schéma peut éliminer des informations critiques pour la sécurité. Mesurez le taux d’appels acceptés et la réussite des tâches avec des modèles de prompt et des quantifications identiques. N’affaiblissez pas silencieusement les permissions pour vous adapter à un modèle plus faible ; préférez des outils simples et ciblés, des adaptateurs contraints ou des modèles plus puissants.