ИИ-агенты получили свои компьютеры: Sandboxes от Cloudflare

Когда мы запустили Cloudflare Sandboxes в июне прошлого года, предпосылка была простой: AI-агентам нужно разрабатывать и запускать код, и им нужно делать это где-то безопасно.

Если агент действует как разработчик, это означает клонирование репозиториев, сборку кода на многих языках, запуск серверов для разработки и т.д. Чтобы делать это эффективно, им часто нужен полноценный компьютер (а если нет — они могут воспользоваться чем-то более легковесным!).

Многие разработчики создают решения, используя виртуальные машины или существующие контейнерные решения, но есть много сложных проблем, которые нужно решить:

  • Резкие всплески нагрузки - Поскольку каждому сеансу нужен свой песочница, часто требуется быстро развернуть много песочниц, но при этом не хочется платить за простой резервных вычислительных мощностей.

  • Быстрое восстановление состояния - Каждый сеанс должен начинаться быстро и быстро перезапускаться, восстанавливая предыдущее состояние.

  • Безопасность - Агентам нужен безопасный доступ к сервисам, но им нельзя доверять учетные данные.

  • Управление - Должно быть просто программно управлять жизненным циклом песочницы, выполнять команды, работать с файлами и многое другое.

  • Эргономика - Нужно предоставить простой интерфейс как для людей, так и для агентов, чтобы выполнять обычные операции.

Мы потратили время на решение этих проблем, чтобы вам не пришлось. С момента первоначального запуска мы сделали Sandboxes еще лучшим местом для запуска агентов в масштабе. Мы работали с нашими первыми партнерами, такими как Figma, которые запускают агентов в контейнерах с помощью Figma Make:

«Figma Make создана, чтобы помочь создателям и изобретателям любого уровня подготовки быстрее пройти путь от идеи до продукта. Чтобы достичь этой цели, нам нужно было инфраструктурное решение, которое могло бы предоставить надежные, высокомасштабируемые песочницы для запуска непроверенного кода, написанного агентами и пользователями. Cloudflare Containers — это именно такое решение».

- Алекс Мулланс, AI and Developer Platforms в Figma

Мы хотим предоставить Sandboxes еще большему количеству отличных организаций, поэтому сегодня мы рады объявить, что Sandboxes и Cloudflare Containers стали общедоступными.

Давайте взглянем на некоторые недавние изменения в Sandboxes:

  • Безопасное внедрение учетных данных позволяет выполнять аутентифицированные вызовы, не предоставляя агенту доступ к учетным данным.

  • Поддержка PTY дает вам и вашему агенту настоящий терминал.

  • Постоянные интерпретаторы кода дают вашему агенту готовое место для выполнения Python, JavaScript и TypeScript с сохранением состояния.

  • Фоновые процессы и URL для live-превью предоставляют простой способ взаимодействовать с серверами для разработки и проверять изменения на лету.

  • Наблюдение за файловой системой ускоряет итерации по мере внесения агентом изменений.

  • Снимки состояния (Snapshots) позволяют быстро восстановить кодинг-сессию агента.

  • Более высокие лимиты и Active CPU Pricing позволяют развернуть флот агентов в масштабе, не оплачивая неиспользуемые циклы ЦПУ.

Основы Sandboxes

Прежде чем перейти к недавним изменениям, давайте быстро рассмотрим основы.

Cloudflare Sandbox — это постоянная изолированная среда, работающая на базе Cloudflare Containers. Вы запрашиваете песочницу по имени. Если она запущена — вы ее получаете. Если нет — она запускается. Когда она простаивает, она автоматически засыпает и пробуждается при получении запроса. С песочницей легко взаимодействовать программно, используя такие методы, как exec, gitClone, writeFile и другие.

import { getSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";

export default {
  async fetch(request: Request, env: Env) {
    // Запросите песочницу по имени. Она запускается по требованию.
    const sandbox = getSandbox(env.Sandbox, "agent-session-47");

    // Клонируйте в неё репозиторий.
    await sandbox.gitCheckout("https://github.com/org/repo", {
      targetDir: "/workspace",
      depth: 1,
    });

    // Запустите набор тестов. Потоковый вывод в реальном времени.
    return sandbox.exec("npm", ["test"], { stream: true });
  },
};

Пока вы предоставляете тот же ID, последующие запросы из любой точки мира могут получить доступ к этой же песочнице.

Безопасное внедрение учетных данных

Одна из самых сложных проблем в агентных рабочих нагрузках — аутентификация. Часто требуется, чтобы агенты имели доступ к частным сервисам, но им нельзя полностью доверять исходные учетные данные.

Sandboxes решают эту проблему, внедряя учетные данные на сетевом уровне с помощью программируемого исходящего прокси. Это означает, что агенты в песочницах никогда не имеют доступа к учетным данным, и вы можете полностью настроить логику аутентификации по своему усмотрению:

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 });
    }
  }
}

Чтобы подробно разобраться, как это работает — включая внедрение учетных данных с учетом идентичности, динамическое изменение правил и интеграцию с привязками Workers — прочитайте наше недавнее сообщение в блоге про аутентификацию в Sandbox.

Настоящий терминал, а не симуляция

Ранние агентные системы часто моделировали доступ к оболочке как цикл запрос-ответ: выполнить команду, дождаться вывода, вставить транскрипт обратно в промпт, повторить. Это работает, но не так, как разработчики на самом деле используют терминал.

Люди что-то запускают, смотрят, как потоком поступает вывод, прерывают его, подключаются позже и продолжают. Агенты получают выгоду от такой же обратной связи.

В феврале мы выпустили поддержку PTY. Сеанс псевдотерминала в Sandbox, проксируемый через WebSocket, совместимый с xterm.js.

Просто вызовите sandbox.terminal, чтобы запустить бэкенд:

// Worker: преобразуйте WebSocket-соединение в live-сессию терминала
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 });
  },
};

И используйте xterm addon, чтобы вызвать его со стороны клиента:

// Браузер: подключите xterm.js к оболочке песочницы
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" });

Это позволяет агентам и разработчикам использовать полноценный PTY для живой отладки этих сеансов.

Agents have their own computers with Sandboxes GA

Каждый терминальный сеанс получает свою изолированную оболочку, свою рабочую директорию, свою среду. Открывайте столько, сколько нужно, прямо как на своей собственной машине. Вывод буферизуется на стороне сервера, поэтому при повторном подключении воспроизводится то, что вы пропустили.

Интерпретатор кода, который помнит

Для анализа данных, написания скриптов и исследовательских рабочих процессов мы также предоставляем абстракцию более высокого уровня: постоянный контекст выполнения кода.

Ключевое слово — «постоянный». Многие реализации интерпретатора кода выполняют каждый фрагмент изолированно, поэтому состояние исчезает между вызовами. Вы не можете задать переменную на одном шаге и прочитать ее на следующем.

Sandboxes позволяют создавать «контексты», которые сохраняют состояние. Переменные и импорты сохраняются между вызовами так же, как и в блокноте Jupyter:

// Создайте контекст Python. Состояние сохраняется на протяжении его жизни.
const ctx = await sandbox.createCodeContext({ language: "python" });

// Первое выполнение: загрузка данных
await sandbox.runCode(`
  import pandas as pd
  df = pd.read_csv('/workspace/sales.csv')
  df['margin'] = (df['revenue'] - df['cost']) / df['revenue']
`, { context: ctx });

// Второе выполнение: df все еще там
const result = await sandbox.runCode(`
  df.groupby('region')['margin'].mean().sort_values(ascending=False)
`, { context: ctx, onStdout: (line) => console.log(line.text) });

// result содержит графики matplotlib, структурированный json-вывод и таблицы Pandas в HTML

Запустите сервер. Получите URL. Отправьте его.

Агенты становятся полезнее, когда они могут что-то создать и сразу показать пользователю. Sandboxes поддерживают фоновые процессы, проверки готовности и URL для предварительного просмотра. Это позволяет агенту запустить сервер для разработки и поделиться живой ссылкой, не покидая разговора.

// Запуск dev-сервера в фоновом процессе
const server = await sandbox.startProcess("npm run dev", {
  cwd: "/workspace",
});

// Ожидание реальной готовности сервера — без простого sleep и надежды
await server.waitForLog(/Local:.*localhost:(d+)/);

// Публикация работающего сервиса через публичный URL
const { url } = await sandbox.exposePort(3000);

// url — это рабочий публичный URL, которым агент может поделиться с пользователем
console.log(`Предварительный просмотр: ${url}`);

С помощью waitForPort() и waitForLog() агенты могут выстраивать работу на основе реальных сигналов от запущенной программы, а не строить догадки. Это гораздо удобнее, чем распространённая альтернатива — обычно какая-нибудь вариация sleep(2000) с последующей надеждой.

Наблюдение за файловой системой и мгновенная реакция

Современные циклы разработки событийно-ориентированы. Сохранили файл — перезапустили сборку. Отредактировали конфиг — перезапустили сервер. Изменили тест — перезапустили набор тестов.

Мы выпустили sandbox.watch() в марте. Он возвращает поток SSE, работающий на основе нативного механизма ядра Linux inotify, который используется для событий файловой системы.

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' });
  }
}

