Aller au contenu

Ajuster le contexte d’outils d’un agent

Ce guide couvre le travail pratique autour de la surface d’outils à deux niveaux : voir ce qu’une exécution envoie, la modifier et diagnostiquer un agent qui a saisi le mauvais outil. Pour comprendre pourquoi les choses fonctionnent ainsi, lisez Contexte des outils et cache de prompts.

Ouvrez une conversation et touchez l’indicateur d’utilisation du contexte dans l’en-tête de la conversation. Il reconstruit la surface exacte que le prochain lancement enverrait, sans rien exécuter. Voir plus (ou la commande /context) ouvre l’explorateur de contexte complet, où vous pouvez inspecter chaque outil.

Deux segments importent ici :

  • Définitions d’outils et MCP et outils dynamiques — l’ensemble résident, avec le coût réel en jetons de chaque outil.
  • Outils chargés à la demande — l’index différé. Chaque entrée n’est facturée que pour son nom, car la requête ne transporte que le nom; en ouvrir une affiche tout de même la description et le schéma complets, de sorte que vous voyez ce que celui-ci coûterait si l’exécution le chargeait.

Si le segment différé est vide, la mise en différé est désactivée pour ce serveur. Vérifiez --tool-deferral dans la CLI cc_server.

L’ensemble résident est une politique, pas une configuration — il vit dans ModeToolPolicy (packages/cc_domain/lib/features/mcp/domain/value_objects/mode_tool_policy.dart) et s’applique à chaque exécution :

  • residentBuiltins — le vocabulaire de fichiers et de shell.
  • residentDiscovery — search_tools et list_my_tools. Ne les retirez pas; c’est ainsi qu’un agent atteint tout le reste.
  • residentMcpTools — la poignée d’outils Control Center qui valent leur schéma à chaque requête.
  • Un bloc par mode, pour les outils qu’un mode donné utilise à presque chaque tour.

Deux règles lorsque vous le modifiez :

N’ajoutez un outil que si la plupart des exécutions l’utilisent. Chaque nom coûte son schéma à chaque requête de chaque exécution, pour toujours. Un outil important lorsqu’il sert, mais rare dans l’ensemble, appartient à la moitié différée — l’agent le trouvera quand même.

Utilisez le vrai nom de l’outil. Un nom résident qui ne correspond à aucun outil est inerte : aucune erreur, aucun avertissement, l’outil est simplement différé. test/tooling/resident_tool_names_test.dart le détecte et maintient aussi chaque mode sous 40 outils résidents, ce qui est le but de l’exercice — au-delà de cette bande, la sélection des outils se dégrade de façon mesurable.

Les verbes requis et épinglés d’un mode sont ajoutés automatiquement, de sorte que vous n’avez jamais à lister le verbe de sortie du mode lui-même.

Quand un agent a utilisé le mauvais outil ou affirmé qu’il ne pouvait pas faire quelque chose, déterminez laquelle de ces trois situations s’est produite.

Chaque exécution consigne les outils différés qu’elle a chargés et ce qui les a déclenchés — un appel direct par nom ou une requête search_tools. Ouvrez le flux d’événements de l’exécution (Paramètres → Espace de travail → Agents → l’agent → Journaux → Vue) et cherchez [harness] loaded N deferred tool(s).

  • Rien de consigné et l’agent a dit que la capacité manquait — il n’a jamais cherché. C’est un problème de prompt, pas de récupération : l’index différé se trouve dans son prompt système avec l’instruction de ne pas déclarer une capacité manquante avant qu’une recherche soit revenue vide.
  • Une recherche consignée, mais rien de chargé — un vrai raté de récupération. Passez à l’étape 2.
  • Des outils chargés, mais le mauvais utilisé — les outils trouvés se ressemblent trop. Passez à l’étape 3.

search_tools fait correspondre les noms, les descriptions et les noms d’arguments. Un raté signifie presque toujours que la description ne contient pas les mots employés par l’agent.

Ajoutez-les. Les descriptions constituent l’index de récupération, donc allongez-les plutôt que de les raccourcir — écrivez le vocabulaire qu’un opérateur saisirait réellement (« assigner », « confier », « donner à »), pas seulement le nom interne. Raccourcir une description pour économiser des jetons vous coûte à la fois la correspondance de recherche et la précision de la sélection, et n’économise presque rien à côté du schéma.

Si le bon outil existe mais se classe bas, c’est à cela que sert l’argument limit; la description de l’outil lui-même indique à l’agent d’élargir une recherche dont le résultat semble proche mais passe à côté.

Si deux outils ont des descriptions qui se liraient de la même façon pour quelqu’un qui ne connaît pas déjà la différence, le modèle devine. Fusionnez-les en un seul outil doté d’un argument discriminant.

ticket_relation et ticket_pr_link suivent ce modèle : chacun gère la liaison et la dissociation au moyen d’un argument discriminant. Un choix fait à l’intérieur d’un seul appel, avec tout le contexte de la requête, est plus fiable qu’un choix fait entre deux descriptions quasi identiques.

Gardez les outils distincts lorsqu’ils exigent des approbations différentes, une journalisation d’audit différente ou un rendu différent — ce sont des raisons pour lesquelles une fusion vous coûterait quelque chose de réel.

Le taux de réussite du cache apparaît dans Observabilité → Aperçu sous Taux de cache. Une longue conversation en bonne santé affiche un taux élevé; s’il reste bas de façon persistante, quelque chose réécrit le préfixe à chaque tour.

Causes courantes, par ordre de probabilité :

  • Tout élément non déterministe au début du prompt. Un horodatage, un identifiant de requête ou un ensemble parcouru dans un ordre variable suffit.
  • Un changement de modèle ou d’effort en cours de conversation. Les deux modifient le préfixe. Les sous-agents sont particulièrement faciles à rater ici : exécuter les enfants à un effort moindre pour économiser peut coûter plus cher en partage de cache perdu que ce que cela permet d’économiser.
  • Un compactage fréquent. Le compactage réécrit l’historique par conception, son coût est donc réel; la solution consiste à compacter rarement et en profondeur plutôt que souvent et en surface.

Désactiver la mise en différé avec --tool-deferral=off constitue un test A/B valide pour isoler si un changement de comportement provient de la surface à deux niveaux. Cela reproduit exactement la requête d’avant la mise en différé, donc si le comportement persiste une fois la mise en différé désactivée, la surface n’en est pas la cause.