Создание собственных шагов Google Workspace Studio в Node.js в Cloud Run

1. Введение

Google Workspace Studio позволяет автоматизировать бизнес-процессы в приложениях Google Workspace, таких как Gmail, Google Chat, Google Диск и Google Таблицы. Если вам нужно запускать рабочие процессы из внешнего веб-приложения или выполнять собственную логику в облачной среде, вы можете расширить Google Workspace Studio с помощью дополнения Google Workspace с поддержкой HTTP, созданного на основе Node.js в Cloud Run.

  1. Собственный начальный шаг (workflowTrigger). Запускает поток Google Workspace Studio при возникновении внешнего события, например когда клиент отправляет запрос через внутренний веб-портал, вызывая Google Workspace Studio triggers.fire REST API.
  2. Шаг с пользовательским действием (workflowAction). Получает переменные от предыдущих шагов процесса, выполняет пользовательскую логику или логический вывод ИИ в сервисе Node.js и возвращает выходные переменные в Google Workspace Studio.

В этой практической работе вы создадите и развернете полное HTTP-расширение Node.js для Google Workspace Studio в Cloud Run, будете использовать Cloud Firestore для хранения активных подписок на триггеры потоков и отправленных заявок в разных версиях контейнера, а также классифицировать срочность заявок на пользовательском шаге действия с помощью Gemini 3.8 Flash (gemini-3.8-flash) на платформе агентов Gemini Enterprise с надежным резервным вариантом на основе сопоставления ключевых слов.

Что вы создадите

  • Веб-приложение Support Quick Portal и дополнение Google Workspace на основе HTTP, созданные с помощью Node.js, Express и Firestore в Cloud Run.
  • Собственный начальный шаг Новый запрос в службу поддержки (supportTicketTrigger), который сохраняет активные подписки на процесс в коллекции Firestore studio_triggers, регистрирует отправленные запросы в коллекции Firestore support_tickets и запускается при отправке запроса на веб-портале, передавая ticketTitle и ticketDescription в процесс Google Workspace Studio.
  • Настраиваемое действие Определить срочность (urgencyDetectorStep), которое классифицирует срочность запроса с помощью Gemini 3.8 Flash (gemini-3.8-flash) на Gemini Enterprise Agent Platform (aiplatform.googleapis.com) с автоматическим переходом на сопоставление ключевых слов (urgent, blocker, critical).

Что вы узнаете

  • Как настроить манифест развертывания дополнения Google Workspace, использующего конечные точки HTTP (deployment.json), с элементами workflowTrigger и workflowAction.
  • Как сохранить подписки на триггеры Google Workspace Studio (triggerId, notifyUri и userOAuthToken) в studio_triggers коллекции Firestore и запускать процессы с помощью triggers.fire REST API.
  • Как создать workflowAction пользовательский шаг в Node.js, используя структурированный вывод Gemini 3.8 Flash (gemini-3.8-flash) на платформе Gemini Enterprise Agent Platform с резервным ключевым словом.

Требования

  • Аккаунт Google Workspace с доступом к Google Workspace Studio.
  • Проект Google Cloud с включенными платежными функциями, созданный в той же организации Google Workspace, что и ваш аккаунт пользователя, чтобы вы могли устанавливать неопубликованные развертывания дополнений для тестирования.

2. Как настроить проект Google Cloud и Firestore