Это одна из тех примитивных возможностей, которые незаметно меняют то, что могут делать агенты. Агент, который может наблюдать за файловой системой в реальном времени, может участвовать в тех же циклах обратной связи, что и разработчик-человек.

Быстрое пробуждение с помощью снимков состояния

Представьте разработчика (человека), работающего на своём ноутбуке. Он выполняет git clone репозитория, запускает npm install, пишет код, отправляет PR, а затем закрывает ноутбук, ожидая ревью кода. Когда приходит время возобновить работу, он просто открывает ноутбук и продолжает с того места, где остановился.

Если агент попытается воспроизвести этот рабочий процесс на простой платформе контейнеров, возникнет загвоздка. Как быстро возобновить работу с нужного места? Можно поддерживать песочницу в рабочем состоянии, но тогда придётся платить за простой вычислений. Можно начать всё заново с образа контейнера, но тогда придётся долго ждать выполнения git clone и npm install.

Наше решение — снимки состояния (snapshots), которые будут запущены в ближайшие недели.

Снимок сохраняет полное состояние диска контейнера, конфигурацию ОС, установленные зависимости, изменённые файлы, файлы данных и многое другое. Затем он позволяет быстро восстановить это состояние позже.

Вы можете настроить Песочницу на автоматическое создание снимка при переходе в режим сна.

class AgentDevEnvironment extends Sandbox {
  sleepAfter = "5m";
  persistAcrossSessions = {type: "disk"}; // можно также указать отдельные директории
}

Вы также можете программно создать снимок и вручную восстановить его. Это полезно для создания контрольных точек работы или ветвления сессий. Например, если вы хотите запустить четыре экземпляра агента параллельно, вы можете легко загрузить четыре песочницы из одного состояния.

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);
}

Снимки хранятся в R2 в рамках вашего аккаунта, что обеспечивает долговечность и независимость от местоположения. Система многоуровневого кэширования R2 позволяет быстро восстанавливать состояние по всему региону Earth.

В будущих релизах также будет захватываться состояние оперативной памяти, что позволит запущенным процессам возобновить работу точно с того места, где они остановились. Терминал и редактор откроются в том же состоянии, в котором они были при последнем закрытии.

Если вас интересует восстановление состояния сессии до выхода снимков, вы уже сегодня можете использовать методы резервного копирования и восстановления. Они также сохраняют и восстанавливают директории с помощью R2, но не так производительны, как настоящие снимки на уровне виртуальной машины. Хотя они всё равно могут привести к значительному увеличению скорости по сравнению с наивным воссозданием состояния сессии.

Agents have their own computers with Sandboxes GA

Загрузка песочницы, клонирование 'axios' и запуск npm install занимает 30 секунд. Восстановление из резервной копии занимает две секунды.

Следите за официальным релизом снимков состояния.

Более высокие лимиты и тарификация активного использования ЦПУ

С момента первоначального запуска мы постоянно увеличиваем мощность. Пользователи нашего стандартного тарифного плана теперь могут запускать 15 000 параллельных экземпляров лёгкого типа, 6 000 экземпляров базового типа и более 1 000 параллельных экземпляров более крупных типов. Свяжитесь с нами, чтобы запускать ещё больше!

Мы также изменили нашу модель ценообразования, чтобы сделать работу в масштабе более рентабельной. Песочницы теперь берут плату только за активно используемые циклы ЦПУ. Это означает, что вы не платите за простой ЦПУ, пока ваш агент ждёт ответа от LLM.

Вот как сейчас выглядит компьютер

Девять месяцев назад мы выпустили песочницу, которая могла выполнять команды и получать доступ к файловой системе. Этого было достаточно, чтобы доказать концепцию.

То, что у нас есть сейчас, отличается по сути. Песочница сегодня — это полноценная среда разработки: терминал, к которому можно подключить браузер, интерпретатор кода с постоянным состоянием, фоновые процессы с URL-адресами для предварительного просмотра в реальном времени, файловая система, которая в реальном времени генерирует события об изменениях, прокси для исходящего трафика для безопасного внедрения учётных данных и механизм снимков, который делает "тёплый" запуск почти мгновенным.

При разработке на этой основе возникает удовлетворительная картина: агенты, которые выполняют реальную инженерную работу. Клонировать репозиторий, установить зависимости, запустить тесты, прочитать ошибки, отредактировать код, снова запустить тесты. Такая же тесная обратная связь, которая делает эффективным разработчика-человека, — теперь она доступна и агенту.

Сейчас у нас версия SDK 0.8.9. Вы можете начать работу уже сегодня: