AI Agents

Maîtriser l'appel de fonctions : créer des agents qui agissent

Les grands modèles de langage (LLM) sont de puissants processeurs de langage naturel, mais ce sont des systèmes intrinsèquement fermés. Ils ne peuvent pas vérifier la météo, interroger une base de données ou envoyer un e-mail. Ils ne peuvent que prédire le jeton suivant sur la base de leurs données d'entraînement. Pour combler le fossé entre les connaissances statiques et l'action dynamique, les développeurs utilisent l'appel de fonctions. Cette technique permet à un LLM de « halluciner » une décision d'utiliser un outil, générant une sortie JSON structurée que votre application peut analyser et exécuter. Bien conçu, l'appel de fonctions transforme un chatbot en agent autonome capable d'interagir avec le monde réel.

L'anatomie d'un appel de fonction

L'appel de fonctions n'est pas de la magie ; c'est un processus de décodage contraint. Le modèle est sollicité avec une liste de fonctions disponibles et leurs schémas. Au lieu de générer du texte libre, le modèle génère une structure JSON spécifique contenant le nom de la fonction et les arguments. Votre code intercepte ensuite ce JSON, le valide par rapport au schéma et exécute la méthode Python ou l'appel API correspondant.

Le composant critique ici est le schéma JSON. Contrairement à la documentation d'API standard, qui peut être ambiguë, l'appel de fonctions exige des définitions rigides et explicites. Le modèle doit savoir exactement quels types de données sont attendus, quels champs sont optionnels et quelles valeurs d'énumération sont autorisées. Si votre schéma est vague, le modèle fera des suppositions, entraînant des erreurs d'exécution.

Implémentation pratique en Python

Ci-dessous, un exemple simplifié utilisant une interface LLM générique. Notez comment nous définissons d'abord l'outil, puis sollicitons le modèle pour qu'il le choisisse.

import json
from typing import Callable, Dict, Any

# 1. Définir la logique de la fonction
def get_weather(city: str) -> str:
    """Simule la récupération de données météo."""
    # Dans une application réelle, cela appellerait une API externe
    return f"La météo à {city} est ensoleillée et de 72F."

# 2. Définir le schéma pour le LLM
weather_tool = {
    "name": "get_weather",
    "description": "Obtenir la météo actuelle pour une ville donnée.",
    "parameters": {
        "type": "object",
        "properties": {
            "city": {
                "type": "string",
                "description": "Le nom de la ville."
            }
        },
        "required": ["city"]
    }
}

# 3. La sortie du LLM (Simulée)
# Dans un scénario réel, vous enverriez le prompt et les définitions des outils à l'API.
llm_response = """
{
    "tool_name": "get_weather",
    "arguments": {
        "city": "San Francisco"
    }
}
"""

# 4. Analyser et exécuter
def dispatch_call(response_json: str) -> Any:
    data = json.loads(response_json)
    tool_name = data["tool_name"]
    args = data["arguments"]
    
    # Associer les noms d'outils aux méthodes réelles
    tool_map: Dict[str, Callable] = {
        "get_weather": get_weather
    }
    
    if tool_name in tool_map:
        return tool_map[tool_name](**args)
    else:
        raise ValueError(f"Outil inconnu : {tool_name}")

result = dispatch_call(llm_response)
print(result)

Gestion de l'ambiguïté et des erreurs

L'un des pièges les plus courants dans l'appel de fonctions est le sous-dimensionnement du schéma. Si vous exposez trop de paramètres, le modèle devient confus quant à ceux qu'il doit remplir. Les meilleures pratiques suggèrent de garder les fonctions individuelles atomiques. Si vous avez besoin de données complexes, créez une fonction spécifique pour ce cas d'utilisation plutôt qu'une fonction générique « query » avec cinquante champs optionnels.

De plus, vous devez gérer les erreurs d'analyse JSON. Même les modèles les plus robustes produisent occasionnellement du JSON mal formé, surtout lorsqu'ils traitent de grands contextes. Enveloppez toujours votre logique d'analyse JSON dans un bloc try-catch. Si l'analyse échoue, ne faites pas planter l'agent. Au lieu de cela, renvoyez le message d'erreur au LLM avec un prompt comme : « Votre sortie précédente était un JSON invalide. Veuillez réessayer. » Cette boucle d'auto-correction améliore considérablement la fiabilité.

Considérations de sécurité

L'appel de fonctions introduit une surface de sécurité significative. Le LLM génère des arguments de code que votre backend exécute. Si vous autorisez une exécution arbitraire, une attaque par injection de prompt pourrait être catastrophique. Par exemple, un utilisateur pourrait tromper le modèle pour qu'il appelle une fonction comme delete_database ou send_email avec des charges malveillantes.

Pour atténuer cela, implémentez un système de liste blanche. N'autorisez jamais le LLM à choisir dynamiquement des fonctions à partir d'un grand pool sans validation stricte. De plus, ajoutez des étapes de confirmation avec intervention humaine pour les actions à haut risque. Avant d'exécuter une fonction qui modifie des données ou dépense de l'argent, mettez l'agent en pause et exigez une approbation explicite de l'utilisateur.

Conclusion

L'appel de fonctions est la colonne vertébrale des agents IA modernes. Il fournit l'interface structurée entre l'intention en langage naturel et l'exécution programmatique. En concevant des schémas précis, en implémentant une gestion robuste des erreurs et en priorisant la sécurité, vous pouvez construire des agents qui ne sont pas seulement conversationnels, mais véritablement opérationnels. Commencez petit avec des outils en lecture seule simples, maîtrisez le modèle et élargissez progressivement les capacités de votre agent. L'avenir de l'IA ne consiste pas seulement à parler ; il s'agit d'agir.

Share: