Aller au contenu principal

Tool Search : outils dynamiques

Tool Search : outils dynamiques

Quand vous avez des dizaines ou des centaines de fonctions, les déclarer toutes dans chaque appel devient contre-productif : le modèle perd en précision et les coûts explosent. Tool Search résout ce problème en permettant au modèle de chercher l’outil adapté dans un catalogue indexe.

Le problème des catalogues d’outils larges

Le coût d’un catalogue d’outils est doublement invisible. D’abord financièrement : chaque définition de fonction est envoyée à chaque requête, y compris les quatre-vingt-dix-huit qui ne serviront pas — sur un volume important, cela finit par dépasser le coût des réponses elles-mêmes. Ensuite qualitativement, et c’est plus grave : au-delà de quelques dizaines d’outils aux périmètres voisins, le modèle choisit moins bien. Trop de choix dégrade la décision, ici comme ailleurs.

# 5 fonctions = ~500 tokens de contexte
# 50 fonctions = ~5000 tokens de contexte
# 200 fonctions = impossible a gerer efficacement

# Le modèle perd en precision quand il y a trop de fonctions
# Il choisit parfois la mauvaise fonction ou hallucine des paramètres

Tool Search renverse la logique : au lieu de tout présenter au modèle, on indexe le catalogue et on ne lui montre que ce qui correspond à la demande en cours. Le mécanisme est celui d’une recherche sémantique sur les descriptions — d’où la section suivante, qui n’est pas un conseil de style mais la condition pour que le système fonctionne.

from openai import OpenAI

client = OpenAI()

# Déclarer un grand nombre de fonctions
toutes_les_fonctions = [
    {
        "type": "function",
        "name": "creer_facture",
        "description": "Créer une nouvelle facture pour un client",
        "parameters": {"type": "object", "properties": {"client_id": {"type": "string"}}, "required": ["client_id"]}
    },
    {
        "type": "function",
        "name": "annuler_facture",
        "description": "Annuler une facture existante",
        "parameters": {"type": "object", "properties": {"facture_id": {"type": "string"}}, "required": ["facture_id"]}
    },
    # ... des dizaines d'autres fonctions
]

# Utiliser Tool Search pour laisser le modèle trouver la bonne fonction
response = client.responses.create(
    model="gpt-5.6-terra",
    input="Annule la facture FAC-2026-0042",
    tools=[{
        "type": "tool_search",
        "tool_search": {
            "tools": toutes_les_fonctions
        }
    }]
)

Le modèle ne reçoit dans son contexte que les fonctions pertinentes pour la requête, pas la totalité du catalogue.

Organiser le catalogue de fonctions

Deux principes suffisent à structurer un catalogue qui reste navigable. Préfixez les noms par domaine (crm_, facturation_, stock_) : la lecture d’une trace d’exécution devient immédiate, et les collisions de noms disparaissent. Et donnez à chaque fonction un périmètre net — deux fonctions dont les descriptions se chevauchent produiront des choix instables, que ce soit un humain ou un moteur vectoriel qui arbitre.

catalogue_crm = [
    {
        "type": "function",
        "name": "crm_rechercher_client",
        "description": "Rechercher un client dans le CRM par nom, email ou identifiant. "
                       "Utiliser pour trouver les informations d'un client existant.",
        "parameters": {
            "type": "object",
            "properties": {
                "terme": {"type": "string", "description": "Terme de recherche"},
                "champ": {"type": "string", "enum": ["nom", "email", "id"]}
            },
            "required": ["terme"],
            "additionalProperties": False
        },
        "strict": True
    },
    {
        "type": "function",
        "name": "crm_creer_client",
        "description": "Créer un nouveau client dans le CRM. "
                       "Utiliser quand un client n'existe pas encore.",
        "parameters": {
            "type": "object",
            "properties": {
                "nom": {"type": "string"},
                "email": {"type": "string"},
                "entreprise": {"type": "string"}
            },
            "required": ["nom", "email", "entreprise"],
            "additionalProperties": False
        },
        "strict": True
    }
]

catalogue_facturation = [
    {
        "type": "function",
        "name": "fact_creer_facture",
        "description": "Créer une facture. Nécessite un client existant.",
        "parameters": {
            "type": "object",
            "properties": {
                "client_id": {"type": "string"},
                "lignes": {"type": "array", "items": {"type": "object", "properties": {
                    "description": {"type": "string"},
                    "montant": {"type": "number"}
                }}}
            },
            "required": ["client_id", "lignes"],
            "additionalProperties": False
        },
        "strict": True
    }
]

