Aller au contenu principal

Les Options de tool_choice

Mis à jour le 29 juillet 2026

Décider qui décide

Passer des tools à l’API ne signifie pas que le modèle doit s’en servir, ni qu’il en a le droit. Entre ces deux extrêmes, le paramètre tool_choice est le levier qui arbitre : selon la valeur que vous lui donnez, vous laissez le modèle libre de son jugement, vous l’obligez à produire un appel structuré, ou vous lui retirez complètement l’accès aux outils que vous venez pourtant de lui déclarer. C’est un réglage discret, mais il change radicalement le comportement d’un même code face à une même question.

ValeurComportementCas d’usage
autoLe modèle décide librement s’il utilise un outil ou répond en texte. C’est le mode par défaut.Assistants conversationnels, chatbots polyvalents
anyLe modèle est obligé d’appeler au moins un outil. Il ne peut pas répondre en texte seul.Pipelines automatisés, extraction structurée de données
noneLe modèle ne peut pas utiliser d’outils, même s’ils sont définis. Il répond en texte uniquement.Résumer des résultats sans nouveaux appels, reformuler une réponse
requiredSynonyme de any. Le modèle doit utiliser au moins un outil.Équivalent de any, pour compatibilité

auto — laisser le modèle juger

C’est la valeur par défaut, celle que vous utilisez sans le savoir dès que vous omettez le paramètre.

response = client.chat.complete(
    model="mistral-large-latest",
    messages=messages,
    tools=tools,
    tool_choice="auto"  # Facultatif, c'est la valeur par défaut
)

Avec auto, le modèle évalue la requête avant d’agir. Si la question réclame des données externes qu’il ne possède pas, il appelle une fonction ; si elle est générale ou relationnelle, il répond directement en texte. Cette bascule est ce qui rend un assistant naturel : l’utilisateur n’a pas à savoir qu’il existe des outils derrière l’écran.

# Le modèle appellera une fonction
messages = [{"role": "user", "content": "Statut du paiement T1001 ?"}]
# → tool_call: retrieve_payment_status(transaction_id="T1001")

# Le modèle répondra directement
messages = [{"role": "user", "content": "Bonjour, comment allez-vous ?"}]
# → content: "Bonjour ! Je suis un assistant de paiements. Comment puis-je vous aider ?"

Le second exemple mérite qu’on s’y arrête : forcer un appel de fonction sur un simple bonjour produirait un retrieve_payment_status avec un identifiant inventé. C’est précisément ce qu’auto évite, et c’est pourquoi ce mode reste le plus courant en production.

any — imposer une sortie structurée

À l’inverse, il existe des contextes où le texte libre n’a aucune valeur pour vous. Un pipeline d’ingestion qui traite dix mille documents attend un JSON exploitable, pas une phrase polie. Avec any, le modèle doit appeler au moins une fonction, même si la question ne semblait pas l’exiger.

response = client.chat.complete(
    model="mistral-large-latest",
    messages=messages,
    tools=tools,
    tool_choice="any"
)

L’usage le plus fréquent détourne d’ailleurs le function calling de sa vocation première : on ne déclare pas une fonction pour l’exécuter, mais pour obtenir un schéma de sortie garanti. Ici, extract_entities ne sera jamais appelée côté serveur ; c’est le contrat JSON qui nous intéresse.

# Extraction structurée : forcer le modèle à extraire des données
tools = [{
    "type": "function",
    "function": {
        "name": "extract_entities",
        "description": "Extrait les entités nommées d'un texte",
        "parameters": {
            "type": "object",
            "properties": {
                "persons": {"type": "array", "items": {"type": "string"}},
                "organizations": {"type": "array", "items": {"type": "string"}},
                "dates": {"type": "array", "items": {"type": "string"}}
            },
            "required": ["persons", "organizations", "dates"]
        }
    }
}]

messages = [{"role": "user", "content": "Sophia Yang de Mistral a présenté "
            "le function calling le 15 mars 2026."}]

response = client.chat.complete(
    model="mistral-large-latest",
    messages=messages,
    tools=tools,
    tool_choice="any"  # Force l'extraction
)

# → extract_entities(persons=["Sophia Yang"],
#                     organizations=["Mistral"],
#                     dates=["15 mars 2026"])

Notez au passage required : c’est un synonyme d’any, présent pour la compatibilité avec du code écrit pour d’autres API. Les deux valeurs produisent le même comportement, choisissez celle qui rend votre code le plus lisible et tenez-vous-y.

none — couper les outils sans les retirer

La troisième valeur répond à un besoin moins évident. Vous avez déjà récupéré vos données via plusieurs tool_calls, et vous voulez maintenant que le modèle en fasse la synthèse. Si vous relancez la conversation en auto, rien n’empêche le modèle de rappeler une fonction « pour vérifier », ce qui allonge la latence et peut lancer une boucle sans fin. none interdit tout nouvel appel tout en laissant les définitions d’outils dans le contexte, ce qui aide le modèle à parler juste des données qu’il a sous les yeux.

response = client.chat.complete(
    model="mistral-large-latest",
    messages=messages,
    tools=tools,
    tool_choice="none"
)

Trois usages reviennent constamment : demander un résumé après une série d’appels de fonctions, faire reformuler une réponse sans déclencher de nouveaux appels, et tester en développement le comportement du modèle privé de ses outils, ce qui révèle vite les questions auxquelles il croit pouvoir répondre de mémoire.

# Après avoir récupéré des données via tool_calls...
# Demander un résumé sans nouveaux appels
messages.append({
    "role": "user",
    "content": "Résume les informations de tous les paiements que tu as trouvés."
})

response = client.chat.complete(
    model="mistral-large-latest",
    messages=messages,
    tools=tools,
    tool_choice="none"  # Le modèle résume avec ce qu'il a déjà
)

Adapter le réglage au fil de la conversation

Rien n’oblige à fixer tool_choice une fois pour toutes au démarrage. En production, il est fréquent de le calculer à chaque tour à partir du contexte : premier message de la conversation, intention détectée dans la demande, nombre d’appels déjà consommés. La fonction ci-dessous illustre cette logique dans sa forme la plus simple — le modèle reste libre par défaut, sauf quand l’utilisateur demande explicitement une synthèse.

def get_tool_choice(user_input: str, turn_count: int) -> str:
    """Détermine le tool_choice selon le contexte."""

    # Premier tour : laisser le modèle décider
    if turn_count == 0:
        return "auto"

    # Si l'utilisateur demande explicitement un résumé
    if any(mot in user_input.lower() for mot in ["résume", "récapitule", "synthèse"]):
        return "none"

    # Par défaut
    return "auto"

En pratique, gardez auto comme réglage de fond : c’est le plus naturel et le plus flexible. Réservez any aux pipelines d’extraction de données structurées, et none aux moments où vous devez empêcher les boucles infinies de tool_calls. Une seule contrainte est absolue : ne changez jamais de tool_choice au milieu du traitement d’un tool_call. Terminez d’abord le cycle en renvoyant tous les messages tool attendus, puis ajustez le paramètre au tour suivant — sans quoi l’historique devient incohérent et l’API rejette la requête.

Points clés à retenir

  • tool_choice contrôle quand le modèle utilise les fonctions
  • auto = le modèle décide librement (défaut, recommandé pour les chatbots)
  • any = le modèle est forcé d’appeler un outil (pipelines, extraction structurée)
  • none = les outils sont désactivés (résumés, reformulation)
  • Adaptez tool_choice dynamiquement selon le contexte de la conversation