На этом этапе вы настраиваете окно запроса доступа OAuth для проекта, включаете необходимые API Google Cloud и Google Workspace в Cloud Shell, инициализируете базу данных (default) Firestore и сервисный аккаунт авторизации дополнения.

  1. Откройте консоль Google Cloud, выберите проект Google Cloud и нажмите на значок Активировать Cloud Shell на панели навигации вверху страницы.
  2. Настройте окно запроса доступа OAuth, чтобы Google Workspace мог предоставить клиенту OAuth доступ к вашему дополнению:
    1. Откройте страницу брендинга платформы аутентификации Google.
    2. Если появится запрос, нажмите Начать. Если окно запроса доступа уже настроено, перейдите к шагу 3.
    3. В разделе Информация о приложении введите Support Extension в поле Название приложения, выберите Адрес электронной почты для поддержки пользователей и нажмите Далее.
    4. В разделе Аудитория выберите Внутренняя и нажмите Далее.
    5. В разделе Контактная информация введите адрес электронной почты и нажмите Далее.
    6. В разделе Завершение примите Правила в отношении пользовательских данных сервисов API Google, нажмите Продолжить и выберите Создать.
  3. В Cloud Shell задайте активный проект (замените YOUR_PROJECT_ID на идентификатор проекта Google Cloud) и включите необходимые API:
    gcloud config set project YOUR_PROJECT_ID
    gcloud services enable \
      aiplatform.googleapis.com \
      firestore.googleapis.com \
      run.googleapis.com \
      cloudbuild.googleapis.com \
      artifactregistry.googleapis.com \
      appsmarket-component.googleapis.com \
      gsuiteaddons.googleapis.com \
      workspacestudio.googleapis.com
    
  4. Подготовьте базу данных (default) Firestore в us-central1 (если она ещё не создана) и инициализируйте сервисный аккаунт дополнений Google Workspace:
    gcloud firestore databases describe --database="(default)" >/dev/null 2>&1 || \
      gcloud firestore databases create \
        --database="(default)" \
        --location=us-central1 \
        --type=firestore-native \
        --quiet
    gcloud workspace-add-ons get-authorization
    

3. Как создать приложение Node.js с Firestore и Gemini 3.8 Flash

На этом шаге вы создадите приложение Node.js в ~/studio-support-addon. Чтобы код был модульным и читаемым, проект разбит на пять файлов:

  • firestore.js – инициализирует клиент Cloud Firestore (коллекции studio_triggers и support_tickets) и обновляет кешированные токены OAuth.
  • starter.js: реализует веб-перехватчики для нового запроса в службу поддержки (/onConfigTrigger и /onManageTrigger).
  • action.js: реализует веб-хуки (/onConfigUrgency и /onExecuteUrgency) для пользовательского действия Detect Urgency с помощью Gemini 3.8 Flash (gemini-3.8-flash) с резервным вариантом на основе сопоставления ключевых слов.
  • portal.js: обслуживает веб-интерфейс портала быстрого доступа к поддержке (GET /) и запускает активные триггеры Google Workspace Studio (POST /api/submit-ticket).
  • index.js: объединяет все маршруты Express и запускает HTTP-сервер.

Инициализация проекта и package.json

  1. Создайте каталог проекта и перейдите в него:
    mkdir -p ~/studio-support-addon && cd ~/studio-support-addon
    
  2. Создание package.json с помощью express, @google-cloud/firestore и @google/genai:
    cat << 'EOF' > package.json
    {
      "name": "studio-support-addon",
      "version": "1.0.0",
      "type": "module",
      "main": "index.js",
      "scripts": {
        "start": "node index.js"
      },
      "dependencies": {
        "@google-cloud/firestore": "^7.11.0",
        "@google/genai": "^1.0.0",
        "express": "^4.21.2"
      }
    }
    EOF
    

firestore.js – общий статус Firestore и обновление токена OAuth

Создайте firestore.js, чтобы управлять двумя коллекциями Firestore и поддерживать актуальность кешированных токенов OAuth пользователей:

  • studio_triggers: хранит активные подписки на потоки Google Workspace Studio (triggerId, notifyUri и userOAuthToken), чтобы они сохранялись при перезапуске контейнера Cloud Run и событиях масштабирования до нуля.
  • support_tickets – содержит записи о каждом запросе в службу поддержки, отправленном через портал.
  • refreshTokenInFirestore(req): когда Google Workspace Studio вызывает любую из ваших конечных точек webhook, он включает в запрос краткосрочный authorizationEventObject.userOAuthToken. Этот помощник обновляет активные документы триггеров с помощью последнего токена, чтобы фоновые вызовы triggers.fire оставались авторизованными.
cat << 'EOF' > firestore.js
import { Firestore } from '@google-cloud/firestore';

export const db = new Firestore({
  projectId: process.env.GOOGLE_CLOUD_PROJECT || undefined,
  databaseId: '(default)',
  ignoreUndefinedProperties: true,
});

export const triggersCollection = db.collection(
  process.env.FIRESTORE_TRIGGERS_COLLECTION || 'studio_triggers'
);

export const ticketsCollection = db.collection(
  process.env.FIRESTORE_TICKETS_COLLECTION || 'support_tickets'
);

// Keep cached userOAuthToken fresh across active trigger documents
export async function refreshTokenInFirestore(req) {
  const freshToken = req.body?.authorizationEventObject?.userOAuthToken;
  if (!freshToken) return;
  try {
    const snapshot = await triggersCollection.get();
    if (snapshot.empty) return;
    const batch = db.batch();
    snapshot.docs.forEach((doc) => {
      batch.set(
        doc.ref,
        { userOAuthToken: freshToken, updatedAt: new Date().toISOString() },
        { merge: true }
      );
    });
    await batch.commit();
  } catch (err) {
    console.error('Error refreshing OAuth token in Firestore:', err.message);
  }
}
EOF

starter.js – пользовательский начальный шаг (workflowTrigger).

Создайте starter.js, чтобы реализовать две конечные точки веб-перехватчика, необходимые для пользовательского начального этапа Google Workspace Studio:

  • onConfigTrigger (POST /onConfigTrigger) – вызывается, когда пользователь выбирает Новый запрос в службу поддержки в качестве запускаемого процесса в Google Workspace Studio. Возвращает действие навигации pushCard, описывающее запуск.
  • onManageTrigger (POST /onManageTrigger). Вызывается, когда пользователь включает (req.body.workflow.triggerCreation) или отключает / удаляет (req.body.workflow.triggerDeletion) поток. Сохраняет или удаляет документ подписки в коллекции Firestore studio_triggers.
cat << 'EOF' > starter.js
import { triggersCollection, refreshTokenInFirestore } from './firestore.js';

// Custom Starter Configuration Card
export async function onConfigTrigger(req, res) {
  await refreshTokenInFirestore(req);
  res.status(200).json({
    action: {
      navigations: [
        {
          pushCard: {
            sections: [
              {
                header: 'Support Ticket Listener',
                widgets: [
                  {
                    textParagraph: {
                      text: 'This starter listens for new tickets submitted through the Support Quick Portal and stores subscriptions in Firestore.',
                    },
                  },
                ],
              },
            ],
          },
        },
      ],
    },
  });
}

// Custom Starter Subscription Lifecycle (Firestore studio_triggers collection)
export async function onManageTrigger(req, res) {
  try {
    const triggerCreation = req.body?.workflow?.triggerCreation;
    const triggerDeletion = req.body?.workflow?.triggerDeletion;
    const userOAuthToken =
      req.body?.authorizationEventObject?.userOAuthToken || null;

    if (triggerCreation?.triggerId) {
      const triggerId = String(triggerCreation.triggerId);
      const docId = encodeURIComponent(triggerId);
      await triggersCollection.doc(docId).set(
        {
          triggerId,
          notifyUri: triggerCreation.notifyUri || null,
          ...(userOAuthToken ? { userOAuthToken } : {}),
          updatedAt: new Date().toISOString(),
        },
        { merge: true }
      );
    }

    if (triggerDeletion?.triggerId) {
      const triggerId = String(triggerDeletion.triggerId);
      const docId = encodeURIComponent(triggerId);
      await triggersCollection.doc(docId).delete();
    }

    if (!triggerCreation && !triggerDeletion && userOAuthToken) {
      await refreshTokenInFirestore(req);
    }
  } catch (err) {
    console.error('Error in /onManageTrigger:', err.message);
  }
  res.status(200).json({});
}
EOF

action.js – шаг "Настраиваемое действие" (workflowAction) с Gemini 3.8 Flash.

Создайте action.js, чтобы реализовать две конечные точки вебхука, необходимые для шага настраиваемого действия Google Workspace Studio:

  • onConfigUrgency (POST /onConfigUrgency): возвращает карточку конфигурации с виджетом textInput, который задает hostAppDataSource.workflowDataSource.includeVariables: true, позволяя пользователям выбрать переменную ticketDescription, выводимую на начальном этапе.
  • onExecuteUrgency (POST /onExecuteUrgency): считывает входные данные ticketDescription из req.body.workflow.actionInvocation.inputs, классифицирует их влияние на бизнес и срочность с помощью Gemini 3.8 Flash (gemini-3.8-flash) на Gemini Enterprise Agent Platform со структурированным выводом (перечисление responseSchema ["High", "Normal"]), возвращается к detectKeywordUrgency() (urgent, blocker, critical), если вызов модели не удался, и возвращает выходную переменную urgencyLevel, используя hostAppAction.workflowAction.returnOutputVariablesAction.
cat << 'EOF' > action.js
import { GoogleGenAI, Type } from '@google/genai';
import { refreshTokenInFirestore } from './firestore.js';

// Initialize Google GenAI SDK for the Gemini Enterprise Agent Platform
const ai = new GoogleGenAI({
  vertexai: true,
  project: process.env.GOOGLE_CLOUD_PROJECT,
  location: 'global',
});

// Fallback keyword classifier if the Gemini API call is unavailable
export function detectKeywordUrgency(description) {
  const lower = (description || '').toLowerCase();
  return lower.includes('urgent') ||
    lower.includes('blocker') ||
    lower.includes('critical')
    ? 'High'
    : 'Normal';
}

// Custom Action Step Configuration Card
export async function onConfigUrgency(req, res) {
  await refreshTokenInFirestore(req);
  res.status(200).json({
    action: {
      navigations: [
        {
          pushCard: {
            sections: [
              {
                header: 'Urgency Detector Step',
                widgets: [
                  {
                    textInput: {
                      name: 'ticketDescription',
                      label: 'Source Description',
                      hintText:
                        'Select the description variable outputted by the starter',
                      hostAppDataSource: {
                        workflowDataSource: {
                          includeVariables: true,
                        },
                      },
                    },
                  },
                ],
              },
            ],
          },
        },
      ],
    },
  });
}

// Custom Action Step Execution: Gemini 3.8 Flash with keyword fallback
export async function onExecuteUrgency(req, res) {
  await refreshTokenInFirestore(req);
  const inputs = req.body?.workflow?.actionInvocation?.inputs || {};
  const description = inputs.ticketDescription?.stringValues?.[0] || '';

  let urgencyLevel = detectKeywordUrgency(description);
  try {
    const response = await ai.models.generateContent({
      model: 'gemini-3.8-flash',
      contents: `Evaluate the business impact and time sensitivity of this support ticket description and classify its urgency:\n\n${description}`,
      config: {
        responseMimeType: 'text/x.enum',
        responseSchema: {
          type: Type.STRING,
          enum: ['High', 'Normal'],
        },
      },
    });
    const candidate = (response.text || '').trim();
    if (candidate === 'High' || candidate === 'Normal') {
      urgencyLevel = candidate;
    }
  } catch (err) {
    console.error('Gemini fallback to keyword matching:', err.message);
  }

  res.status(200).json({
    hostAppAction: {
      workflowAction: {
        returnOutputVariablesAction: {
          variables: {
            urgencyLevel: {
              stringValues: [urgencyLevel],
            },
          },
        },
      },
    },
  });
}
EOF

portal.js – поддержка интерфейса портала поддержки и диспетчера triggers.fire

