Головна / Статті

Перенесення навантажень Apps Script на Linux-сандбокс Gemini Managed Agent

Google Apps Script, який керує Linux-сандбоксом управлінського агента Gemini: передача файлів у Drive за допомогою ggsrun, обхід обмеження у 50 МБ, запуск Playwright, FFmpeg та esbuild.

Видавець: REACTAPP.TOP13 хв читання2934 слів

Джерела: Taking Advantage of Gemini Managed Agents with Google Apps Script

Чому варто поєднувати Apps Script із Linux-сандбоксом

Google Apps Script — це зручний інструмент для автоматизації Google Workspace, проте це легкий, обмежений серверлесс-рунтайм без доступу на рівні операційної системи. Це виключає виконання багатьох складніших завдань. Контроль мережевих з’єднань та протоколів на низькому рівні є обмеженим. Немає безвіконного браузера для відображення динамічних сторінок, неможливості запуску нативних бінарів для транскодування медіа чи обробки сигналів, сучасних компіляторів чи інструментів для компіляції, а також існують суворі обмеження щодо часу виконання та розміру даних.

Gemini Managed Agents пропонують спосіб обійти ці обмеження. Як частина API Gemini v1beta Interactions and Environments вони дозволяють розробникам створювати віддалені Linux-сандбокси, які можуть самостійно виконувати код, запускати команди та керувати пакетами. Поєднання їх із CLI Google Workspace вже викликало інтерес у спільноті.

Описана тут архітектура поєднує ці два елементи. Apps Script керує постійним Linux-контейнером із 4 vCPUs та 16 ГБ оперативної пам’яті, а також з Python 3.12, Node.js 22 та звичайними менеджерами пакетів (apt, npm, pip). Контейнер передає створені файли безпосередньо до Google Drive за допомогою інструменту командного рядка ggsrun. Оскільки файли ніколи не передаються назад через відповідь API, така конструкція уникає обмежень щодо обсягу даних та додаткових витрат, пов’язаних із токенами, що забезпечує високу продуктивність автоматизації в хмарі.

Проблема повернення бінарних файлів у форматі Base64

