Безопасный запуск ИИ-агентов в 100 раз быстрее контейнеров

В сентябре прошлого года мы представили Code Mode — идею о том, что агенты должны выполнять задачи не с помощью вызова инструментов, а путём написания кода, который вызывает API. Мы показали, что простое преобразование MCP-сервера в TypeScript API может сократить использование токенов на 81%. Мы продемонстрировали, что Code Mode также может работать за MCP-сервером, а не перед ним, создав новый MCP-сервер Cloudflare, который открывает доступ ко всему API Cloudflare всего с двумя инструментами и менее чем 1000 токенов.

Но если агент (или MCP-сервер) будет выполнять код, сгенерированный искусственным интеллектом на лету для выполнения задач, этому коду нужно где-то выполняться, и это место должно быть безопасным. Нельзя просто выполнять eval() для кода, созданного ИИ, прямо в вашем приложении: злонамеренный пользователь может элементарно подтолкнуть ИИ к внедрению уязвимостей.

Вам нужна песочница (sandbox): место для выполнения кода, которое изолировано от вашего приложения и от всего остального мира, за исключением конкретных возможностей, к которым этот код должен иметь доступ.

Песочницы — горячая тема в индустрии ИИ. Для этой задачи большинство используют контейнеры. Используя контейнер на базе Linux, вы можете запустить любую среду выполнения кода. Cloudflare даже предлагает нашу среду выполнения контейнеров и наш Sandbox SDK для этой цели.

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

Если мы хотим поддерживать агентов потребительского масштаба, где у каждого конечного пользователя есть агент (или много!) и каждый агент пишет код, контейнеров недостаточно. Нам нужно что-то более лёгкое.

И у нас это есть.

Dynamic Worker Loader: лёгкая песочница

В нашем посте о Code Mode в сентябре было объявление о новой экспериментальной функции: API Dynamic Worker Loader. Этот API позволяет Cloudflare Worker'у создавать новый Worker в собственной песочнице с кодом, указанным во время выполнения, и всё это на лету.

Dynamic Worker Loader теперь находится в открытой бета-версии и доступен всем платным пользователям Workers.

Прочтите документацию для получения полной информации, но вот как это выглядит:

// Пусть ваш LLM сгенерирует код примерно так.
let agentCode: string = `
  export default {
    async myAgent(param, env, ctx) {
      // ...
    }
  }
`;

// Получите заглушки RPC, представляющие API, к которым агент должен иметь доступ.
// (Это может быть любой определённый вами Workers RPC API.)
let chatRoomRpcStub = ...;

// Загрузите воркер для выполнения кода, используя привязку загрузчика воркеров.
let worker = env.LOADER.load({
  // Укажите код.
  compatibilityDate: "2026-03-01",
  mainModule: "agent.js",
  modules: { "agent.js": agentCode },

  // Дайте агенту доступ к API чата.
  env: { CHAT_ROOM: chatRoomRpcStub },

  // Блокируйте доступ в интернет. (Вы также можете его перехватывать.)
  globalOutbound: null,
});

// Вызовите RPC-методы, экспортированные кодом агента.
await worker.getEntrypoint().myAgent(param);

Вот и всё.

В 100 раз быстрее

Dynamic Workers используют тот же базовый механизм изоляции, на котором была построена вся платформа Cloudflare Workers с момента её запуска восемь лет назад: изоляты (isolates). Изолят — это экземпляр механизма выполнения JavaScript V8, того же движка, что используется в Google Chrome. Так работают Workers.

Запуск изолята занимает несколько миллисекунд, и он использует несколько мегабайт памяти. Это примерно в 100 раз быстрее и в 10-100 раз эффективнее по памяти, чем типичный контейнер.

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

Неограниченная масштабируемость

Многие поставщики песочниц на основе контейнеров накладывают ограничения на глобальное количество одновременных песочниц и скорость их создания. У Dynamic Worker Loader таких ограничений нет. Они ему и не нужны, потому что это просто API к той же технологии, которая всегда питала нашу платформу и всегда позволяла Workers легко масштабироваться до миллионов запросов в секунду.

Хотите обрабатывать миллион запросов в секунду, где каждый отдельный запрос загружает отдельную песочницу Dynamic Worker, и все они работают одновременно? Нет проблем!

Нулевая задержка