Создайте portal.js для веб-портала и отправки заявок в Google Workspace Studio:

  • renderPortal (GET /) – запрашивает коллекцию studio_triggers Firestore, чтобы показать, сколько активных триггеров Google Workspace Studio зарегистрировано, и предоставляет форму отправки запроса в формате HTML.
  • submitTicket (POST /api/submit-ticket): перебирает все активные документы-триггеры в studio_triggers, вызывает конечную точку triggers.fire Google Workspace Studio (https://workspacestudio.googleapis.com/v1/triggers/{triggerId}:fire) с выходными переменными ticketTitle и ticketDescription и уникальным значением requestId и сохраняет заявку в коллекцию Firestore support_tickets.
cat << 'EOF' > portal.js
import { triggersCollection, ticketsCollection } from './firestore.js';

// Support Quick Portal UI
export async function renderPortal(req, res) {
  let activeCount = 0;
  try {
    const snapshot = await triggersCollection.get();
    activeCount = snapshot.size;
  } catch (err) {
    console.error('Error reading studio_triggers collection:', err.message);
  }

  res.status(200).send(`<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Support Quick Portal</title>
    <style>
      body { font-family: Arial, sans-serif; padding: 24px; max-width: 540px; margin: auto; color: #202124; }
      .badge { background: #e8f0fe; color: #1967d2; padding: 8px 12px; border-radius: 6px; font-size: 14px; margin-bottom: 16px; }
      label { font-weight: bold; display: block; margin-top: 16px; }
      input, textarea { width: 100%; margin-top: 6px; padding: 10px; box-sizing: border-box; border: 1px solid #dadce0; border-radius: 4px; }
      button { background-color: #1a73e8; color: white; border: none; padding: 10px 18px; margin-top: 16px; font-weight: bold; cursor: pointer; border-radius: 4px; }
      button:hover { background-color: #1557b0; }
      #status { margin-top: 16px; font-weight: bold; }
    </style>
  </head>
  <body>
    <h2>Support Quick Portal</h2>
    <div class="badge">
      Active Google Workspace Studio triggers in Firestore (<code>studio_triggers</code>):
      <strong id="trigger-count">${activeCount}</strong>
    </div>
    <p>Submit a support ticket below to fire your Google Workspace Studio flow.</p>

    <label for="title">Ticket Title</label>
    <input type="text" id="title" placeholder="e.g. Database failure">

    <label for="description">Issue Description</label>
    <textarea id="description" rows="4" placeholder="Describe the issue..."></textarea>

    <button onclick="submitTicket()">Submit Ticket</button>
    <div id="status"></div>

    <script>
      async function submitTicket() {
        const title = document.getElementById('title').value.trim();
        const description = document.getElementById('description').value.trim();
        const statusEl = document.getElementById('status');

        if (!title || !description) {
          statusEl.style.color = '#d93025';
          statusEl.textContent = 'Please fill out both fields.';
          return;
        }

        statusEl.style.color = '#188038';
        statusEl.textContent = 'Submitting ticket...';

        try {
          const res = await fetch('/api/submit-ticket', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ title, description }),
          });
          const data = await res.json();
          if (data.ok) {
            statusEl.textContent =
              'Ticket saved to Firestore! Fired ' + data.triggersFired + ' Google Workspace Studio trigger(s).';
          } else {
            statusEl.style.color = '#d93025';
            statusEl.textContent = 'Error: ' + (data.error || 'Unknown error');
          }
        } catch (err) {
          statusEl.style.color = '#d93025';
          statusEl.textContent = 'Request failed: ' + err.message;
        }
      }
    </script>
  </body>
</html>`);
}

// Portal Form Submission -> Fire Active Google Workspace Studio Triggers
export async function submitTicket(req, res) {
  const title = req.body?.title || 'Untitled Ticket';
  const description = req.body?.description || '';
  const results = [];

  try {
    const snapshot = await triggersCollection.get();
    for (const doc of snapshot.docs) {
      const trigger = doc.data();
      if (!trigger.triggerId || !trigger.userOAuthToken) continue;

      const response = await fetch(
        `https://workspacestudio.googleapis.com/v1/triggers/${encodeURIComponent(trigger.triggerId)}:fire`,
        {
          method: 'POST',
          headers: {
            Authorization: `Bearer ${trigger.userOAuthToken}`,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            name: 'triggers/' + trigger.triggerId,
            outputs: {
              ticketTitle: { stringValues: [title] },
              ticketDescription: { stringValues: [description] },
            },
            requestId:
              'req_' + Date.now() + '_' + Math.floor(Math.random() * 1000),
          }),
        }
      );
      results.push({ triggerId: trigger.triggerId, status: response.status });
    }

    await ticketsCollection.add({
      title,
      description,
      triggersFired: results.length,
      createdAt: new Date().toISOString(),
    });

    res.status(200).json({ ok: true, triggersFired: results.length, results });
  } catch (err) {
    console.error('Error in /api/submit-ticket:', err.message);
    res.status(500).json({ ok: false, error: err.message });
  }
}
EOF

index.js – точка входа на сервер Express

Создайте index.js, чтобы связать маршруты портала и конечные точки веб-перехватчиков Google Workspace Studio в HTTP-сервер Express, который прослушивает PORT (по умолчанию 8080 в Cloud Run):

cat << 'EOF' > index.js
import express from 'express';
import { onConfigTrigger, onManageTrigger } from './starter.js';
import { onConfigUrgency, onExecuteUrgency } from './action.js';
import { renderPortal, submitTicket } from './portal.js';

const app = express();
app.use(express.json());

// Support Quick Portal routes
app.get('/', renderPortal);
app.post('/api/submit-ticket', submitTicket);

// Google Workspace Studio Custom Starter Step webhooks
app.post('/onConfigTrigger', onConfigTrigger);
app.post('/onManageTrigger', onManageTrigger);

