Home » AI » Comment sécuriser le tool calling avec Gemma 4 ?

Comment sécuriser le tool calling avec Gemma 4 ?

Le tool calling avec Gemma 4 devient utile quand le modèle peut lire un environnement local et exécuter des actions encadrées. Le vrai sujet n’est pas l’appel d’outil, mais les limites à poser pour éviter fuite de fichiers, exécution dangereuse et comportements imprévus.

Pourquoi un agent a besoin d’outils locaux ?

Un agent a besoin d’outils locaux parce qu’il devient vraiment utile lorsqu’il peut observer l’état local, agir dessus de façon contrôlée, puis tenir compte du résultat dans sa réponse.

Un chatbot classique produit du texte à partir du prompt et de ce que le modèle a appris pendant son entraînement. Le RAG, pour Retrieval-Augmented Generation, consiste à récupérer des documents ou des données externes avant de générer une réponse. C’est utile pour citer une documentation interne, interroger une base de connaissances ou enrichir une réponse avec des données récentes.

Une API web en lecture seule aide à mieux répondre, mais elle ne suffit pas toujours. Elle ne permet pas forcément d’inspecter un projet local, d’analyser des fichiers, de lancer un calcul ou de vérifier une hypothèse dans votre environnement de travail. Pour cela, l’agent doit pouvoir utiliser des outils proches du contexte réel : votre dépôt, vos fichiers autorisés, vos scripts de test, vos résultats intermédiaires.

Dans un montage Gemma 4 avec tool calling local, le modèle ne doit pas avoir accès directement au système. Il doit seulement pouvoir demander l’exécution de fonctions déclarées, limitées et observables. Le tool calling désigne ce mécanisme : le modèle propose un appel de fonction, puis l’application décide si elle l’exécute, avec quels paramètres, et ce qu’elle renvoie au modèle.

La règle est simple : pas de shell libre, pas d’accès disque global, pas de réseau implicite. L’agentivité pratique consiste à raisonner sur l’état du système et sur les conséquences d’une action, pas seulement à produire du texte plausible.

Avec des outils locaux bien bornés, l’agent peut par exemple :

  • Lister un dossier de projet autorisé pour comprendre sa structure.
  • Lire un fichier de configuration précis, après validation du chemin.
  • Exécuter un calcul Python restreint, sans accès arbitraire au système.
  • Synthétiser les résultats observés au lieu d’inventer une conclusion.

La documentation officielle d’Ollama décrit l’orchestration des tool calls et des fonctions exposées : https://docs.ollama.com/capabilities/tool-calling. Pour sécuriser les chemins locaux, les modules Python os.path et pathlib documentent la normalisation et la résolution de chemins : https://docs.python.org/3/library/os.path.html et https://docs.python.org/3/library/pathlib.html. OWASP documente aussi le risque de Path Traversal, une attaque qui consiste à sortir d’un répertoire autorisé avec des chemins comme ../ : https://owasp.org/www-community/attacks/Path_Traversal.

Capacité Limite principale Exemple d’usage
Chatbot Répond sans observer l’environnement local. Expliquer un concept ou reformuler un texte.
RAG Récupère des informations, mais agit peu ou pas. Répondre avec une documentation interne.
Agent local outillé Doit être strictement limité et audité. Inspecter un projet, lire un fichier autorisé, lancer un calcul contrôlé.

Comment fonctionne la boucle d’orchestration ?

La boucle d’orchestration est le cœur du tool calling avec Gemma 4. Le principe reste le même quel que soit l’outil branché derrière : déclarer les outils disponibles, laisser le modèle demander un appel, exécuter cet appel côté application, puis renvoyer le résultat au modèle pour produire une réponse finale.

Le flux commence par des fonctions Python classiques. Chaque fonction est ensuite décrite avec un schéma JSON, c’est-à-dire une description structurée des paramètres attendus : noms, types, champs obligatoires, contraintes simples. Ce schéma aide le modèle à produire un appel bien formé, mais il ne remplace jamais une validation côté application.