Одноразовые Dynamic Workers обычно работают на той же машине — даже на том же потоке — что и Worker, который их создал. Нет необходимости общаться по всему миру, чтобы найти "тёплую" песочницу. Изоляты настолько легковесны, что мы можем просто запускать их там, где приземлился запрос. Dynamic Workers поддерживаются в каждом из сотен расположений Cloudflare по всему миру.

Это всё JavaScript

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

Технически, Workers (включая динамические) могут использовать Python и WebAssembly, но для небольших фрагментов кода — таких, которые агент пишет по требованию — JavaScript загружается и работает намного быстрее.

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

Но мы говорим не о людях. Мы говорим об ИИ. ИИ напишет на любом языке, который вы захотите. LLM — эксперты во всех основных языках. Их обучающие данные по JavaScript огромны.

JavaScript по своей природе в вебе предназначен для работы в песочнице. Это правильный язык для данной задачи.

Инструменты, определённые на TypeScript

Если мы хотим, чтобы наш агент мог делать что-то полезное, ему нужно общаться с внешними API. Как мы сообщим ему о тех API, к которым у него есть доступ?

MCP определяет схемы для простых вызовов инструментов, но не для программных API. OpenAPI предлагает способ выражения REST API, но он многословен — как в самой схеме, так и в коде, который нужно написать для его вызова.

Для API, предоставляемых JavaScript, есть один очевидный ответ: TypeScript.

Агенты знают TypeScript. TypeScript разработан для лаконичности. С помощью очень небольшого количества токенов вы можете дать своему агенту точное понимание вашего API.

// Интерфейс для взаимодействия с чат-комнатой.
interface ChatRoom {
  // Получить последние `limit` сообщений из лога чата.
  getHistory(limit: number): Promise<Message[]>;

  // Подписаться на новые сообщения. Удалите возвращаемый объект,
  // чтобы отписаться.
  subscribe(callback: (msg: Message) => void): Promise<Disposable>;

  // Отправить сообщение в чат.
  post(text: string): Promise<void>;
}

type Message = {
  author: string;
  time: Date;
  text: string;
}

Сравните это с эквивалентной спецификацией OpenAPI (она такая длинная, что нужно прокручивать, чтобы увидеть всё):

openapi: 3.1.0
info:
  title: ChatRoom API
  description: >
    Interface to interact with a chat room.
  version: 1.0.0

paths:
  /messages:
    get:
      operationId: getHistory
      summary: Get recent chat history
      description: Returns the last `limit` messages from the chat log, newest first.
      parameters:
        - name: limit
          in: query
          required: true
          schema:
            type: integer
            minimum: 1
      responses:
        "200":
          description: A list of messages.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Message"

    post:
      operationId: postMessage
      summary: Post a message to the chat room
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - text
              properties:
                text:
                  type: string
      responses:
        "204":
          description: Message posted successfully.

  /messages/stream:
    get:
      operationId: subscribeMessages
      summary: Subscribe to new messages via SSE
      description: >
        Opens a Server-Sent Events stream. Each event carries a JSON-encoded
        Message object. The client unsubscribes by closing the connection.
      responses:
        "200":
          description: An SSE stream of new messages.
          content:
            text/event-stream:
              schema:
                description: >
                  Each SSE `data` field contains a JSON-encoded Message object.
                $ref: "#/components/schemas/Message"

components:
  schemas:
    Message:
      type: object
      required:
        - author
        - time
        - text
      properties:
        author:
          type: string
        time:
          type: string
          format: date-time
        text:
          type: string

Мы считаем, что API на TypeScript лучше. Он требует меньше токенов и намного проще для понимания (как для агентов, так и для людей).

Dynamic Worker Loader позволяет легко реализовать API на TypeScript, подобный этому, в вашем собственном Worker, а затем передать его в Dynamic Worker либо в качестве параметра метода, либо в объекте env. Workers Runtime автоматически настроит мост Cap'n Web RPC между песочницей и вашим управляющим кодом, так что агент сможет вызывать ваш API через границу безопасности, даже не подозревая, что использует не локальную библиотеку.

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

// Мысль: Пользователь попросил меня обобщить недавние сообщения из чата от Алисы.
// Я отфильтрую историю последних сообщений в коде, чтобы мне пришлось
// читать только соответствующие сообщения.
let history = await env.CHAT_ROOM.getHistory(1000);
return history.filter(msg => msg.author == "alice");