// Google Workspace Studio Custom Action Step webhooks
app.post('/onConfigUrgency', onConfigUrgency);
app.post('/onExecuteUrgency', onExecuteUrgency);

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Support Extension listening on port ${PORT}`);
});
EOF

4. Как развернуть в Cloud Run и установить дополнение

На этом шаге вы развернете сервис Node.js в Cloud Run, предоставите разрешения IAM для вызывающего дополнения Google Workspace, а также для среды выполнения Firestore и платформы агентов, создадите манифест deployment.json и установите развертывание дополнения.

  1. Разверните сервис support-extension в Cloud Run с переменными среды проекта и коллекции Firestore:
    gcloud run deploy support-extension \
      --source . \
      --region us-central1 \
      --allow-unauthenticated \
      --set-env-vars="GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project),FIRESTORE_TRIGGERS_COLLECTION=studio_triggers,FIRESTORE_TICKETS_COLLECTION=support_tickets" \
      --quiet
    
  2. Предоставьте сервисному аккаунту дополнений Google Workspace разрешение roles/run.invoker для сервиса Cloud Run, чтобы Google Workspace Studio мог вызывать конечные точки вашего веб-перехватчика:
    ADDON_SA=$(gcloud workspace-add-ons get-authorization --format="value(serviceAccountEmail)")
    gcloud run services add-iam-policy-binding support-extension \
      --region=us-central1 \
      --member="serviceAccount:${ADDON_SA}" \
      --role="roles/run.invoker" \
      --quiet
    
  3. Предоставьте аккаунту сервиса времени выполнения Cloud Run разрешения roles/datastore.user (для чтения и записи коллекций Firestore studio_triggers и support_tickets) и roles/aiplatform.user (для вызова Gemini на платформе агентов Gemini Enterprise):
    PROJECT_ID=$(gcloud config get-value project)
    PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}" --format="value(projectNumber)")
    RUNTIME_SA=$(gcloud run services describe support-extension --region=us-central1 --format="value(spec.template.spec.serviceAccountName)")
    RUNTIME_SA=${RUNTIME_SA:-"${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"}
    
    gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
      --member="serviceAccount:${RUNTIME_SA}" \
      --role="roles/datastore.user" \
      --condition=None \
      --quiet
    
    gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
      --member="serviceAccount:${RUNTIME_SA}" \
      --role="roles/aiplatform.user" \
      --condition=None \
      --quiet
    
  4. Получите URL развернутого сервиса Cloud Run и создайте манифест deployment.json, в котором определены элементы supportTicketTrigger (workflowTrigger) и urgencyDetectorStep (workflowAction):
    SERVICE_URL=$(gcloud run services describe support-extension --region=us-central1 --format="value(status.url)")
    
    cat << EOF > deployment.json
    {
      "oauthScopes": [
        "https://www.googleapis.com/auth/workspace.studio.trigger",
        "https://www.googleapis.com/auth/userinfo.email"
      ],
      "addOns": {
        "common": {
          "name": "Support Extension",
          "logoUrl": "https://www.gstatic.com/images/branding/productlogos/buganizer/v1/192px.svg"
        },
        "studio": {
          "flows": {
            "workflowElements": [
              {
                "id": "supportTicketTrigger",
                "state": "ACTIVE",
                "name": "New Support Ticket",
                "description": "Fires when someone submits a ticket in the Support Quick Portal.",
                "workflowTrigger": {
                  "inputs": [],
                  "outputs": [
                    {
                      "id": "ticketTitle",
                      "description": "Ticket Title",
                      "cardinality": "SINGLE",
                      "dataType": { "basicType": "STRING" }
                    },
                    {
                      "id": "ticketDescription",
                      "description": "Ticket Description",
                      "cardinality": "SINGLE",
                      "dataType": { "basicType": "STRING" }
                    }
                  ],
                  "onConfigFunction": "${SERVICE_URL}/onConfigTrigger",
                  "onManageFunction": "${SERVICE_URL}/onManageTrigger"
                }
              },
              {
                "id": "urgencyDetectorStep",
                "state": "ACTIVE",
                "name": "Detect Urgency",
                "description": "Evaluates the ticket description to detect High or Normal urgency.",
                "workflowAction": {
                  "inputs": [
                    {
                      "id": "ticketDescription",
                      "description": "Description to scan",
                      "cardinality": "SINGLE",
                      "dataType": { "basicType": "STRING" }
                    }
                  ],
                  "outputs": [
                    {
                      "id": "urgencyLevel",
                      "description": "Detected Urgency (High / Normal)",
                      "cardinality": "SINGLE",
                      "dataType": { "basicType": "STRING" }
                    }
                  ],
                  "onConfigFunction": "${SERVICE_URL}/onConfigUrgency",
                  "onExecuteFunction": "${SERVICE_URL}/onExecuteUrgency"
                }
              }
            ]
          }
        },
        "httpOptions": {
          "authorizationHeader": "SYSTEM_ID_TOKEN"
        }
      }
    }
    EOF
    
  5. Создайте (или замените) и установите дополнение Google Workspace для своего аккаунта и распечатайте URL портала быстрой поддержки:
    gcloud workspace-add-ons deployments create support-extension --deployment-file=deployment.json || \
      gcloud workspace-add-ons deployments replace support-extension --deployment-file=deployment.json
    gcloud workspace-add-ons deployments install support-extension
    echo "Support Quick Portal URL: ${SERVICE_URL}"
    

5. Как тестировать собственные шаги в Google Workspace Studio

  1. Откройте Google Workspace Studio (если вкладка браузера уже открыта, обновите ее) и нажмите Новый процесс.
  2. Нажмите Выбрать стартовый пакет > Расширение для поддержки > Новый запрос в службу поддержки.
  3. Нажмите Добавить шаг (+) > Расширение для поддержки > Определить срочность. В поле Описание источника нажмите + Переменные и выберите Описание заявки.
  4. Нажмите Добавить шаг (+) и добавьте шаг Chat > Уведомить меня в Chat, в котором будут переменные Название заявки, Уровень срочности и Описание заявки.
  5. В правом верхнем углу включите функцию On (Включено). Google Workspace Studio сразу же вызывает вашу конечную точку /onManageTrigger в Cloud Run, которая записывает активную подписку на триггер в коллекцию studio_triggers в Firestore.
  6. Откройте URL портала быстрого доступа к поддержке (https://support-extension-...run.app) в новой вкладке, убедитесь, что на портале отображается 1 активный триггер в Firestore, и отправьте два запроса, чтобы протестировать классификацию Gemini 3.8 Flash (gemini-3.8-flash):
    • Важная заявка (Название заявки: London office lockout, Описание проблемы: All 500 employees in the London office are locked out of their workstations 20 minutes before the board presentation.). Даже без жестко заданных ключевых слов, таких как urgent или blocker, gemini-3.8-flash классифицирует эту заявку как High.
    • Обычный запрос (Название запроса: Conference room adapters, Описание проблемы: Could we order two extra HDMI adapters for the third-floor conference room next month?): gemini-3.8-flash классифицирует этот запрос как Normal.
  7. Проверьте в Google Chat и на вкладке Действия в Google Workspace Studio, что процесс был выполнен для обоих запросов с тегами Urgency Level: High и Urgency Level: Normal.

6. Организовать

Чтобы избежать постоянных расходов в аккаунте Google Cloud, удалите развернутые ресурсы в Cloud Shell:

gcloud workspace-add-ons deployments uninstall support-extension
gcloud workspace-add-ons deployments delete support-extension --quiet
gcloud run services delete support-extension --region=us-central1 --quiet
gcloud firestore databases delete --database="(default)" --quiet

7. Поздравляем!

Вы создали, развернули и протестировали расширение HTTP для Google Workspace Studio с помощью Firestore, Cloud Run и Gemini 3.8 Flash на Gemini Enterprise Agent Platform.

Что мы обсудили

  • Настройка манифеста дополнения HTTP для Google Workspace Studio (deployment.json) с элементами workflowTrigger и workflowAction.
  • Сохранение подписок Google Workspace Studio workflowTrigger в коллекции studio_triggers Firestore и регистрация отправленных на портал данных в support_tickets, чтобы триггеры переживали масштабирование и повторное развертывание Cloud Run.
  • Создание шага workflowAction, который классифицирует запросы в службу поддержки с помощью Gemini 3.8 Flash (gemini-3.8-flash) со структурированным выводом на Gemini Enterprise Agent Platform с отказоустойчивым резервным вариантом сопоставления ключевых слов.

Дальнейшие действия