SYSTÈME D'ACCÈS INFORMATIQUE

Patterns d'Utilisation des Outils : Interfaces Agent-Outil Fiables


Votre agent a appelé un outil et a reçu un blob JSON de 40 lignes — réponse API brute, objets imbriqués, codes d’erreur enfouis dans un champ status. Le modèle l’a lu, a choisi une valeur vraisemblable et a continué. La valeur était incorrecte. Trois étapes plus tard, l’agent a rédigé en toute confiance un rapport basé sur des données erronées.

L’outil a fonctionné. L’interface a échoué.

L’utilisation des outils est le mécanisme qui transforme un modèle de langage en agent. Chaque capacité de votre agent — recherche en base de données, écriture de fichiers, appels API, interrogation de services — arrive via une interface d’outil. Si l’interface est mal conçue, le modèle prend de meilleures décisions même quand le service sous-jacent fonctionne correctement. Ce guide couvre cinq patterns pour construire des interfaces d’outils précises, fiables et prêtes pour la production.

Prérequis : Familiarité avec Python et l’API Claude. Pour le contexte sur MCP comme couche de transport d’outils, voir Construire votre Premier Serveur MCP.


Pourquoi la Conception de l’Interface Importe

Quand un agent choisit et utilise un outil, il prend deux décisions :

  1. Quel outil appeler — piloté par le name et la description de l’outil
  2. Quels arguments passer — piloté par le input_schema de l’outil

Les descriptions ambiguës mènent à une mauvaise sélection d’outils. Les schémas trop souples permettent au modèle de passer des entrées malformées. Les résultats non structurés le forcent à deviner ce qui s’est passé. La plupart des bugs d’agents ne résident pas dans le raisonnement — ils résident à la frontière de l’outil.


Pattern 1 : Conception Schéma-en-Premier

Écrivez le schéma JSON avant d’écrire l’implémentation. Un schéma strict contraint le comportement du modèle au stade des entrées — avant que quoi que ce soit ne s’exécute.

import anthropic
client = anthropic.Anthropic()
tools = [
{
"name": "search_products",
"description": (
"Recherche dans le catalogue de produits par mot-clé. "
"Retourne une liste de produits correspondants avec IDs, noms et prix. "
"Utilisez-le quand l'utilisateur veut trouver ou parcourir des produits."
),
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Mots-clés à rechercher"
},
"category": {
"type": "string",
"enum": ["electronics", "clothing", "food", "home", "all"],
"description": "Catégorie de produit à filtrer. Utilisez 'all' si non spécifié."
},
"max_results": {
"type": "integer",
"minimum": 1,
"maximum": 20,
"description": "Nombre de résultats à retourner. Défaut : 5"
}
},
"required": ["query", "category"]
}
}
]
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "Trouve-moi de l'électronique à moins de 100€"}]
)

Règles de schéma qui réduisent les erreurs :

  • Utilisez enum pour tout champ avec un ensemble fixe de valeurs valides
  • Définissez minimum/maximum sur les champs numériques
  • Marquez les champs required uniquement quand l’outil ne peut pas s’exécuter sans eux
  • Rédigez les descriptions du point de vue du modèle : “Utilisez-le quand…”

Quand utiliser : Pour chaque définition d’outil.


Pattern 2 : Résultats d’Outil Structurés

Retournez des résultats typés et lisibles par machine. Ne retournez jamais des réponses API brutes.

import json
from dataclasses import dataclass, asdict
from typing import Any, Optional
@dataclass
class ToolResult:
success: bool
data: Optional[Any] = None
error: Optional[str] = None
def to_content(self) -> str:
return json.dumps(asdict(self))
def search_products(
query: str,
category: str,
max_results: int = 5,
) -> ToolResult:
try:
raw_results = _query_database(query, category, limit=max_results)
products = [
{"id": r["product_id"], "name": r["title"], "price": r["price_usd"]}
for r in raw_results
]
return ToolResult(success=True, data={"products": products, "count": len(products)})
except ConnectionError as e:
return ToolResult(success=False, error=f"Base de données indisponible : {e}")
except Exception as e:
return ToolResult(success=False, error=f"Recherche échouée : {type(e).__name__}: {e}")
def _query_database(query, category, limit):
return []

L’enveloppe consistante {success, data, error} signifie que le modèle sait toujours où chercher. Pour la gestion gracieuse des pannes, voir Patterns de Récupération d’Erreurs.


Pattern 3 : Appels d’Outils en Parallèle

Claude peut demander plusieurs outils dans une seule réponse. Traitez-les en parallèle plutôt que séquentiellement.