HTTP-фильтрация и инъекция учетных данных

Если вы предпочитаете предоставлять своим агентам HTTP API, это полностью поддерживается. Используя опцию globalOutbound в API загрузчика Worker, вы можете зарегистрировать обратный вызов, который будет вызываться для каждого HTTP-запроса. В нём вы можете проверить запрос, изменить его, внедрить ключи аутентификации, ответить на него напрямую, заблокировать его или сделать что-либо ещё.

Например, вы можете использовать это для реализации инъекции учетных данных (инъекции токена): когда агент делает HTTP-запрос к сервису, требующему авторизации, вы добавляете учетные данные к запросу на выходе. Таким образом, сам агент никогда не узнает секретные учетные данные и, следовательно, не может их раскрыть.

Использование простого HTTP-интерфейса может быть предпочтительно, когда агент взаимодействует с хорошо известным API, который присутствует в его обучающем наборе, или когда вы хотите, чтобы ваш агент использовал библиотеку, построенную на REST API (библиотека может выполняться внутри песочницы агента).

Тем не менее, при отсутствии требований совместимости, TypeScript RPC интерфейсы лучше, чем HTTP:

  • Как показано выше, TypeScript интерфейс требует значительно меньше токенов для описания, чем HTTP интерфейс.

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

  • С TypeScript интерфейсами, поскольку вы в любом случае определяете свою собственную обёртку, легче сузить интерфейс, чтобы предоставить именно те возможности, которые вы хотите дать вашему агенту, как для простоты, так и для безопасности. С HTTP вы, скорее всего, реализуете фильтрацию запросов к какому-то существующему API. Это сложно, потому что ваш прокси должен полностью интерпретировать смысл каждого вызова API, чтобы правильно решить, разрешать ли его, а HTTP-запросы сложны, с множеством заголовков и других параметров, которые могут быть значимыми. В итоге проще просто написать TypeScript-обёртку, которая реализует только нужные вам функции.

Проверенная в боях безопасность

Укрепление песочницы на основе изолятов — непростая задача, так как это более сложная поверхность атаки, чем аппаратные виртуальные машины. Хотя во всех механизмах песочницы есть ошибки, ошибки безопасности в V8 встречаются чаще, чем в типичных гипервизорах. При использовании изолятов для песочницы с потенциально вредоносным кодом важно иметь дополнительные уровни защиты в глубину. Google Chrome, например, реализовал строгую изоляцию процессов по этой причине, но это не единственное возможное решение.

У нас есть почти десятилетний опыт защиты нашей платформы на основе изолятов. Наши системы автоматически развертывают патчи безопасности V8 в продакшене за считанные часы — быстрее, чем сам Chrome. Наша архитектура безопасности включает пользовательскую песочницу второго уровня с динамическим разграничением клиентов на основе оценки рисков. Мы расширили саму песочницу V8, чтобы использовать аппаратные функции, такие как MPK. Мы объединились (и наняли) ведущих исследователей для разработки новых средств защиты от Spectre. У нас также есть системы, которые сканируют код на наличие вредоносных шаблонов и автоматически блокируют их или применяют дополнительные уровни изоляции. И многое другое.

Когда вы используете Dynamic Workers на Cloudflare, вы получаете всё это автоматически.

Вспомогательные библиотеки

Мы создали несколько библиотек, которые могут оказаться полезными при работе с Dynamic Workers:

Code Mode

@cloudflare/codemode упрощает выполнение кода, сгенерированного моделью, с помощью AI Tools и Dynamic Workers. В его основе — DynamicWorkerExecutor(), который создаёт специализированную песочницу с нормализацией кода для обработки распространённых ошибок форматирования и прямым доступом к globalOutbound fetcher для контроля поведения fetch() внутри песочницы — установите его в null для полной изоляции или передайте привязку Fetcher для маршрутизации, перехвата или обогащения исходящих запросов из песочницы.

const executor = new DynamicWorkerExecutor({
  loader: env.LOADER,
  globalOutbound: null, // полная изоляция 
});

const codemode = createCodeTool({
  tools: myTools,
  executor,
});

return generateText({
  model,
  messages,
  tools: { codemode },
});

SDK Code Mode также предоставляет две серверные служебные функции. codeMcpServer({ server, executor }) оборачивает существующий MCP Server, заменяя его набор инструментов единым инструментом code(). openApiMcpServer({ spec, executor, request }) идёт дальше: задав спецификацию OpenAPI и исполнитель, он создаёт полноценный MCP Server с инструментами search() и execute(), как в Cloudflare MCP Server, что лучше подходит для крупных API.

В обоих случаях код, сгенерированный моделью, выполняется внутри Dynamic Workers, а вызовы внешних сервисов осуществляются через RPC-привязки, переданные исполнителю.

Узнайте больше о библиотеке и том, как её использовать.

Бандлинг

Dynamic Workers ожидают предварительно собранные модули. @cloudflare/worker-bundler делает это за вас: передайте ему исходные файлы и package.json, и он разрешит npm-зависимости из реестра, соберёт всё с помощью esbuild и вернёт карту модулей, которую ожидает Worker Loader.

import { createWorker } from "@cloudflare/worker-bundler";

const worker = env.LOADER.get("my-worker", async () => {
  const { mainModule, modules } = await createWorker({
    files: {
      "src/index.ts": `
        import { Hono } from 'hono';
        import { cors } from 'hono/cors';

        const app = new Hono();
        app.use('*', cors());
        app.get('/', (c) => c.text('Hello from Hono!'));
        app.get('/json', (c) => c.json({ message: 'It works!' }));

        export default app;
      `,
      "package.json": JSON.stringify({
        dependencies: { hono: "^4.0.0" }
      })
    }
  });

  return { mainModule, modules, compatibilityDate: "2026-01-01" };
});

await worker.getEntrypoint().fetch(request);

Он также поддерживает полноценные приложения через createApp — собирает серверный Worker, клиентский JavaScript и статические ресурсы вместе со встроенной раздачей ресурсов, которая обрабатывает типы контента, ETag и SPA-маршрутизацию.

Узнайте больше о библиотеке и том, как её использовать.

Манипуляции с файлами

@cloudflare/shell предоставляет вашему агенту виртуальную файловую систему внутри Dynamic Worker. Код агента вызывает типизированные методы объекта state — чтение, запись, поиск, замена, сравнение, glob, JSON-запросы/обновления, архивация — со структурированными входными и выходными данными вместо парсинга строк.

Хранилище поддерживается долговечным Workspace (SQLite + R2), поэтому файлы сохраняются между выполнениями. Крупные операции, такие как searchFiles, replaceInFiles и planEdits, минимизируют количество RPC-циклов — агент делает один вызов вместо перебора отдельных файлов. Пакетные записи по умолчанию транзакционны: если какая-либо запись не удаётся, предыдущие записи автоматически откатываются.

import { Workspace } from "@cloudflare/shell";
import { stateTools } from "@cloudflare/shell/workers";
import { DynamicWorkerExecutor, resolveProvider } from "@cloudflare/codemode";

const workspace = new Workspace({
  sql: this.ctx.storage.sql, // Работает с любым SqlStorage DO, D1 или пользовательским SQL-бэкендом
  r2: this.env.MY_BUCKET, // большие файлы автоматически выгружаются в R2
  name: () => this.name   // ленивая загрузка — разрешается при необходимости, а не при создании
});

// Код выполняется в изолированной песочнице Worker без доступа к сети
const executor = new DynamicWorkerExecutor({ loader: env.LOADER });

// LLM пишет этот код; вызовы `state.*` передаются обратно на хост через RPC
const result = await executor.execute(
  `async () => {
    // Поиск шаблона во всех TypeScript файлах
    const hits = await state.searchFiles("src/**/*.ts", "answer");
    // Планирование нескольких правок как единой транзакции
    const plan = await state.planEdits([
      { kind: "replace", path: "/src/app.ts",
        search: "42", replacement: "43" },
      { kind: "writeJson", path: "/src/config.json",
        value: { version: 2 } }
    ]);
    // Применение атомарно — откатывается при сбое
    return await state.applyEditPlan(plan);
  }`,
  [resolveProvider(stateTools(workspace))]
);

Пакет также включает предсобранные объявления типов TypeScript и шаблон системного промпта, поэтому вы можете добавить полный API state в контекст вашей LLM всего за несколько токенов.