Ensuite, le registre d’outils est transmis au runtime, par exemple Ollama, avec le prompt utilisateur. Gemma 4 peut alors répondre avec des tool_calls, c’est-à-dire des demandes d’exécution. L’orchestrateur intercepte ces demandes, valide les arguments reçus, choisit la bonne fonction locale, exécute l’action, ajoute le résultat comme message de rôle outil, puis relance le modèle pour obtenir une synthèse lisible.

Le point d’architecture à ne pas rater : le modèle propose une action, mais l’application garde le contrôle. Gemma 4 ne doit jamais exécuter directement du code système, lire librement le disque ou appeler un service sans filtre. Au modèle, la décision et la synthèse. À l’orchestrateur, la validation, l’exécution, les logs, les limites de privilèges et les retours d’erreur clairs.

def get_order_status(order_id):
    # Retourne un statut depuis une source contrôlée.
    return {"order_id": order_id, "status": "shipped"}

registry = {
    "get_order_status": {
        "function": get_order_status,
        "schema": {
            "type": "object",
            "properties": {
                "order_id": {"type": "string"}
            },
            "required": ["order_id"]
        }
    }
}

messages = [
    {"role": "user", "content": "Quel est le statut de la commande A123 ?"}
]

response = call_model(
    model="gemma4",
    messages=messages,
    tools=registry
)

if "tool_calls" in response:
    for call in response["tool_calls"]:
        name = call["name"]
        args = call["arguments"]

        if name not in registry:
            raise ValueError("Outil non autorisé")

        validate(args, registry[name]["schema"])

        result = registry[name]["function"](**args)

        messages.append({
            "role": "tool",
            "name": name,
            "content": result
        })

final_response = call_model(
    model="gemma4",
    messages=messages
)

Cette boucle doit être défensive par défaut. Les garde-fous essentiels sont simples, mais non négociables :

  • Allowlist d’outils : seuls les outils déclarés peuvent être appelés.
  • Validation stricte des arguments : types, champs obligatoires et valeurs acceptées.
  • Timeouts : aucun outil ne doit bloquer indéfiniment l’application.
  • Logs : chaque appel doit être traçable, sans exposer de secrets.
  • Refus par défaut : une demande ambiguë ou non autorisée est rejetée.
  • Erreurs non sensibles : le modèle reçoit une erreur claire, pas une stack trace complète.

Comment sandboxer l’exploration de fichiers ?

L’Exploration de fichiers doit être limitée à un répertoire de base sûr, puis chaque chemin demandé par le modèle doit être normalisé et vérifié avant toute lecture. Le risque principal est simple : une entrée produite par Gemma 4 comme /, ~, ../ ou ../../etc peut exposer des fichiers sensibles si elle est utilisée telle quelle.

Le Path Traversal, documenté par OWASP, désigne une technique qui consiste à sortir d’un répertoire autorisé en manipulant les chemins. Exemple classique : demander ../../etc/passwd au lieu d’un dossier prévu par l’application.

Le pattern de sécurité tient en quelques règles strictes :

  • Choisir un SAFE_BASE_DIR au démarrage.
  • Joindre la demande du modèle à ce répertoire.
  • Résoudre le chemin absolu et canonique avec Path.resolve(), documenté dans la documentation Python.
  • Vérifier que le chemin obtenu est égal au répertoire de base, ou descendant de ce répertoire.
  • Refuser tout le reste avant os.listdir, open ou toute opération disque.

Une règle ne change pas : ne jamais faire confiance à une chaîne produite par un modèle, même si elle ressemble à un chemin normal.

from pathlib import Path

SAFE_BASE_DIR = Path.cwd().resolve()
MAX_ENTRIES = 100

def list_directory_contents(path: str | None = None, limit: int = MAX_ENTRIES) -> dict:
    requested_path = path or "."
    target = (SAFE_BASE_DIR / requested_path).resolve()

    if target != SAFE_BASE_DIR and SAFE_BASE_DIR not in target.parents:
        return {
            "error": "access denied",
            "message": "Path is outside the allowed directory."
        }

    if not target.exists():
        return {
            "error": "not found",
            "message": "Directory does not exist."
        }

    if not target.is_dir():
        return {
            "error": "not a directory",
            "message": "Path exists but is not a directory."
        }

    entries = []
    for item in sorted(target.iterdir(), key=lambda p: p.name.lower())[:limit]:
        entry = {
            "name": item.name,
            "type": "directory" if item.is_dir() else "file"
        }

        if item.is_file():
            entry["size_bytes"] = item.stat().st_size

        entries.append(entry)

    return {
        "path": str(target.relative_to(SAFE_BASE_DIR)),
        "entries": entries,
        "limit": limit
    }

Avec os.path, la variante courte consiste à utiliser abspath, realpath, puis à vérifier le préfixe avec prudence. Un simple startswith est dangereux : /safe/data2 commence aussi par /safe/data. Il faut comparer le dossier exact ou vérifier safe + os.sep. pathlib rend ce contrôle plus lisible grâce à target.parents.

Entrée Comportement attendu
Racine / Refusée, car hors du répertoire autorisé.
Home ~ Non développée vers le home utilisateur, donc refusée ou introuvable.
Parent ../ Refusée si la résolution sort du répertoire de base.
Dossier autorisé Listé avec une limite d’éléments.
Fichier au lieu d’un dossier Refusé avec une erreur explicite.

Sources : OWASP Path Traversal, https://owasp.org/www-community/attacks/Path_Traversal ; Documentation Python pathlib.Path.resolve, https://docs.python.org/3/library/pathlib.html.

Comment encadrer un interpréteur Python ?

L’interpréteur Python doit être traité comme l’outil le plus risqué de votre système de tool calling. Exécuter du code généré ou demandé par Gemma 4 peut lire des fichiers, consommer du CPU, ouvrir un réseau ou lancer des processus si aucune barrière n’est posée. Restreindre Python ne consiste donc pas à masquer deux imports sensibles. Il faut contrôler tout l’environnement d’exécution.

Les garde-fous minimaux sont clairs : liste blanche de modules autorisés, suppression des builtins dangereux, interdiction des accès fichiers non prévus, pas de réseau par défaut, timeout strict, limite mémoire si possible, taille maximale d’entrée et de sortie, processus isolé, répertoire de travail sandboxé, journalisation des appels et refus explicite des commandes système. Les builtins sont les fonctions disponibles par défaut dans Python, comme open, eval, exec ou __import__. Certaines permettent de lire des fichiers, d’exécuter du code ou de charger des modules, donc elles doivent être retirées ou surveillées.

Un sandbox Python parfait dans le même processus est très difficile. Pour un usage sérieux, j’exécute le code dans un processus séparé, un conteneur, une microVM ou un environnement jetable, avec le principe du moindre privilège : seulement les permissions nécessaires, rien de plus. La documentation Python avertit explicitement sur les risques de eval et exec, et les bonnes pratiques de sécurité applicative, notamment celles de l’OWASP, recommandent l’isolation des composants à risque.

import os
import subprocess
import tempfile
from pathlib import Path

def run_python_code(code: str, timeout_seconds: int = 2) -> dict:
    if len(code) > 5_000:
        return {"stdout": "", "stderr": "Code trop long", "returncode": 1}

    allowed_prelude = """
import math
import statistics

__builtins__ = {
    "abs": abs,
    "min": min,
    "max": max,
    "sum": sum,
    "round": round,
    "len": len,
    "range": range,
    "print": print,
}
"""

    with tempfile.TemporaryDirectory() as tmp:
        sandbox = Path(tmp)
        script = sandbox / "main.py"
        script.write_text(allowed_prelude + "\\n" + code, encoding="utf-8")

        result = subprocess.run(
            ["python3", str(script)],
            cwd=sandbox,
            env={"PYTHONPATH": "", "PATH": "/usr/bin:/bin"},
            capture_output=True,
            text=True,
            timeout=timeout_seconds,
        )

        return {
            "stdout": result.stdout[:10_000],
            "stderr": result.stderr[:10_000],
            "returncode": result.returncode,
        }

Cet exemple protège partiellement contre certains risques, mais ne suffit pas seul en production :

  • Il limite le temps d’exécution avec un timeout.
  • Il exécute le code dans un processus séparé et un dossier temporaire.
  • Il réduit l’environnement et tronque les sorties trop longues.
  • Il ne désactive pas le réseau au niveau système.
  • Il ne remplace pas un conteneur, une microVM, des quotas mémoire ou une politique de fichiers stricte.

Dans la boucle d’orchestration, le modèle demande un calcul, l’orchestrateur valide la requête, exécute le code dans ce périmètre limité, puis renvoie stdout, stderr et le code retour. Le modèle ne pilote jamais directement la machine.

Quelles règles garder en production ?

En production, le bon réflexe est de considérer chaque appel d’outil comme une action non fiable tant qu’elle n’a pas été validée, limitée, tracée et rendue réversible quand c’est possible. L’architecture de tool calling reste simple sur le papier : Gemma 4 propose un appel, votre code l’exécute, puis renvoie le résultat. Le risque augmente dès qu’on branche le modèle au système local, à des fichiers, à Python ou à une API interne.

Permissions minimales. Le principe du moindre privilège consiste à donner à chaque outil uniquement les droits nécessaires à sa tâche. Un explorateur de fichiers ne doit lire qu’un répertoire autorisé, jamais tout le disque. Un interpréteur Python ne doit pas accéder au réseau, aux variables d’environnement, c’est-à-dire aux secrets chargés par le système, ni aux chemins contenant des clés ou des fichiers privés.

Validation des entrées. Les arguments doivent être validés côté serveur, même si Gemma 4 les produit dans un schéma JSON, le format texte structuré utilisé pour échanger des données. Les chemins contenant ../, les noms trop longs, les charges volumineuses et les paramètres inattendus doivent être refusés avant exécution.

Isolation d’exécution. Un outil risqué doit tourner dans un environnement séparé : conteneur, sandbox, utilisateur système dédié ou processus sans droits. Ajoutez des limites de débit, des timeouts, c’est-à-dire des durées maximales d’exécution, et des quotas de sortie pour éviter les boucles, les fuites massives ou les coûts incontrôlés.

Observabilité. Les logs doivent conserver l’outil appelé, l’horodatage, le statut, la durée et un identifiant de requête. Ils ne doivent pas stocker de données sensibles. Les messages d’erreur doivent rester sobres : pas de chemins secrets, pas de variables d’environnement, pas de traces système complètes.

Stratégie de refus. Le modèle peut demander une action invalide, ambiguë ou dangereuse. Dans ce cas, l’orchestrateur refuse par défaut, demande une clarification ou propose une alternative sûre.

  • Définir une allowlist d’outils, c’est-à-dire une liste explicite des outils autorisés.
  • Versionner les schémas JSON.
  • Valider les arguments côté serveur.
  • Refuser par défaut.
  • Isoler le runtime, c’est-à-dire l’environnement d’exécution.
  • Surveiller les appels.
  • Auditer les prompts système.
  • Prévoir une coupure d’urgence.
  • Tester les chemins ../ et les charges longues.
  • Documenter les droits de chaque outil.