# Fusionner tous les catalogues
catalogue_complet = catalogue_crm + catalogue_facturation

Bonnes pratiques pour les descriptions

Écrivez les descriptions pour la recherche, pas pour la documentation. « Récupère les données client » ne sera jamais trouvé par une requête portant sur « historique des commandes » ou « coordonnées de facturation » : il faut que les mots que vos utilisateurs emploient réellement figurent dans le texte indexé. Une description utile nomme les cas d’usage et le vocabulaire métier, pas l’implémentation.

# Mauvais : trop generique
{"name": "get_data", "description": "Recuperer des données"}

# Bon : spécifique avec contexte d'usage
{
    "name": "rh_consulter_solde_conges",
    "description": "Consulter le solde de congés (CP, RTT, maladie) d'un salarié. "
                   "Retourne les jours restants par type de conge. "
                   "Utiliser quand un salarié demande combien de jours il lui reste."
}

# Bon : avec des synonymes pour améliorer la recherche
{
    "name": "compta_rapprocher_banque",
    "description": "Rapprochement bancaire : comparer les écritures comptables "
                   "avec les mouvements bancaires. Réconciliation des comptes. "
                   "Utiliser pour vérifier la cohérence entre la comptabilite "
                   "et les relevés de banque."
}

Combiner Tool Search avec des outils fixes

Le mélange est le mode d’emploi normal en production. Quelques outils transversaux — l’heure, l’utilisateur courant, la recherche documentaire — restent déclarés en permanence parce qu’ils servent à presque toutes les requêtes, tandis que le catalogue métier reste dynamique. Vous gardez ainsi le contexte léger sans faire chercher au modèle ce dont il a besoin à chaque fois.

response = client.responses.create(
    model="gpt-5.6-terra",
    input="Cherche les ventes du mois sur le web et cree la facture correspondante",
    tools=[
        # Outils toujours disponibles
        {"type": "web_search_preview"},
        {"type": "code_interpreter"},
        # Catalogue dynamique
        {
            "type": "tool_search",
            "tool_search": {
                "tools": catalogue_complet  # 200+ fonctions
            }
        }
    ]
)

Pattern : système multi-domaines

class SystemeMultiDomaines:
    def __init__(self):
        self.client = OpenAI()
        self.catalogues = {}

    def enregistrer_domaine(self, nom: str, fonctions: list[dict]):
        """Enregistre un catalogue de fonctions pour un domaine."""
        self.catalogues[nom] = fonctions

    def catalogue_complet(self) -> list[dict]:
        """Fusionne tous les catalogues."""
        result = []
        for fonctions in self.catalogues.values():
            result.extend(fonctions)
        return result

    def executer(self, requete: str) -> str:
        """Exécute une requête avec Tool Search sur tous les domaines."""
        response = self.client.responses.create(
            model="gpt-5.6-terra",
            input=requete,
            tools=[{
                "type": "tool_search",
                "tool_search": {
                    "tools": self.catalogue_complet()
                }
            }]
        )

        # Traiter les appels de fonction
        for item in response.output:
            if item.type == "function_call":
                resultat = self.dispatcher(item.name, item.arguments)
                # ... boucle de function calling

        return response.output_text

    def dispatcher(self, name: str, arguments: str):
        """Route l'appel vers le bon service."""
        # Votre logique de routage
        pass

# Utilisation
systeme = SystemeMultiDomaines()
systeme.enregistrer_domaine("crm", catalogue_crm)
systeme.enregistrer_domaine("facturation", catalogue_facturation)
systeme.enregistrer_domaine("rh", catalogue_rh)
systeme.enregistrer_domaine("support", catalogue_support)

# Le modèle trouve automatiquement la bonne fonction
# parmi des centaines de possibilités
resultat = systeme.executer("Combien de jours de congés reste-t-il a Marie Dupont ?")

Points clés à retenir

  • Tool Search résout le problème des catalogues de fonctions volumineux
  • Le modèle ne reçoit que les fonctions pertinentes dans son contexte
  • Des descriptions riches et spécifiques sont essentielles pour la précision
  • Préfixez les noms de fonctions par domaine pour la lisibilité
  • Combinez Tool Search avec des outils built-in toujours disponibles