from concurrent.futures import ThreadPoolExecutor, as_completed
TOOL_REGISTRY = {
"search_products": search_products,
}
def dispatch_tool(name: str, inputs: dict) -> ToolResult:
handler = TOOL_REGISTRY.get(name)
if not handler:
return ToolResult(success=False, error=f"Outil inconnu : {name}")
return handler(**inputs)
def process_tool_calls(response: anthropic.types.Message) -> list[dict]:
tool_uses = [
block for block in response.content
if block.type == "tool_use"
]
if not tool_uses:
return []
def execute(tool_use):
result = dispatch_tool(tool_use.name, tool_use.input)
return {
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": result.to_content(),
}
with ThreadPoolExecutor(max_workers=len(tool_uses)) as executor:
futures = {executor.submit(execute, tu): tu for tu in tool_uses}
results = []
for future in as_completed(futures):
results.append(future.result())
return results
def run_agent(user_message: str) -> str:
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=4096,
tools=tools,
messages=messages,
)
if response.stop_reason == "end_turn":
for block in response.content:
if hasattr(block, "text"):
return block.text
return ""
if response.stop_reason == "tool_use":
tool_results = process_tool_calls(response)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
break
return ""

Pour orchestrer plusieurs agents appelant chacun des outils, voir Patterns Multi-Agent.


Pattern 4 : Enveloppe d’Appel d’Outil Sécurisée

Ne laissez jamais les exceptions d’outils atteindre la boucle d’agent sans être gérées.

import signal
def timeout_handler(signum, frame):
raise TimeoutError("Délai d'exécution de l'outil dépassé")
def safe_tool_call(
name: str,
inputs: dict,
timeout_seconds: int = 30,
) -> ToolResult:
"""
Exécute un outil avec délai, capturant toutes les exceptions.
Retourne toujours un ToolResult — ne lève jamais d'exception.
"""
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(timeout_seconds)
try:
return dispatch_tool(name, inputs)
except TimeoutError:
return ToolResult(
success=False,
error=f"L'outil '{name}' a dépassé le délai de {timeout_seconds}s"
)
except Exception as e:
return ToolResult(
success=False,
error=f"L'outil '{name}' a levé {type(e).__name__}: {e}"
)
finally:
signal.alarm(0)

Quand un outil retourne success: false, le modèle peut décider de réessayer, d’essayer une alternative ou de signaler l’échec. Pour des stratégies de retry plus larges, voir Patterns de Récupération d’Erreurs.


Pattern 5 : Validation et Troncature des Résultats

Validez les résultats d’outils avant de les retourner au modèle.

MAX_TOOL_RESULT_CHARS = 8000
def validate_result(result: ToolResult, expected_keys: list[str]) -> ToolResult:
if not result.success or not isinstance(result.data, dict):
return result
missing = [k for k in expected_keys if k not in result.data]
if missing:
return ToolResult(
success=False,
error=f"Champs manquants dans la réponse : {missing}"
)
return result
def truncate_result(result: ToolResult) -> ToolResult:
content = result.to_content()
if len(content) <= MAX_TOOL_RESULT_CHARS:
return result
truncated_data = {
"truncated": True,
"chars_omitted": len(content) - MAX_TOOL_RESULT_CHARS,
"content": content[:MAX_TOOL_RESULT_CHARS],
}
return ToolResult(
success=result.success,
data=truncated_data,
error="Résultat tronqué — trop grand pour la fenêtre de contexte",
)
def safe_tool_call_validated(
name: str,
inputs: dict,
expected_keys: list[str] | None = None,
) -> ToolResult:
result = safe_tool_call(name, inputs)
if expected_keys:
result = validate_result(result, expected_keys)
result = truncate_result(result)
return result

Pour observer et déboguer ces échecs en production, voir Débogage et Observabilité.


Erreurs Courantes

Erreur 1 : Retourner des Réponses API Brutes

Le modèle reçoit un objet imbriqué avec 30 champs, la plupart non pertinents. Il choisit le mauvais.

Solution : Formatez la réponse avant de la retourner. Ne retournez que ce dont le modèle a besoin pour sa prochaine décision.

Erreur 2 : Outils avec Effets de Bord sans Confirmation

Un outil qui envoie un email ou supprime un enregistrement ne doit pas s’exécuter silencieusement.

Solution : Pour les actions irréversibles, utilisez un pattern à deux outils : plan_email retourne un aperçu, send_email envoie réellement.

Erreur 3 : Responsabilités d’Outils qui se Chevauchent

Deux outils qui font des choses similaires forcent le modèle à deviner lequel utiliser.

Solution : Chaque outil doit avoir un but distinct et non chevauchant.

Erreur 4 : Pas de Délai sur les Outils Externes

Un appel API tiers lent bloque indéfiniment toute la boucle d’agent.

Solution : Définissez toujours un délai (Pattern 4).


Liste de Contrôle pour la Production

  • Chaque outil a une description du point de vue du modèle (“Utilisez-le quand…”)
  • Champs enum pour toutes les entrées à valeurs fixes
  • Chaque outil retourne {success, data, error} — jamais des réponses brutes
  • Appels d’outils enveloppés dans safe_tool_call
  • Exécution parallèle pour les réponses multi-outils
  • Délai défini sur chaque outil externe
  • Troncature des résultats pour les charges de taille variable
  • Validation pour les outils appelant des APIs externes

Prochaines Étapes

  1. Commencez par le schéma — écrivez votre input_schema avant le corps de la fonction
  2. Ajoutez l’enveloppe ToolResult à chaque outil existant
  3. Intégrez safe_tool_call pour renforcer votre boucle d’agent
  4. Configurez la validation pour les outils qui appellent des APIs externes

Guides associés :