Lecture: 6 min.
Lorsque nous avons lancé Cloudflare Sandboxes en juin dernier, le principe était simple : les agents d’IA ont besoin de développer et d’exécuter du code, et ils doivent le faire dans un endroit sûr.
Si un agent agit comme un développeur, cela implique de cloner des référentiels, de créer du code dans de nombreux langages et d’exécuter des serveurs de développement, etc. Pour effectuer ces tâches de manière efficace, ils auront souvent besoin d’un ordinateur complet (et si ce n’est pas le cas, ils peuvent se tourner vers quelque chose de léger!).
De nombreux développeurs assemblent des solutions hétéroclites en utilisant des machines virtuelles ou des solutions de conteneurs existantes, mais ils sont confrontés à de nombreux problèmes difficiles à résoudre :
Intermittence - Chaque session nécessitant sa propre sandbox, vous devez souvent faire tourner rapidement de nombreuses sandboxes, mais vous ne souhaitez pas payer de calcul inactif en mode veille.
Restauration rapide de l’état – Chaque session doit démarrer et redémarrer rapidement, en revenant à son état précédent.
Sécurité - Les agents doivent accéder aux services en toute sécurité, mais ils ne sont pas fiables pour ce qui est des informations d’identification.
Contrôle - Le contrôle du cycle de vie de la sandbox, l’exécution de commandes, le traitement des fichiers, etc., doivent être simples.
Ergonomie - Vous devez proposer aux humains et aux agents une interface simple pour effectuer des opérations courantes.
Nous avons passé du temps à résoudre ces problèmes pour que vous n’ayez pas à le faire. Depuis notre lancement initial, nous avons fait des Sandboxes un endroit encore plus propice à l’exécution d’agents à grande échelle. Nous avons collaboré avec nos partenaires initiaux, tels que Figma, qui exécute des agents dans des conteneurs avec Figma Make :
« Figma Make est conçue pour aider les développeurs et les créateurs de tous horizons à concrétiser l’idée en production, et ce, plus rapidement. Pour atteindre cet objectif, nous avions besoin d’une solution d’infrastructure capable de fournir des sandboxes fiables et hautement évolutives, dans lesquelles nous pourrions exécuter du code non fiable, créé par l’agent et l’utilisateur. Cloudflare Containers est la solution. »
- Alex Mullans, IA et plateformes pour développeurs chez Figma
Nous voulons proposer Sandboxes à encore plus de grandes organisations, c’est pourquoi aujourd’hui nous sommes heureux d’annoncer que Sandboxes et les conteneurs Cloudflare sont tous deux proposés en disponibilité générale.
Examinons certaines des récentes modifications apportées à Sandboxes :
L’injection d’identifiants sécurisés vous permet de passer des appels authentifiés sans que l’agent ne dispose d’un accès aux identifiants
La prise en charge du PTY vous offre, à vous et à votre agent, un véritable terminal
Les interpréteurs de code persistants donnent à votre agent un endroit où exécuter les fonctions Python, JavaScript et TypeScript avec état, prêts à l’emploi
Les processus en arrière-plan et les URL de prévisualisation en direct proposent un moyen simple d’interagir avec les serveurs de développement et de vérifier les modifications en cours
La surveillance du système de fichiers améliore la vitesse d’itération à mesure que les agents effectuent des modifications
Les instantanés vous permettent de récupérer rapidement la session de codage d’un agent
Des limites plus élevées et la tarification CPU active vous permettent de déployer une flotte d’agents à grande échelle sans payer pour les cycles processeur inutilisés
Sandboxes : Principes fondamentaux
Avant d’aborder les récentes modifications, examinons rapidement les principes de base.
Un Cloudflare Sandbox est un environnement persistant et isolé, alimenté par Cloudflare Containers. Vous demandez une sandbox par son nom. Si elle est en cours d’exécution, vous l’obtenez. Si ce n’est pas le cas, elle démarre. Lorsqu’elle est inactive, elle se met automatiquement en pause et se réveille lorsqu’elle reçoit une requête. Il est facile d’interagir de manière programmatique avec la sandbox à l’aide de méthodes telles que exec, gitClone, writeFile et plus encore.
import { getSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";
export default {
async fetch(request: Request, env: Env) {
// Ask for a sandbox by name. It starts on demand.
const sandbox = getSandbox(env.Sandbox, "agent-session-47");
// Clone a repository into it.
await sandbox.gitCheckout("https://github.com/org/repo", {
targetDir: "/workspace",
depth: 1,
});
// Run the test suite. Stream output back in real time.
return sandbox.exec("npm", ["test"], { stream: true });
},
};
Tant que vous fournissez le même ID, les requêtes suivantes peuvent être acheminées vers cette même sandbox depuis n’importe où dans le monde.
Injection sécurisée d’identifiants
L’authentification constitue l’un des problèmes les plus difficiles pour les charges de travail des agents. Vous avez souvent besoin d’agents pour accéder à des services privés, mais vous ne pouvez pas leur faire entièrement confiance en ce qui concerne les informations d’identification brutes.
Les sandbox résolvent ce problème en injectant des informations d’identification au niveau de la couche réseau à l’aide d’un proxy de sortie programmable. Ainsi, les agents de sandbox n’ont jamais accès aux informations d’identification et vous pouvez personnaliser entièrement la logique d’authentification comme vous le souhaitez :
class OpenCodeInABox extends Sandbox {
static outboundByHost = {
"my-internal-vcs.dev": (request, env, ctx) => {
const headersWithAuth = new Headers(request.headers);
headersWithAuth.set("x-auth-token", env.SECRET);
return fetch(request, { headers: headersWithAuth });
}
}
}
Pour une analyse approfondie du fonctionnement de cette solution (y compris l’injection d’identifiants tenant compte de l’identité, la modification dynamique des règles et l’intégration avec les liaisons Workers), lisez notre récent article de blog sur l’authentification Sandbox.
Un vrai terminal, pas une simulation
Les premiers systèmes d’agent modélisaient souvent l’accès au shell comme une boucle requête-réponse : exécuter une commande, attendre le résultat, renvoyer la transcription dans l’invite, puis répéter. Cela fonctionne, mais ce n’est pas la manière dont les développeurs utilisent réellement un terminal.
Les humains exécutent quelque chose, observent le flux de sortie, l’interrompent, se reconnectent plus tard et continuent. Les agents bénéficient de cette même boucle de rétroaction.
En février, nous avons lancé la prise en charge de PTY. Une session pseudo-terminale dans une sandbox, mise en proxy via WebSocket, compatible avec xterm.js.
Il suffit d’appeler sandbox.terminal pour servir le back-end :
// Worker: upgrade a WebSocket connection into a live terminal session
export default {
async fetch(request: Request, env: Env) {
const url = new URL(request.url);
if (url.pathname === "/terminal") {
const sandbox = getSandbox(env.Sandbox, "my-session");
return sandbox.terminal(request, { cols: 80, rows: 24 });
}
return new Response("Not found", { status: 404 });
},
};
Puis d'utiliser add-on xterm pour l’appeler depuis le client :
// Browser: connect xterm.js to the sandbox shell
import { Terminal } from "xterm";
import { SandboxAddon } from "@cloudflare/sandbox/xterm";
const term = new Terminal();
const addon = new SandboxAddon({
getWebSocketUrl: ({ origin }) => `${origin}/terminal`,
});
term.loadAddon(addon);
term.open(document.getElementById("terminal-container")!);
addon.connect({ sandboxId: "my-session" });
Ceci permet aux agents et aux développeurs d’utiliser un PTY complet pour déboguer ces sessions en direct.
Chaque session de terminal dispose de son propre shell isolé, de son propre répertoire de travail et de son propre environnement. Ouvrez autant d’instances que vous le souhaitez, comme vous le feriez sur votre propre machine. La sortie est mise en mémoire tampon côté serveur, de sorte que la reconnexion permet de reproduire ce que vous avez manqué.
Un interpréteur de code qui mémorise
Pour l’analyse des données, la création de scripts et les flux de travail exploratoires, nous publions également une abstraction de plus haut niveau : un contexte d’exécution de code persistant.
Le mot clé est « persistant ». De nombreuses implémentations d’interpréteurs de code exécutent chaque extrait de code de manière isolée, de sorte que l’état disparaît entre les appels. Vous ne pouvez pas définir une variable lors d'une étape et la lire à la suivante.
Les sandboxes vous permettent de créer des « contextes » présentant un état persistant. Les variables et les importations sont persistantes au sein des appels, de la même manière qu’elles le seraient dans un notebook Jupyter :
// Create a Python context. State persists for its lifetime.
const ctx = await sandbox.createCodeContext({ language: "python" });
// First execution: load data
await sandbox.runCode(`
import pandas as pd
df = pd.read_csv('/workspace/sales.csv')
df['margin'] = (df['revenue'] - df['cost']) / df['revenue']
`, { context: ctx });
// Second execution: df is still there
const result = await sandbox.runCode(`
df.groupby('region')['margin'].mean().sort_values(ascending=False)
`, { context: ctx, onStdout: (line) => console.log(line.text) });
// result contains matplotlib charts, structured json output, and Pandas tables in HTML
Démarrez un serveur. Obtenez une URL. Déployez-la.
Les agents sont plus utiles lorsqu’ils peuvent créer quelque chose et le montrer immédiatement à l’utilisateur. Les sandboxes prennent en charge les processus en arrière-plan, les contrôles de préparation et les URL d’aperçu. Cela permet à un agent de lancer un serveur de développement et de partager un lien en direct sans quitter la conversation.
// Start a dev server as a background process
const server = await sandbox.startProcess("npm run dev", {
cwd: "/workspace",
});
// Wait until the server is actually ready — don't just sleep and hope
await server.waitForLog(/Local:.*localhost:(\d+)/);
// Expose the running service with a public URL
const { url } = await sandbox.exposePort(3000);
// url is a live public URL the agent can share with the user
console.log(`Preview: ${url}`);
Avec waitForPort() et waitForLog(), les agents peuvent séquencer le travail en fonction de signaux réels provenant du programme en cours d’exécution au lieu de suppositions. C’est beaucoup plus intéressant que l'autre solution courante qui correspond généralement à une version de sleep(2000) exécutée en croisant les doigts.
Les boucles de développement modernes sont commandées par des événements. Enregistrez un fichier, puis exécutez à nouveau la version exécutable. Éditez une configuration, puis redémarrez le serveur. Modifiez un test et exécutez à nouveau la suite.
Nous avons lancé sandbox.watch() au mois de mars. Elle renvoie un flux SSE étayé par inotify natif, le mécanisme du noyau que Linux utilise pour les événements du système de fichiers.
import { parseSSEStream, type FileWatchSSEEvent } from '@cloudflare/sandbox';
const stream = await sandbox.watch('/workspace/src', {
recursive: true,
include: ['*.ts', '*.tsx']
});
for await (const event of parseSSEStream<FileWatchSSEEvent>(stream)) {
if (event.type === 'modify' && event.path.endsWith('.ts')) {
await sandbox.exec('npx tsc --noEmit', { cwd: '/workspace' });
}
}
Il s’agit d’une de ces primitives qui modifient discrètement ce que les agents peuvent faire. Un agent capable d’observer le système de fichiers en temps réel peut participer aux mêmes boucles de collecte d’informations qu’un développeur humain.
Se réveiller rapidement grâce aux instantanés
Imaginez un développeur (humain) travaillant sur son ordinateur portable. Il clone un référentiel Git, exécute une installation via npm, écrit du code, déploie un PR, puis ferme son ordinateur portable en attendant la révision du code. Lorsque vient le moment de reprendre le travail, il lui suffit de rouvrir l’ordinateur portable et de poursuivre là où il s'est arrêté.
Si un agent souhaite reproduire ce flux de travail sur une plateforme de conteneur simpliste, vous rencontrez un problème. Comment reprendre rapidement là où vous vous êtes interrompu ? Vous pouvez maintenir une sandbox active, mais vous payez alors pour le calcul inutilisé. Vous pouvez tout recommencer à partir de l’image du conteneur, mais vous devrez ensuite patienter longtemps, le temps d'un clonage de référentiel git et d'une installation via npm.
Notre réponse, ce sont les instantanés, qui seront déployés dans les semaines à venir.
Un instantané préserve l’état complet du disque d’un conteneur, la configuration du système d’exploitation, les dépendances installées, les fichiers modifiés, les fichiers de données et bien davantage. Il vous permet ensuite de tout restaurer rapidement.
Vous pouvez configurer une sandbox afin qu’elle réalise automatiquement un instantané lorsqu’elle est mise en veille.
class AgentDevEnvironment extends Sandbox {
sleepAfter = "5m";
persistAcrossSessions = {type: "disk"}; // you can also specify individual directories
}
Vous pouvez également effectuer un instantané de manière programmatique, puis le restaurer manuellement. Cette fonctionnalité est utile pour contrôler les tâches ou prévoir des instances de sessions. Par exemple, si vous souhaitiez exécuter quatre instances d’un agent en parallèle, vous pourriez facilement démarrer quatre sandboxes à partir du même état.
class AgentDevEnvironment extends Sandbox {}
async forkDevEnvironment(baseId, numberOfForks) {
const baseInstance = await getSandbox(baseId);
const snapshotId = await baseInstance.snapshot();
const forks = Array.from({ length: numberOfForks }, async (_, i) => {
const newInstance = await getSandbox(`${baseId}-fork-${i}`);
return newInstance.start({ snapshot: snapshotId });
});
await Promise.all(forks);
}
Les instantanés sont stockés dans R2 au sein de votre compte, ce qui vous garantit durabilité et indépendance vis-à-vis de l’emplacement. Le système de mise en cache par niveau de R2 permet des restaurations rapides dans toutes les régions du monde.
Dans les versions futures, l’état de la mémoire en direct sera également capturé, ce qui permettra aux processus en cours de reprendre exactement là où ils se sont arrêtés. Un terminal et un éditeur rouvriront dans l’état exact où ils se trouvaient lors de leur dernière fermeture.
Si vous souhaitez restaurer l’état de la session avant la mise en service des instantanés, vous pouvez utiliser dès maintenant les méthodes de sauvegarde et de restauration. Ces méthodes conservent et restaurent également les répertoires à l’aide de R2, mais ne se montrent pas aussi performantes que les véritables instantanés au niveau de la VM. Toutefois, elles peuvent encore améliorer considérablement la vitesse par rapport à la recréation simpliste de l’état de session.
Le démarrage d’une sandbox, le clonage d’axios et l’installation via npm prennent 30 secondes. La restauration à partir d’une sauvegarde prend deux secondes.
Restez à l’écoute dans l'attente de la publication officielle de l’instantané.
Limites élevées et tarification en fonction du temps processeur
Depuis notre lancement initial, nous avons régulièrement augmenté notre capacité. Les utilisateurs de notre offre Standard peuvent désormais exécuter 15 000 instances simultanées de type léger, 6 000 instances de base et plus de 1 000 instances simultanées de plus grande taille. Contactez-nous pour en exécuter encore plus !
Nous avons également modifié notre modèle de tarification afin que ce dernier soit plus rentable à grande échelle. Les sandboxes ne facturent désormais que les cycles CPU activement utilisés. Cela signifie que vous ne payez pas de temps processeur inutilisé pendant que votre agent attend la réponse d’un LLM.
Voici à quoi ressemble un ordinateur
Il y a neuf mois, nous avons livré une sandbox capable d’exécuter des commandes et d’accéder à un système de fichiers. Cela a suffi à éprouver le concept.
Ce dont nous disposons aujourd’hui est différent par nature. Aujourd’hui, un environnement de test est un environnement de développement complet : un terminal auquel vous pouvez connecter un navigateur, un interpréteur de code avec un état persistant, des processus en arrière-plan avec des URL de prévisualisation en direct, un système de fichiers qui émet des événements de changement en temps réel, des proxys de sortie pour l’injection sécurisée d’informations d’identification et un mécanisme d’instantanéité qui rend les démarrages à chaud presque instantanés.
Lorsque vous développez sur cette base, il se dégage une logique satisfaisante : des agents qui effectuent un véritable travail d’ingénierie. Cloner un référentiel, l’installer, exécuter les tests, lire les erreurs, modifier le code, exécuter à nouveau les tests. Le genre de boucle de rétroaction étroite qui rend l'ingénieur humain efficace ; et maintenant, l’agent la suit également.
Nous sommes à la version 0.8.9 du SDK. Commencez dès aujourd’hui :
npm i @cloudflare/sandbox@latest