Узнайте больше о библиотеке и том, как её использовать.

Как люди это используют?

Режим кода (Code Mode)

Разработчики хотят, чтобы их агенты писали и выполняли код, используя API инструментов, вместо того чтобы делать последовательные вызовы инструментов по одному. С помощью Dynamic Workers LLM генерирует одну функцию TypeScript, которая объединяет несколько вызовов API, выполняет её в Dynamic Worker и возвращает конечный результат агенту. В результате в контекстное окно попадает только итоговый вывод, а не каждый промежуточный шаг. Это снижает и задержку, и расход токенов, а также даёт лучшие результаты, особенно когда поверхность инструментов велика.

Наш собственный Cloudflare MCP сервер построен именно так: он предоставляет весь Cloudflare API всего через два инструмента — поиск и выполнение — менее чем за 1000 токенов, потому что агент пишет код против типизированного API, а не навигацию по сотням отдельных определений инструментов.

Создание пользовательских автоматизаций 

Разработчики используют Dynamic Workers, чтобы позволить агентам создавать пользовательские автоматизации на лету. Например, Zite создает платформу для приложений, где пользователи взаимодействуют через чат-интерфейс — LLM пишет TypeScript за кулисами, чтобы создавать CRUD-приложения, подключаться к сервисам вроде Stripe, Airtable и Google Calendar и запускать серверную логику, при этом пользователь никогда не видит ни строчки кода. Каждая автоматизация выполняется в своем собственном Dynamic Worker с доступом только к конкретным сервисам и библиотекам, которые нужны эндпоинту.

“Чтобы включить серверный код для LLM-генерируемых приложений Zite, нам нужен был уровень исполнения, который был мгновенным, изолированным и безопасным. Cloudflare Dynamic Workers соответствовали всем трём критериям и превзошли все другие платформы, которые мы тестировали по скорости и поддержке библиотек. Совместимая с NodeJS среда выполнения поддерживала все рабочие процессы Zite, позволяя сотням сторонних интеграций без ущерба для времени запуска. Благодаря Dynamic Workers Zite теперь обслуживает миллионы запросов на выполнение ежедневно.”

Антони Торон, технический директор и сооснователь, Zite 

Запуск AI-генерируемых приложений

Разработчики создают платформы, которые генерируют полноценные приложения с помощью ИИ — либо для своих клиентов, либо для внутренних команд, создающих прототипы. С помощью Dynamic Workers каждое приложение можно развернуть по требованию, а затем вернуть в холодное хранение до следующего вызова. Быстрое время запуска позволяет легко предварительно просматривать изменения в процессе активной разработки. Платформы также могут блокировать или перехватывать любые сетевые запросы, которые делает сгенерированный код, обеспечивая безопасный запуск AI-генерируемых приложений.

Цены

Динамически загружаемые Workers стоят $0.002 за каждый уникальный загруженный Worker в день (на момент публикации этого поста), в дополнение к обычной стоимости времени ЦП и вызовов для обычных Workers.

Для случаев использования AI-генерируемого «режима кода», где каждый Worker является уникальным и одноразовым, это означает цену в $0.002 за загруженный Worker (плюс ЦП и вызовы). Эти затраты, как правило, незначительны по сравнению со стоимостью инференса для генерации кода.

В течение бета-периода плата в размере $0.002 не взимается. Поскольку цены могут меняться, всегда проверяйте актуальную информацию на странице цен Dynamic Workers

Начать работу

Если вы на платном тарифе Workers, вы можете начать использовать Dynamic Workers уже сегодня. 

Стартовый шаблон Dynamic Workers

Используйте этот «hello world» стартовый шаблон, чтобы развернуть Worker, который может загружать и выполнять Dynamic Workers. 

Песочница Dynamic Workers

Вы также можете развернуть Песочницу Dynamic Workers, где вы сможете писать или импортировать код, собирать его во время выполнения с помощью @cloudflare/worker-bundler, выполнять через Dynamic Worker и видеть ответы и логи выполнения в реальном времени.

Sandboxing AI agents, 100x faster

Dynamic Workers быстры, масштабируемы и легковесны. Найдите нас в Discord, если у вас есть вопросы. Нам не терпится увидеть, что вы построите!

Sandboxing AI agents, 100x faster