Vous avez un agent IA, et vous souhaitez qu’il fonctionne dans un environnement sécurisé où il a accès aux bonnes ressources, comme un système de fichiers et la capacité d’exécuter des commandes (par exemple, des commandes shell), sans risque de « casser quoi que ce soit ». Quelles sont vos options ?
La meilleure approche est de donner à votre agent IA un bac à sable (sandbox). Dans un bac à sable, l’agent peut interagir en toute sécurité avec une machine Linux, travailler avec le système de fichiers et exécuter des commandes spécifiques tout en étant restreint d’effectuer des opérations potentiellement dangereuses.
Avec cette configuration, nous pouvons construire des applications puissantes. Par exemple, un agent de codage capable de :
- Créer et modifier des fichiers de code dans le système de fichiers
- Exécuter des commandes comme git, python ou node
- Collaborer avec les développeurs en exécutant et testant le code directement dans l’environnement
Dans cet article, nous allons parcourir la construction d’un tel agent de codage. Nous utiliserons les LLM de Novita avec appel de fonction, associés au bac à sable (sandbox) d’agent Novita comme environnement sécurisé. Pour couronner le tout, nous créerons une interface conviviale avec Gradio, et la déploierons sur Hugging Face Spaces.
Avant de choisir un modèle pour cette configuration, consultez Cas d’utilisation des grands modèles de langage pour les agents de codage pour des schémas pratiques tels que les questions-réponses sur les dépôts, le triage des tests, le refactoring et les workflows routés par outils.
Plongeons-nous !
Agent Sandbox
L’Agent Sandbox de Novita est un environnement d’exécution conçu spécifiquement pour les agents IA. Il fournit une configuration cloud sécurisée et isolée qui fonctionne comme un ordinateur virtuel. Dans cet environnement, les agents peuvent exécuter en toute sécurité le code généré sans risquer le système sous-jacent.
Fonctionnalités clés de l’Agent Sandbox Novita
- Sécurisé : Le bac à sable est totalement isolé, donc l’agent n’a accès qu’à ses propres ressources.
- Démarrage rapide : Les nouveaux environnements se mettent en place en moins de 200 ms.
- Machine virtuelle : Comme le bac à sable se comporte comme une VM, les agents peuvent exécuter du code dans n’importe quel langage de programmation.
- Pause et reprise : Vous pouvez mettre un bac à sable en pause à tout moment et le reprendre plus tard.
- Tâches en arrière-plan : Les agents peuvent exécuter des tâches en arrière-plan et récupérer les résultats de manière asynchrone.
Installation du SDK
Pour utiliser le Novita Sandbox, vous aurez besoin du SDK, qui prend en charge Python et TypeScript/JavaScript. Pour ce tutoriel, nous utiliserons le SDK Python :
pip install novita-sandbox
Après l’installation, définissez votre clé API Novita comme variable d’environnement :
export NOVITA_API_KEY=your_api_key_here
Test du bac à sable
Une fois tout configuré, créons un bac à sable et exécutons quelques opérations de base :
from novita_sandbox.code_interpreter import Sandbox
sandbox = Sandbox.create()
files = sandbox.files.list("/")
for file in files:
print(file.name)
result = sandbox.commands.run('pwd')
print(result)
sandbox.kill()
Cet exemple montre comment :
- Créer une instance de bac à sable
- Accéder au système de fichiers avec l’objet files* Exécuter des commandes avec la méthode commands.run
- Libérer les ressources avec kill une fois que vous avez terminé
Maintenant que nous avons exploré les bases de l’accès au système de fichiers et de l’exécution de commandes, nous sommes préts à construire notre agent de codage qui utilsera ces capacités du bac à sable comme outils.
Construction d’un agent de codage
Pour construire notre agent de codage, nous avons besoin d’un LLM qui prend en charge l’appel de fonction. Novita fournit plusieurs modèles capables de le faire. Pour que notre agent se comporte comme un assistant de codage, il doit disposer de l’ensemble de fonctions approprié.
Réfléchissons à ce que fait un codeur humain. Il érit, lit et exécute généralement du code. Donc notre agent doit pouvoir :
- Érire dans un fichier
- Lire un fichier
- Exécuter des commandes
- Érire dans plusieurs fichiers à la fois
Configuration de l’agent
Comme les modèles de Novita sont compatibles OpenAI, nous pouvons utilser le SDK OpneAI pour interagir avec eux. Installons-le :
pip install openai
Après l’installation, définissez votre clé API Novita comme variable d’environnement comme nous l’avons fait plus tôt. Une fois cela fait, nous pouvons commencer à coder en ajoutant nos imports :
from openai import OpeneAI
import os
import json
from novita_andbox.code_interpreter import Sandbox
Maintenant, créez notre instance client OpenneAI :
client = OpeneAI(
base_url="https://api.novita.ai/openai",
api_key=os.environ["NOVITA_API_KEY"],
)
Ici, nous pointons le client vers l’URL de base de Novita au lieu de celle d’OpeneAI et utilisons notre clé API Novita pour l’autentification.
Ensuite, créez l’instance du bac à sable que notre agent utilsera :
sandbox = Sandbox.create(timeout=1200)
Le paramètre timeout spécifie combien de temps le bac à sable doit rester actif. Dans ce cas, nous l’avons fixé à 10 minutes.
Définitions des fonctions
Maintenant, nous pouvons déinir les fonctions que notre agent utilsera.
1. Lire un fichier
Cette fonction prend un chemin de fichier et lit son contenu en utilisan l’objet files du bac à sable.
def read_file(path: str):
print(f"[DEBUG] read_file called with path: {path}")
try:
content = sandbox.files.read(path)
print(f"[DEBUG] read_file result: {content}")
return content # returns string content
except Exception as e:
print(f"[DEBUG] read_file error: {e}")
return f"Error reading file: {e}"
2. Érire un fichier
Cette fonction érit des données dans un chemin de fichier spécifié.
def write_file(path: str, data: str):
print(f"[DEBUG] write_file called with path: {path}")
try:
sandbox.files.write(path, data)
msg = f"File created successfully at {path}"
print(f"[DEBUG] {msg}")
return msg
except Exception as e:
print(f"[DEBUG] write_file error: {e}")
return f"Error writing file: {e}"
3. Érire plusieurs fichiers
Cette fonction fonctionne comme write_file mais gère plusieurs fichiers à la fois.
def write_files(files: list):
print(f"[DEBUG] write_files called with {len(files)} files")
try:
sandbox.files.write_files(files)
msg = f"{len(files)} file(s) created successfully"
print(f"[DEBUG] {msg}")
return msg
except Exception as e:
print(f"[DEBUG] write_files error: {e}")
return f"Error writing multiple files: {e}"
4. Exécuter des commandes
Cette fonction exécute des commandes shell dans le bac à sable et renvoie la sortie standard.
def run_commands(command: str):
print(f"[DEBUG] run_commands called with commands: {command}")
try:
result = sandbox.commands.run(command)
print(f"[DEBUG] run_commands result: {result}")
return result.stdout # returns CommandResult object
except Exception as e:
print(f"[DEBUG] run_commands error: {e}")
return f"Error running commands: {e}"
Enregistrement des outils
Maintenant que nous avons toutes nos fonctions, nous allons les enregistrer comme outils que le LLM peut appeler en cas de besoin. Chaque définition d’outil inclut le nom de la fonction, la description et le schéma des paramètres.
tools = [
{
"type": "function",
"function": {
"name": "read_file",
"description": "Lire le contenu d'un fichier dans le bac à sable",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Chemin du fichier dans le bac à sable"}
},
"required": ["path"],
},
},
},
{
"type": "function",
"function": {
"name": "write_file",
"description": "Écrire un seul fichier dans le bac à sable",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Chemin du fichier dans le bac à sable"},
"data": {"type": "string", "description": "Contenu à écrire"}
},
"required": ["path", "data"],
},
},
},
{
"type": "function",
"function": {
"name": "write_files",
"description": "Écrire plusieurs fichiers dans le bac à sable",
"parameters": {
"type": "object",
"properties": {
"files": {
"type": "array",
"items": {
"type": "object",
"properties": {
"path": {"type": "string"},
"data": {"type": "string"}
},
"required": ["path", "data"]
}
}
},
"required": ["files"]
},
},
},
{
"type": "function",
"function": {
"name": "run_commands",
"description": "Exécuter une seule commande shell dans le répertoire de travail du bac à sable",
"parameters": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "La commande shell à éexecuter, par exemple 'ls' ou 'python main.py'"
}
},
"required": ["command"]
},
},
}
]
Avec les outils enregistrés, passons à la création d’une boucle de chat qui utilsera notre agent et tous les outils que nous avons définis.
Boucle de chat
Maintenant, nous allons créer une simple boucle de chat qui permet à l’utilisateur d’interagir avec l’agent de codage. La boucle maintiendra une liste de messages et gérera les appels de fonctions chaque fois que l’agent en demandera un.
messages = []
print("💬 Saisissez vos requêtes (tapez 'exit' pour quitter) :")
while True:
user_input = input("Vous : ")
if user_input.lower() == "exit":
break
# Ajouter le message de l'utilisateur
messages.append({"role": "user", "content": user_input})
# Envoyer au modèle
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
)
assistant_msg = response.choices[0].message
messages.append(assistant_msg)
if assistant_msg.tool_calls:
print(f"[DEBUG] L'assistant a demandé {len(assistant_msg.tool_calls)} appel(s) d'outil.")
for tool_call in assistant_msg.tool_calls:
fn_name = tool_call.function.name
fn_args = json.loads(tool_call.function.arguments)
print(f"[DEBUG] Appel d'outil détecté : {fn_name} avec args {fn_args}")
if fn_name == "read_file":
fn_result = read_file(**fn_args)
elif fn_name == "write_file":
fn_result = write_file(**fn_args)
elif fn_name == "write_files":
fn_result = write_files(**fn_args)
elif fn_name == "run_commands":
fn_result = run_commands(**fn_args)
else:
fn_result = f"Erreur : Outil inconnu {fn_name}"
print(f"[DEBUG] Outil inconnu demandé : {fn_name}")
# Ajouter le résultat
messages.append({
"tool_call_id": tool_call.id,
"role": "tool",
"content": str(fn_result),
})
# Obtenir la réponse finale du modèle avec les résultats des outils
follow_up = client.chat.completions.create(
model=model,
messages=messages,
)
final_answer = follow_up.choices[0].message
messages.append(final_answer)
print("Assistant :", final_answer.content)
else:
print("Assistant :", assistant_msg.content)
sandbox.kill()
print("[DEBUG] Sandbox terminé. 👋")
Cette boucle de chat maintient l’interaction en cours, permet à l’agent d’appeler n’importe quel outil enregistré en cas de besoin, et nettoie le bac à sable lorsque l’utilisateur quitte.
Création d’une interface utilisateur avec Gradio
Nous avons maintenant un agent de codage entièrement fonctionnel qui peut discuter avec nous, mais interagir via un REPL n’est pas très excitant. Rendons l’expérience plus engageante en donnant à notre agent une simple interface Gradio.
Créer une interface Gradio es simple. Nous utilseron gr.ChatInterface pour gérer nos interctions de chat et la lier à la logique que nous avons construite plus tôt. En parallèle, nous incluons une interface de commande pour exécuter des commandes shell dans le bac à sable, ainsi qu’un menu déroulant permettant de sélectionner le modèle à utilser.
Pour mettre à jour notre code précédent pourprendre en charge Gradio, nous remplacerons la boucle de chat et les deux dernières lignes par ce qui suit :
# --- Messages de chat persistants ---
messages = []
# --- Sélecteur de modèle global ---
def set_model(selected_model):
global model
model = selected_model
print(f"[DEBUG] Modèle changé pour : {model}")
return f"✅ Modèle changé pour **{model}**"
def chat_fn(user_message, history):
global messages, model
messages.append({"role": "user", "content": user_message})
# Envoyer au modèle
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
)
assistant_msg = response.choices[0].message
messages.append(assistant_msg)
output_text = ""
if assistant_msg.tool_calls:
print(f"[DEBUG] Assistant a demandé {len(assistant_msg.tool_calls)} appel(s) d'outil.")
for tool_call in assistant_msg.tool_calls:
fn_name = tool_call.function.name
fn_args = json.loads(tool_call.function.arguments)
print(f"[DEBUG] Appel d'outil détecté : {fn_name} avec args {fn_args}")
if fn_name == "read_file":
fn_result = read_file(**fn_args)
elif fn_name == "write_file":
fn_result = write_file(**fn_args)
elif fn_name == "write_files":
fn_result = write_files(**fn_args)
elif fn_name == "run_commands":
fn_result = run_commands(**fn_args)
else:
fn_result = f"Erreur : Outil inconnu {fn_name}"
messages.append({
"tool_call_id": tool_call.id,
"role": "tool",
"content": str(fn_result),
})
follow_up = client.chat.completions.create(
model=model,
messages=messages,
)
final_answer = follow_up.choices[0].message
messages.append(final_answer)
output_text = final_answer.content
else:
output_text = assistant_msg.content
return output_text
# --- Fonction de l'interface de commande ---
def execute_command(command):
if not command.strip():
return "⚠️ Veuillez entrer une commande."
print(f"[DEBUG] Exécution de la commande depuis l'interface : {command}")
output = run_commands(command)
return f"```bash\
{output}\
```" if output else "✅ Command exéctée (aucune sortie)."
# --- Interface Gradio ---
with gr.Blocks(title="App Novita Sandbox") as demo:
gr.Markdown("## 🧠 Agent Novita Sandbox")
gr.Markdown(
"Cette application est un **agent de code** alimenté par IA qui vous permet de discuter avec des assistants intelligents basés sur **les LLM Novita AI**. Ces agents peuvent écrire, lire et exécuter du code en toute sécurité dans un **bac à sable Novita**, fournissant un environnement sécurisé pour exécuter des commandes, tester des scripts et gérer des fichiers, le tout via une interface de chat intuitive avec sélection de modèle et exécution de commandes intégrée."
)
with gr.Row(equal_height=True):
# Gauche : Interface de chat
with gr.Column(scale=2):
gr.Markdown("### 💬 Interface de chat")
gr.ChatInterface(chat_fn)
# Droite : Interface de commande
with gr.Column(scale=1):
gr.Markdown("### 💻 Interface de commande")
# Sélecteur de modèle
model_selector = gr.Dropdown(
label="Sélectionner un modèle",
choices=[
"meta-llama/llama-3.3-70b-instruct",
"deepseek/deepseek-v3.2-exp",
"qwen/qwen3-coder-30b-a3b-instruct",
"openai/gpt-oss-120b",
"moonshotai/kimi-k2-instruct",
],
value=model,
interactive=True,
)
model_status = gr.Markdown(f"✅ Modèle actuel : **{model}**")
model_selector.change(set_model, inputs=model_selector, outputs=model_status)
command_input = gr.Textbox(
label="Commande",
placeolder="par ex., ls, python main.py",
lines=1,
)
with gr.Row():
run_btn = gr.Button("Exécuter", variant="primary", scale=0)
command_output = gr.Markdown("La sortie de la commande apparaîtra ici...")
run_btn.click(execute_command, inputs=command_input, outputs=command_output)
# --- Nettoyage à la sortie ---
atexit.register(lambda: (sandbox.kill(), print("[DEBUG] Sandbox terminé. 👋")))
if __name__ == "__main__":
demo.launch()
Dans cette version, la fonction chat_fn gère chaque échange de messages entre l’utilisateur et l’agent. Le gr.ChatInterface prend cette fonction en entrée et gère automatiquement les interctions de l’interface.
Lorsque l’application Gradio démarre, elle exétue votre agent dans le navigateur, offrant à l’utilisateur une interface de chat propre et interactive. Enfin, nous enregistrons une routine de nettoyage utilisant atexit pour garantir que le bac à sable est correctement terminé lorsque l’application s’arête.
Maintenant, nous avons un agent de codage alimenté par IA s’écutant en toute sécurité dans un bac à sable, avec une interface de chat Gradio conviviale.
Vous pouvez trouver le code complet sur GitHub.
Test de l’agent de codage
Pour utilser l’agent, nous devons exécuter notre code Gradio en tant que script.
python gradio_chat.py
Lorsque nous le faisons, notre application Gradio s’écute sur localhost. Ainsi, nous pouvons converser avec notre agent de codage et l’agent exécutera toutes nos actions dans le bac à sable.
Depuis l’application Gradio, nous ne voyons que les discussions de notre agent, mais si nous allons dans notre terminal, nous pouvons également voir les sorties de débogage montrant quelle commande la fonction que l’agent a appelée pour répondre à la demande de l’utilisateur.
Le fait que nous ayons accès à un outil de fichiers et à un outil de commande signifie qu’il n’y a presque rien que nous ne puissions pas coder, mais au lieu de coder directement, nous donnons des instructions à notre agent et c’est lui qui écrit et exécute les codes pour nous.
Déploiement sur Hugging Face Spaces
Nous avons actuellement notre agent de codage qui fonctionne localement sur notre ordinateur. Maintenant, rendons-le accessible au reste du monde en le déployant sur Hugging Face Spaces. Hugging Face Spaces nous permet d’héberger à la fois notre code et notre application au même endroit. Commençons.
Créer l’espace
Rendez-vous sur Hugging Face et créez un nouvel espace pour votre agent de codage en lui donnant un nom unique.