Припустимо, що сандбокс створює великі файли — наприклад, знімки екрана високої роздільної здатності, форми аудіохвиль чи збірний файл JavaScript — і їх потрібно зберегти у Drive. Очевидним способом є повернення необроблених байтів у вигляді рядка Base64 у відповіді API Gemini та його декодування в Apps Script. Однак цей підхід стикається з кількома проблемами одночасно.

  • UrlFetchApp у Apps Script обмежує розмір відповідей 50 МБ.
  • Середовище виконання коду в Interactions API обмежує буфер stdout, тож багатомегабайтний рядок Base64 може бути перерваний на півдорозі.
  • У Managed Agents є ліміт у 200 000 токенів на хвилину. Base64 збільшує розмір бінарних даних приблизно на третину, і під час багатокрокових сесій попередні рядки Base64 накопичуються в історії розмови, поки не вичерпується ліміт вхідних даних, після чого одразу з’являються помилки 429 Quota Exceeded.
  • Декодування великих рядків у форматі Base64 та створення об’єктів Drive у Apps Script споживають час виконання та пам’ять скрипту.
  • У наступній діаграмі порівнюється цей підхід із прямим стрімінгом.

    Кращим рішенням є використання інструменту ggsrun, написаного на Go, безпосередньо всередині самої сандбокс-середовища, з введенням токена доступу OAuth у момент виконання за допомогою функції ScriptApp.getOAuthToken(). Потім сандбокс передає бінарні дані до Drive через внутрішню мережу Google Cloud зі швидкістю понад 2 МБ/с, уникаючи при цьому обмежень пам’яті Apps Script, лімітів розміру відповідей та вичерпання квот на токени.

    Стрімінг працює в обох напрямках

    Пряма передача даних від хмари до хмари також допомагає під час завантажень. Коли великі зовнішні набори даних, такі як зображення високої роздільної здатності, аудіо, відео, файли CSV чи JSON об’ємом у кілька гігабайт або моделі машинного навчання, мають потрапити до сандбоксу, їх вбудовування у запит у форматі Base64 чи серіалізованого тексту майже миттєво споживає всю квоту токенів вхідних даних, що призводить до помилки 429. За допомогою ggsrun сандбокс сам отримує файл з Drive, а запит потребує лише короткої інструкції на кшталт „завантажити цільовий набір даних з Drive та проаналізувати його“. Використання токенів вхідних даних знижується майже до нуля, що запобігає вичерпанню лімітів швидкості.

    Спільне використання однієї постійної пісочниці ще більше знижує витрати. Одна процедура налаштування створює environmentId, який можуть використовувати проекти Apps Script, локальні робочі станції Node.js, скрипти Python та пайплайни CI/CD. Дані набори, корпуси, бібліотеки чи заздалегідь навчені моделі, розміщені під /workspace/, стають доступними для кожного клієнта, тож ніхто не змушений перезавантажувати дані чи повторювати налаштування щоразу. Це зменшує затримки, пропускну здатність мережі та загальні витрати на використання API, а завдання можуть повторно використовувати спільні робочі файли та заздалегідь встановлені пакети.

    На наступній діаграмі показана повна архітектура, де Apps Script та локальні робочі станції Node.js координують роботу однієї постійної пісочниці за допомогою спільного environmentId, використовуючи двостороннє стрімінг та спільні набори даних для швидкого створення контенту.

    Вона складається з чотирьох частин:

    • Кілька клієнтів керують одним контейнером. Додатки на базі Cloud з використанням Apps Script використовують синхронний тригер та динамічний токен OAuth, тоді як локальні робочі станції на Node.js використовують потокову передачу даних у реальному часі через SSE та автентифікацію за допомогою gcloud.
    • Сандбокс зберігає спільні набори даних та інструменти для створення контенту, такі як Playwright, FFmpeg та esbuild, що дозволяє генерувати контент без повторної завантаження даних.
    • ggsrun download передає великі набори даних з Drive до сандбоксу у потоці, що уникає негайної вбудовки даних та допомагає зберегти ліміт у 200 тис. TPM.
    • ggsrun upload завантажує готові бінарні файли у Drive зі швидкістю 2 МБ/с або вище, що дозволяє обійти ліміт обсягу даних у 50 МБ та проблему обрізання виводу у стандартному виводі.

    Повний вихідний код (класи Apps Script, клієнти потоків у Node.js, набори тестів та необроблені журнали виконання) знаходиться у публічному репозиторії проекту на GitHub.

    Налаштування

    Спочатку створіть API-ключ у Google AI Studio. Він автентифікує запити до API Gemini v1beta Interactions and Environments. Потім створіть проект Apps Script одним із двох способів: окремий проект через script.google.com за допомогою опції «Новий проект» або проект, прив’язаний до контейнера, шляхом відкриття аркуша, документа чи форми та вибору «Розширення», а потім «Apps Script».

    Скопіюйте два файли з репозиторію до редактора:

    • ManagedAgentSandboxClient.js — це основний клас клієнта. Він керує життєвим циклом сандбоксу та динамічними змінними середовища, зберігає дані сесії у PropertiesService та застосовує розумні стратегії відкладення при отриманні коду помилки 429.
    • tests.js — це основний набір тестів. Він охоплює налаштування сандбоксу, перевірку інструментів, обробку медіа, веб-скрапінг та тестування продуктивності.

    Далі відкрийте Налаштування проєкту, потім Властивості скрипту та додайте властивість під назвою GEMINI_API_KEY, у якій буде зберігатися ключ. Нарешті, переконайтеся, що маніфест (appsscript.json) містить потрібні діапазони OAuth. Діапазон https://www.googleapis.com/auth/script.external_request необхідний для викликів UrlFetchApp, а https://www.googleapis.com/auth/drive дозволяє створювати цільові папки та завантажувати файли. Код, який використовує існуючі папки без DriveApp.createFolder(), може обійтися діапазоном https://www.googleapis.com/auth/drive.file. Журнали виконання кожного тесту знаходяться у файлі gas-src/execution-logs.md репозиторію.

    Налаштування спільного сандбоксу

    Виконання provisionSharedSandbox() запускає новий віддалений контейнер Linux, встановлює інструменти CLI та залежності, налаштовує шляхи до цільових дисків та зберігає отриманий environmentId у PropertiesService.

    Інфографіка показує чотири кроки. Спочатку сервіс Drive створює папку призначення під назвою ManagedAgent_Artifacts_YYYYMMDD. Потім контейнер із 4 vCPU та 16 GB запускає програми ggsrun, ffmpeg, sox, jq, typescript, esbuild та Playwright із Chromium за допомогою команди ініціалізації. Далі сандбокс перевіряє встановлені бінарні файли та повідомляє READY. Нарешті унікальний ідентифікатор environmentId зберігається під ключем SHARED_SANDBOX_SESSION, щоб пізніші тести та інші клієнти могли його повторно використати. Після цього виклик функції testListSandboxes() запитує API середовищ для підтвердження активності сандбоксу та повертає його метадані.

    Тест 1: користувацькі заголовки, які Apps Script перезаписує

    Перший тест показує те, що UrlFetchApp не може зробити. Apps Script автоматично замінює користувацький заголовок User-Agent на строк ідентифікатора проксі від Google, тоді як сандбокс зберігає будь-які заголовки, які ви встановили, оскільки він надсилає запити через сирі POSIX-сокети та нативний curl.

    Діаграма ілюструє запит із значенням User-Agent: sample user agent до адреси httpbin.org/anything. У середовищі Apps Script політика проксі платформи змінює цей заголовок. У сандбоксі ж curl зберігає оригінальну рядкову значення. Потім вбудований скрипт на Python порівнює отримані JSON-дані та виводить матрицю перевірки. Конкретно: Apps Script надсилає запит типу GET із власним заголовком, сандбокс надсилає той самий запит за допомогою curl, і порівняння підтверджує, що Apps Script замінив значення на Mozilla/5.0 (compatible; Google-Apps-Script; beanserver; ...), тоді як сандбокс точно зберіг sample user agent.

    Тест 2: автентифікація у Drive зсередини сандбоксу

    Цей тест перевіряє автентифікацію у Drive та прямий доступ через ggsrun шляхом вставки нового токену OAuth від ScriptApp.getOAuthToken() під час виконання цього кроку.

    На першому кроці Apps Script отримує токен та вставляє його як змінну середовища GGSRUN_AT, що усуває ризик закінчення терміну дії токена через годину. На другому кроці сандбокс створює файл для перевірки 00_ggsrun_verification.txt у каталозі /workspace/test2/ та завантажує його за допомогою ggsrun upload у режимі безблокуючого перезапису (--nc --cm OverwriteIfNewer -j). На третьому кроці ggsrun searchfiles запитує цю папку та повертає структуровані метадані, які підтверджують наявність усіх дев’яти файлів. Весь цикл створення, завантаження та перевірки зайняв 12,1 секунди, і жоден конфіденційний токен не зберігався між сеансами.

    Тест 3: безголове скрапінгування за допомогою Playwright

    Далі автоматизована безголова сесія Chromium у сандбоксі збирає динамічний контент, відрендерований за допомогою JavaScript, та створює скріншоти у кількох розмірах вікна перегляду.

    Скрипт Node.js Playwright відкриває сайт цитат, який рендерується за допомогою JavaScript (quotes.toscrape.com/js/), створює скріншоти всієї сторінки у розмірі десктопа (1280x800) та у режимі емуляції iPhone (375x812), потім натискає кнопку навігації по сторінках, бере скріншот другої сторінки у розмірі десктопа та витягує цитати у файл 02_Page2_Quotes.json. Розміри скріншотів становили приблизно 92,5 КБ для десктопного варіанту, 51,6 КБ для мобільного та 171,9 КБ для другої сторінки, плюс 4,1 КБ для JSON-файлу — загалом близько 320 КБ для чотирьох файлів. Одна команда ggsrun upload надіслала всі три PNG-файли та набір даних у форматі JSON до Drive без жодної конвертації у формат Base64, а весь процес зайняв 20,4 секунди. Це приклад рендерингу з боку клієнта, який Apps Script не може обробити нативно.

    Тест 4: синтез аудіо за допомогою FFmpeg та SoX

    Четвертий тест запускає нативну обробку цифрових сигналів у сандбоксі, створюючи багатотонову акордову структуру за допомогою FFmpeg та SoX.

    Три генератори синусоїдальних хвиль з частотами 440 Гц (A4), 554,37 Гц (C#5) та 659,25 Гц (E5) змішуються за допомогою фільтра ffmpeg amix у трьохсекундний MP3-файл мажорної акордової структури розміром 73,4 КБ. Потім ffprobe витягує метадані потоку у файл 03_Audio_Analysis.json, розміром 1,8 КБ. Команда ggsrun upload надсилає файл 03_Chord_Major.mp3 та JSON-файл безпосередньо на Drive. Синтез, витягування метаданих та завантаження зайняли 9,1 секунди.

    Тест 5: парсинг TypeScript та пакування за допомогою esbuild

    Цей тест демонструє роботу сучасних інструментів для обробки JavaScript та TypeScript усередині сандбокса.

    Два набори інструментів працюють над модулем TypeScript matrix.ts, який визначає універсальні класи та інтерфейси та знаходиться у каталозі /workspace/test5/. У першій гілці скрипт Node.js використовує офіційний API компілятора TypeScript для аналізу дерева абстрактної синтаксису та експортує схеми інтерфейсів та методів у файл 04_TypeScript_AST.json, розміром лише 152 байти. У другій гілці esbuild об’єднує та оптимізує модуль у окремий файл у форматі IIFE — 04_Matrix_Bundle.iife.js розміром 1,2 КБ, причому час збірки становить лише 13 мілісекунд. Потім команда ggsrun upload надсилає обидва результати до Drive, і загальний час виконання склав 10,0 секунд.

    Тест 6: пряме завантаження у форматі Base64 через Apps Script

    У цьому тесті 10-тисячобайтовий бінарний файл переміщується з сандбоксу до Drive двома різними способами.

    У підході A сандбокс створює 10 КБ бінарного файлу з /dev/urandom та передає його до Drive за допомогою ggsrun протягом однієї операції (freshInteraction: true). У підході B сандбокс кодує ці самі байти у форматі Base64 та повертає їх у текстовій відповіді API, після чого Apps Script декодує цей рядок та зберігає файл у Drive. Підхід A виконався за 16,20 секунди зі швидкістю 0,60 КБ/с та не використовував ресурси CPU Apps Script. Підхід B зайняв 32,13 секунди зі швидкістю 0,30 КБ/с, плюс ще 1,23 секунди на використання CPU Apps Script, тож підхід A був у 1,98 разів швидшим. Крім того, він уникає приблизно 33-відсоткового збільшення розміру файлу через кодування Base64 та проблем з вичерпанням токенів, які виникають під час багатоетапних сеансів. Записаний звіт виглядає ось так.

    ================================================================================
    PERFORMANCE BENCHMARK REPORT: 10,000 BYTES FILE TRANSFER TO GOOGLE DRIVE
    ================================================================================
    | Metric                       | Approach A: Direct ggsrun Upload | Approach B: Base64 via Gemini API -> GAS |
    | :--------------------------- | :------------------------------- | :--------------------------------------- |
    | Transfer Method              | Direct Sandbox-to-Drive (Go CLI) | Base64 Stream -> GAS -> Drive            |
    | Drive File Name              | benchmark_10kb_ggsrun.bin        | benchmark_10kb_gas.bin                   |
    | Verified File Size           | 10,000 bytes (9.77 KB)           | 10,000 bytes (9.77 KB)                   |
    | API Turns Required           | 1 Turn (Direct Offload)          | 1 Turn (Base64 Retrieval)                |
    | Local GAS Processing Time    | 0.00 s (Zero CPU overhead)       | 1.23 s (Base64 Decode & Blob Creation)   |
    | Total End-to-End Duration    | 16.20 s                          | 32.13 s                                  |
    | Effective Throughput         | 0.60 KB/s                        | 0.30 KB/s                                |
    | Performance Multiplier       | 1.98x FASTER                     | Baseline (Higher Latency & Token Usage)  |
    ================================================================================
    

    Стрімінг через ggsrun був у 1,98 разу швидшим, усунув усі витрати CPU та пам’яті Apps Script на декодування та не використовував жодної частини квоти на токени розмови. Для навантажень розміром у кілька мегабайт саме цей прямий шлях допомагає уникнути помилок 429 Quota Exceeded.

    Керування тією самою сандбокс-середовищем з локальної робочої станції

    Щоб показати, що одну й ту саму постійну сандбокс-середовище Linux можна керувати як з Apps Script, так і з ноутбука, був створений клієнт Node.js зі стрімінгом Server-Sent Events на основі SDK @google/genai. Apps Script працює синхронно та блокуюче, збираючи події агента наприкінці HTTP-запиту, тому локальний виконувач надає розробникам кілька переваг, яких немає у Apps Script.

    • Реальний перегляд процесу виконання. Кроки міркувань (thought), команди оболонки (code_execution_call), дані stdout та stderr зі сандбоксу (code_execution_result) та текст моделі (model_output) транслюються у термінал у момент їх виникнення з використанням кольорів ANSI.
    • Гібридний підхід до розробки. Запити та набори інструментів можна прототипувати, відлагоджувати та налаштовувати локально з реальним часом зворотного зв’язку, а потім переносити у автоматизовані тригери Apps Script, які працюють без людського керування.
    • Просте спільне використання сандбоксу. Введення ідентифікатора, створеного під час налаштування Apps Script, у поле ENVIRONMENT_ID у локальному файлі .env дозволяє під’єднати клієнта до існуючого контейнера з усіма встановленими пакетами, скомпільованими бінарними файлами та файлами робочого простору, без необхідності повторної установки.
  • Автоматична обробка OAuth. Запускач отримує нові токени доступу до Google за допомогою команди gcloud auth print-access-token та вставляє їх у змінну GGSRUN_AT, що дозволяє здійснювати завантаження безпосередньо у Drive так само, як у Apps Script, без необхідності вручну копіювати облікові дані.
  • Для запуску локальних тестів потрібно виконати чотири кроки.

    1. Клонуйте репозиторій та виконайте команду npm install у файлі local-node.js-src.
    2. Скопіюйте файл .env.example як .env та введіть три значення: ваш ключ API як GEMINI_API_KEY, ідентифікатор сандбоксу з довгим терміном дії як ENVIRONMENT_ID, а також ідентифікатор папки у Drive, куди потрібно записувати дані, як TARGET_FOLDER_ID.
    3. Виконайте команду npm test або окремий тест за допомогою npm run test:1 від test:6 та спостерігайте за роботою агента в реальному часі.
    4. Після завершення виконайте npm run test:teardown, щоб видалити віддалений сандбокс та звільнити обчислювальні ресурси в хмарі.

    Повні необроблені записи разом із результатами прямої трансляції зберігаються у файлі local-node.js-src/execution-logs.md репозиторію, і вони показують ті самі функціональні результати, що й виконання Apps Script.

    Шаблони API для керованих агентів

    Наведені нижче шаблони узагальнюють поширені способи роботи з API Gemini v1beta Interactions and Environments. Кожен з них надсилає запит POST до одного й того самого базового кінцевого пункту.

    POST https://generativelanguage.googleapis.com/v1beta/interactions?key=${API_KEY}
    Content-Type: application/json
    

    У сценарії 1 одна постійна пісочниця використовується багатьма клієнтами. Створіть віддалене середовище один раз, встановивши environment.type на "remote", збережіть environment_id, який буде повернутий, та надсилайте його у вигляді звичайного рядка у подальших запитах з Apps Script, Node.js, Python або завдання CI/CD.

    {
      "agent": "antigravity-preview-05-2026",
      "input": "Run task in shared container...",
      "environment": "environments/env-12345"
    }
    

    Сценарій 2 — це ізольована сандбокс-середовище для кожного запуску. Коли завданню потрібне абсолютно нове Linux-середовище, встановіть environment.type у значення "remote" під час кожного виклику.

    {
      "agent": "antigravity-preview-05-2026",
      "input": "Execute client-specific isolated task...",
      "environment": {
        "type": "remote"
      }
    }
    

    Сценарій 3 зберігає контекст розмови між кроками. Включайте previous_interaction_id кожного разу, коли агенту потрібно пам’ятати попередні міркування, змінні чи результати команд.

    {
      "agent": "antigravity-preview-05-2026",
      "input": "Based on the previous output, proceed to step 2...",
      "environment": "environments/env-12345",
      "previous_interaction_id": "interaction-prev-67890"
    }
    

    Сценарій 4 повторно використовує пісочницю зі свіжим контекстом за допомогою опції freshInteraction. Вкажіть наявний environment_id та пропустіть previous_interaction_id. Усі файли та встановлені інструменти в контейнері залишаються, тоді як історія розмови скидається до нуля токенів, що дозволяє уникнути перевищення ліміту швидкості TPM.

    {
      "agent": "antigravity-preview-05-2026",
      "input": "Execute a completely new task in the existing sandbox...",
      "environment": "environments/env-12345"
    }
    

    Порівняння за мить

    Матриця узагальнення розміщує підходи поруч один з одним.

    Що це дає

    Поєднання Apps Script з Gemini Managed Agents дозволяє подолати звичайні обмеження безсерверного середовища виконання. Постійний віддалений сандбокс, а також пряма передача даних між хмарним сховищем та сандбоксом у обох напрямках, уможливлюють виконання завдань, які Apps Script сам по собі не може обробити, а також уникають обмежень щодо обсягу даних у API та квот на токени для спілкування.

    • Обмеження Apps Script долаються. Завдання, які потребують нативного Linux-середовища, такі як збір даних з браузера без вікна керування за допомогою Playwright, синтез аудіо з FFmpeg та компіляція TypeScript за допомогою esbuild, тепер можуть запускатися з Apps Script.
    • Штучний інтелект для міркувань поєднується з виконанням у Linux. Замість виконання фіксованих зовнішніх команд механізм міркувань Gemini працює разом із справжньою оболонкою, тож команди створюються на льоту, код виконується під час роботи, а агент може самостійно виправляти свої дії під час складних робочих процесів.
    • Стрімінг у обох напрямках дозволяє заощадити токени та дані. Завантаження результатів роботи та отримання великих наборів даних з Drive до сандбоксу уникає необхідності включення даних у запити та їх конвертації у формат Base64, що зменшує кількість вхідних та вихідних токенів, допомагає уникнути обмеження у 50 МБ та ліміту у 200 000 TPM.
    • Спільні сандбокси знижують витрати. Зберігання поширених наборів даних та інструментів у спільній файловій системі контейнера усуває необхідність повторного завантаження та налаштувань для кожного завдання, що зменшує затримки та використання пропускної здатності.
    • Бенчмарк підтверджує це. Пряме стрімінгу даних від хмари до хмари через CLI було у 1,98 разів швидшим, ніж отримання даних у форматі Base64 через API, без витрат на декодування в процесорі та без використання локальної пам’яті в Apps Script.