Niveau Isolation recommandée Logs Limites Validations minimales
Usage local de démonstration Répertoire dédié, aucun secret accessible Appels et erreurs sans contenu sensible Timeout court et taille de sortie limitée Allowlist d’outils et validation des chemins
Prototype interne Processus séparé ou conteneur sans privilèges Journalisation centralisée avec identifiant de requête Débit limité, quotas par utilisateur Schémas JSON versionnés et tests d’entrées malveillantes
Production sensible Sandbox stricte, réseau contrôlé, secrets isolés Audit complet, alertes, rétention maîtrisée Timeouts, quotas, coupure d’urgence Refus par défaut, revue des prompts système, validation serveur obligatoire

L’objectif n’est pas de rendre Gemma 4 omnipotent, mais utile dans un périmètre que l’on comprend et que l’on maîtrise.

Jusqu’où faut-il laisser agir Gemma 4 ?

Le tool calling avec Gemma 4 devient intéressant quand il dépasse la simple consultation d’API et permet au modèle d’inspecter un environnement local, de lancer un calcul et de synthétiser le résultat. Mais cette puissance n’a de valeur que si l’orchestrateur garde la main : outils déclarés, arguments validés, sandbox fichier, Python restreint, refus par défaut, logs et limites d’exécution. Je retiens une règle simple : le modèle peut demander, l’application décide. En appliquant ce cadre, vous obtenez des agents plus utiles, sans ouvrir inutilement votre système ni vos données.

FAQ

  • Qu’est-ce que le tool calling avec Gemma 4 ?
    Le tool calling consiste à permettre à Gemma 4 de demander l’exécution d’une fonction externe, par exemple lister un dossier ou lancer un calcul. Le modèle ne fait pas l’action lui-même : il formule une demande structurée, puis votre application valide, exécute l’outil et lui renvoie le résultat.
  • Pourquoi une API en lecture seule ne suffit pas pour créer un agent ?
    Une API en lecture seule enrichit les réponses, mais elle ne permet pas toujours d’interagir avec l’environnement de travail. Un agent utile doit parfois inspecter des fichiers locaux, vérifier l’état d’un projet, exécuter un calcul ou produire une action contrôlée. C’est cette interaction encadrée qui rapproche le système d’un comportement agentique.
  • Quel est le risque principal avec un explorateur de fichiers ?
    Le risque principal est le Path Traversal, c’est-à-dire la possibilité de sortir du dossier autorisé avec des chemins comme ../, / ou ~/. Pour l’éviter, il faut définir un répertoire de base sûr, résoudre le chemin demandé en chemin absolu, puis vérifier qu’il reste bien dans ce périmètre avant toute lecture.
  • Peut-on exécuter du Python généré par un modèle en toute sécurité ?
    Il faut rester prudent. Exécuter du Python est risqué si le code peut accéder au disque, au réseau, aux processus système ou consommer trop de ressources. Pour un usage sérieux, il faut isoler l’exécution dans un processus séparé, limiter les modules, appliquer des timeouts, contrôler les sorties et utiliser le principe du moindre privilège.
  • Quelle règle simple appliquer en production ?
    La règle la plus saine est de considérer chaque appel d’outil comme non fiable jusqu’à validation. Le modèle peut proposer une action, mais l’application doit garder le contrôle : allowlist d’outils, validation des paramètres, sandbox, logs, limites d’exécution et refus par défaut.

 

 

A propos de l’auteur

Je suis Franck Scandolera, responsable de l’agence webAnalyste et de l’organisme Formations Analytics. J’accompagne les entreprises sur le tracking avancé server-side, l’Analytics Engineering, l’automatisation No/Low Code avec n8n, l’intégration de l’IA dans les process, le SEO et le GEO. J’ai travaillé pour des organisations comme Logis Hôtel, Yelloh Village, BazarChic, la Fédération Française de Football ou Texdecor. Si vous voulez cadrer des agents IA, automatiser vos workflows ou sécuriser vos données, contactez-moi.

Retour en haut
Vizyz