Ensuite, sélectionnez le SDK pour l’espace, qui dans notre cas est Gradio. Choisissez le modèle Blank puisque nous avons déjà notre code d’application.

Puis, sélectionnez le matériel. Comme notre agent et notre bac à sable sont propulsés par Novita, nous n’avons pas besoin de matériel spécialisé. L’option Basic CPU est suffisante. Une fois terminé, cliquez sur Create Space.

Hugging Face créera l’espace avec un fichier README.md et un fichier .gitignore.
Il existe plusieurs façons d’ajouter votre code, mais la plus simple est de cliquer sur Contribute → Add file.

Créez un fichier requirements.txt et incluez les dépendances suivantes :
openai
novita-sandbox
Ajouter des variables d’environnement
Avant de pouvoir exécuter notre application, nous devons définir notre NOVITA_API_KEY comme variable d’environnement.
Pour ce faire, naviguez vers les paramètres de votre espace, faites défiler la section Variables et secrets, et ajoutez un nouveau secret nommé NOVITA_API_KEY avec votre clé API comme valeur.
Configurer l’application
La variable d’environnement étant définie, il est temps de crér notre application.
Créez un nouveau fichier nommé app.py et collez-y l’ensemble de notre code d’agent Gradio.

Une fois que vous enregistrez le fichier, Hugging Face démarrera automatiquement la contruction de votre espace.

Après que le processus de construction soit terminé, votre agent de codage sera en ligne et accessible sur Hugging Face Spaces.
Vous pouvez maintenant discuter avec votre agent via l’interface de chat interctive.
De plus, vous pouvez surveiller les logs pour voir les outils que votre agent appelle pendant l’exécution.
Et voilà, vous avez maintenant un agent de codage entièrement fonctionnel qui s’écute dans un bac à sable sécurisé, équipé d’une interface Gradio, et déplové en toute transparence sur Hugging Face Spaces.
Conclusion
Dans cet article, nous avons exploré comment exploiter le bac à sable de Novita pour construire un agent de codage entièrement fonctionnel capable de lire et de créer des fichiers, d’exécuter des commandes et de fonctionner en toute sécurité dans un environnement sécurisé.
Ce que nous avons construit ici n’est que le début. Le bac à sable ouvre la porte à d’innombrables possibilités, de la création d’agents de visualisation de données alimentés par IA au développement d’agents d’utilisation d’ordinateur capables d’interagir intelligemment avec les systèmes.
Presque tout est possible lorsque vous combinez un agent avec un ensemble d’outils dédiés comme le bac à sable.