Google Chat、Vertex AI、Apps Script を使用してインシデントに対応する

このチュートリアルでは、インシデントにリアルタイムで対応する Google Chat アプリの作成方法について説明します。インシデントに対応する際、このアプリは Chat スペースを作成してデータを入力し、メッセージ、スラッシュ コマンド、ダイアログを使用してインシデントの解決を容易にします。また、AI を使用してインシデント対応を Google ドキュメント ドキュメントに要約します。

インシデントは、解決のためにチームによる早急な対応が必要なイベントです。インシデントの例:

  • 時間的制約のあるケースが顧客管理(CRM)プラットフォームで作成され、サービスチームが協力して問題を解決する必要がある場合。
  • システムがオフラインになり、サイト信頼性エンジニア(SRE)のグループにアラートが発せられ、それらのチームが協力してシステムをオンラインに戻すことができます。
  • 高マグニチュードの地震が発生したため、救急隊員は対応を調整する必要があります。

このチュートリアルでは、ウェブページでボタンをクリックしてインシデントが報告されると、インシデント アラートが開始されます。ウェブページは、インシデントの基本的な情報(タイトル、説明、回答者のメールアドレス)の入力をユーザーに求めて、インシデントをシミュレートします。

インシデント管理の Chat アプリの実例をご覧ください。

  • インシデントを開始したウェブサイト。
    図 1: ユーザーがインシデントを報告できるウェブサイト。
  • インシデントの Chat スペースが作成されたという通知。
    図 2.インシデントの Chat スペースが作成されたという通知。
  • インシデント対応の Chat スペース。
    図 3.インシデント対応の Chat スペース。
  • スラッシュ コマンドでインシデントを解決する。
    図 4.スラッシュ コマンドでインシデントを解決する。
  • インシデント解決ダイアログ。
    図 5.インシデント解決ダイアログ
  • スペースで共有されたインシデント解決に関する Google ドキュメントのドキュメント。
    図 6.スペースで共有されたインシデント解決に関する Google ドキュメントのドキュメント。
  • AI によるインシデント解決の概要を示す Google ドキュメント。
    図 7.AI サマリー インシデント解決に関する Google ドキュメント ドキュメント。

前提条件

組織でこれらの前提条件のいずれかを有効にする必要がある場合は、Google Workspace 管理者に有効にするよう依頼してください。

  • Google Chat へのアクセス権を持つ Google Workspace アカウント。
  • Google Workspace のディレクトリ(連絡先の共有)を有効にする。インシデント アプリは、このディレクトリを使用してインシデント対応者の連絡先情報(名前やメールアドレスなど)を検索します。インシデント対応者は、Google Workspace 組織の Google Chat アカウントを持つユーザーである必要があります。

目標

  • インシデントに対応する Chat アプリを構築します。
  • 次の方法でユーザーがインシデントに対応できるようにします。
    • インシデント対応のためのスペースの作成
    • インシデントと対応をまとめたメッセージを投稿。
    • インタラクティブな Chat アプリ機能で コラボレーションをサポートします
  • Vertex AI で会話と解決策を要約する。

アーキテクチャ

次の図は、インシデント対応の Google Chat アプリで使用される Google Workspace リソースと Google Cloud リソースのアーキテクチャを示しています。

インシデント対応 Google Chat アプリのアーキテクチャ

このアーキテクチャは、インシデント対応の Google Chat アプリがインシデントを処理する仕組みと解決策を示しています。

  1. ユーザーが、Apps Script でホストされている外部ウェブサイトからインシデントを開始します。

  2. このウェブサイトは、同じく Apps Script でホストされている Google Chat アプリに非同期 HTTP リクエストを送信します。

  3. インシデント対応の Google Chat アプリがリクエストを処理します。

    1. Apps Script Admin SDK サービスは、ユーザー ID やメールアドレスなどのチームメンバー情報を取得します。

    2. インシデント対応 Google Chat アプリは、Apps Script Advanced Chat サービスを使用して Chat API への一連の HTTP リクエストを使用して、インシデント Chat スペースを作成し、チームメンバーを配置して、そのスペースにメッセージを送信します。

  4. チームメンバーが Chat スペースでインシデントについて話し合います。

  5. チームメンバーがスラッシュ コマンドを呼び出してインシデントに解決策を通知します。

    1. Apps Script Advanced Chat サービスを使用して Chat API を HTTP 呼び出しすると、Chat スペースのすべてのメッセージが一覧表示されます。

    2. Vertex AI は、リストされたメッセージを受信して、サマリーを生成します。

    3. Apps Script の DocumentApp サービスは、ドキュメント ドキュメントを作成し、そのドキュメントに Vertex AI の概要を追加します。

    4. インシデント対応の Google Chat アプリが Chat API を呼び出して、概要ドキュメント ドキュメントへのリンクを共有するメッセージを送信します。

環境を準備する

このセクションでは、Chat アプリ用の Google Cloud プロジェクトを作成して構成する方法について説明します。

Google Cloud プロジェクトを作成する

Google Cloud コンソール

  1. Google Cloud コンソールで、メニュー > [IAM と管理] > [プロジェクトを作成] に移動します。

    [プロジェクトの作成] に移動

  2. [プロジェクト名] フィールドに、プロジェクトのわかりやすい名前を入力します。

    省略可: プロジェクト ID を編集するには、[編集] をクリックします。プロジェクトの作成後にプロジェクト ID を変更することはできません。プロジェクトの存続期間に応じて、ニーズに合った ID を選択してください。

  3. [ロケーション] フィールドで [参照] をクリックして、プロジェクトの潜在的なロケーションを表示します。[選択] をクリックします。
  4. [作成] をクリックします。Google Cloud コンソールが [ダッシュボード] ページに移動し、数分以内にプロジェクトが作成されます。

gcloud CLI

次のいずれかの開発環境で、Google Cloud CLI(「gcloud」)にアクセスします。

  • Cloud Shell: gcloud CLI がすでに設定されているオンライン ターミナルを使用するには、Cloud Shell をアクティブにします。
    Cloud Shell をアクティブにする
  • ローカルシェル: ローカル開発環境を使用するには、gcloud CLI をインストールしてinitializeします。
    Cloud プロジェクトを作成するには、gcloud projects create コマンドを使用します。
    gcloud projects create PROJECT_ID
    PROJECT_ID は、作成するプロジェクトの ID を設定して置き換えます。

Cloud プロジェクトの課金を有効にする

Google Cloud コンソール

  1. Google Cloud コンソールで、[お支払い] に移動します。メニュー アイコン > [お支払い] > [マイ プロジェクト] をクリックします。

    [マイ プロジェクトの課金] に移動

  2. [組織を選択] で、Google Cloud プロジェクトに関連付けられた組織を選択します。
  3. プロジェクトの行で [アクション] メニュー()を開き、[お支払い情報を変更] をクリックして、Cloud 請求先アカウントを選択します。
  4. [アカウントを設定] をクリックします。

gcloud CLI

  1. 使用可能な請求先アカウントを一覧表示するには、次のコマンドを実行します。
    gcloud alpha billing accounts list
  2. 請求先アカウントを Google Cloud プロジェクトにリンクします。
    gcloud beta billing projects link PROJECT_ID --billing-account=BILLING_ACCOUNT_ID

    次のように置き換えます。

    • PROJECT_ID は、課金を有効にする Cloud プロジェクトのプロジェクト ID です。
    • BILLING_ACCOUNT_ID は、Google Cloud プロジェクトにリンクする請求先アカウント ID です。

API を有効にする

Google Cloud コンソール

  1. Google Cloud コンソールで、Google Chat API、Google ドキュメント API、Admin SDK API、Vertex AI API を有効にします。

    API を有効にする

  2. 正しい Cloud プロジェクトで API が有効になっていることを確認し、[次へ] をクリックします。

  3. 正しい API が有効になっていることを確認し、[有効にする] をクリックします。

gcloud CLI

  1. 必要に応じて、現在の Cloud プロジェクトを gcloud config set project コマンドで作成したプロジェクトに設定します。

    gcloud config set project PROJECT_ID
    

    PROJECT_ID は、作成した Cloud プロジェクトのプロジェクト ID に置き換えます。

  2. gcloud services enable コマンドを使用して、Google Chat API、Google ドキュメント API、Admin SDK API、Vertex AI API を有効にします。

    gcloud services enable chat.googleapis.com docs.googleapis.com admin.googleapis.com aiplatform.googleapis.com
    

認証と権限付与の設定

認証と認可により、Chat アプリは Google Workspace と Google Cloud のリソースにアクセスして、インシデント対応を処理できます。

このチュートリアルでは、アプリを内部で公開するため、プレースホルダ情報を使用しても問題ありません。アプリを外部に公開する前に、プレースホルダ情報を同意画面の実際の情報に置き換えます。

  1. Google Cloud コンソールで、メニュー > [API とサービス] > [OAuth 同意画面] に移動します。

    OAuth 同意画面に移動

  2. [ユーザーの種類] で [内部] を選択し、[作成] をクリックします。

  3. [アプリ名] に「Incident Management」と入力します。

  4. [ユーザー サポートメール] で、メールアドレスまたは適切な Google グループを選択します。

  5. [デベロッパーの連絡先情報] にメールアドレスを入力します。

  6. [Save and Continue] をクリックします。

  7. [スコープを追加または削除] をクリックします。パネルに、Cloud プロジェクトで有効にした各 API のスコープのリストが表示されます。

  8. [スコープを手動で追加] に、次のスコープを貼り付けます。

    • https://www.googleapis.com/auth/chat.spaces.create
    • https://www.googleapis.com/auth/chat.memberships
    • https://www.googleapis.com/auth/chat.memberships.app
    • https://www.googleapis.com/auth/chat.messages
    • https://www.googleapis.com/auth/documents
    • https://www.googleapis.com/auth/admin.directory.user.readonly
    • https://www.googleapis.com/auth/script.external_request
    • https://www.googleapis.com/auth/userinfo.email
    • https://www.googleapis.com/auth/cloud-platform
  9. [Add to Table] をクリックします。

  10. [更新] をクリックします。

  11. [Save and Continue] をクリックします。

  12. アプリ登録の概要を確認し、[ダッシュボードに戻る] をクリックします。

Chat アプリを作成してデプロイする

次のセクションでは、Chat アプリに必要なすべてのアプリケーション コードを含む Apps Script プロジェクト全体をコピーして更新するため、各ファイルをコピーして貼り付ける必要はありません。

一部の関数には名前の末尾にアンダースコアが付いています(例: ChatApp.gsprocessSlashCommand_())。アンダースコアにより、インシデント初期化ウェブページがブラウザで開いている場合、そのウェブページでは関数が非表示になります。詳細については、プライベート関数をご覧ください。

Apps Script は、.gs スクリプトと .html ファイルの 2 つのファイル形式をサポートしています。このサポートを遵守するために、アプリのクライアント側 JavaScript は <script /> タグ内、CSS は HTML ファイル内の <style /> タグ内に含まれています。

必要に応じて、GitHub でプロジェクト全体を表示できます。

GitHub で表示

各ファイルの概要は次のとおりです。

Consts.gs

Cloud プロジェクト ID、Vertex AI ロケーション ID、インシデントを閉じるためのスラッシュ コマンド ID など、他のコードファイルから参照される定数を定義します。

Consts.gs のコードを表示

apps-script/incident-response/Consts.gs
const PROJECT_ID = 'replace-with-your-project-id';
const VERTEX_AI_LOCATION_ID = 'us-central1';
const CLOSE_INCIDENT_COMMAND_ID = 1;
ChatApp.gs

メッセージ、カードのクリック、スラッシュ コマンド、ダイアログなどの Chat インタラクション イベントを処理します。/closeIncident スラッシュ コマンドに応答して、インシデント解決の詳細を収集するダイアログを開きます。Chat API の spaces.messages.list メソッドを呼び出して、スペース内のメッセージを読み取ります。Apps Script で Admin SDK ディレクトリ サービスを使用してユーザー ID を取得します。

ChatApp.gs のコードを表示

apps-script/incident-response/ChatApp.gs
/**
 * Responds to a MESSAGE event in Google Chat.
 *
 * This app only responds to a slash command with the ID 1 ("/closeIncident").
 * It will respond to any other message with a simple "Hello" text message.
 *
 * @param {Object} event the event object from Google Chat
 */
function onMessage(event) {
  if (event.message.slashCommand) {
    return processSlashCommand_(event);
  }
  return { "text": "Hello from Incident Response app!" };
}

/**
 * Responds to a CARD_CLICKED event in Google Chat.
 *
 * This app only responds to one kind of dialog (Close Incident).
 *
 * @param {Object} event the event object from Google Chat
 */
function onCardClick(event) {
  if (event.isDialogEvent) {
    if (event.dialogEventType == 'SUBMIT_DIALOG') {
      return processSubmitDialog_(event);
    }
    return {
      actionResponse: {
        type: "DIALOG",
        dialogAction: {
          actionStatus: "OK"
        }
      }
    };
  }
}

/**
 * Responds to a MESSAGE event with a Slash command in Google Chat.
 *
 * This app only responds to a slash command with the ID 1 ("/closeIncident")
 * by returning a Dialog.
 *
 * @param {Object} event the event object from Google Chat
 */
function processSlashCommand_(event) {
  if (event.message.slashCommand.commandId != CLOSE_INCIDENT_COMMAND_ID) {
    return {
      "text": "Command not recognized. Use the command `/closeIncident` to close the incident managed by this space."
    };
  }
  const sections = [
    {
      header: "Close Incident",
      widgets: [
        {
          textInput: {
            label: "Please describe the incident resolution",
            type: "MULTIPLE_LINE",
            name: "description"
          }
        },
        {
          buttonList: {
            buttons: [
              {
                text: "Close Incident",
                onClick: {
                  action: {
                    function: "closeIncident"
                  }
                }
              }
            ]
          }
        }
      ]
    }
  ];
  return {
    actionResponse: {
      type: "DIALOG",
      dialogAction: {
        dialog: {
          body: {
            sections,
          }
        }
      }
    }
  };
}

/**
 * Responds to a CARD_CLICKED event with a Dialog submission in Google Chat.
 *
 * This app only responds to one kind of dialog (Close Incident).
 * It creates a Doc with a summary of the incident information and posts a message
 * to the space with a link to the Doc.
 *
 * @param {Object} event the event object from Google Chat
 */
function processSubmitDialog_(event) {
  const resolution = event.common.formInputs.description[""].stringInputs.value[0];
  const chatHistory = concatenateAllSpaceMessages_(event.space.name);
  const chatSummary = summarizeChatHistory_(chatHistory);
  const docUrl = createDoc_(event.space.displayName, resolution, chatHistory, chatSummary);
  return {
    actionResponse: {
      type: "NEW_MESSAGE",
    },
    text: `Incident closed with the following resolution: ${resolution}\n\nHere is the automatically generated post-mortem:\n${docUrl}`
  };
}

/**
 * Lists all the messages in the Chat space, then concatenate all of them into
 * a single text containing the full Chat history.
 *
 * For simplicity for this demo, it only fetches the first 100 messages.
 *
 * Messages with slash commands are filtered out, so the returned history will
 * contain only the conversations between users and not app command invocations.
 *
 * @return {string} a text containing all the messages in the space in the format:
 *          Sender's name: Message
 */
function concatenateAllSpaceMessages_(spaceName) {
  // Call Chat API method spaces.messages.list
  const response = Chat.Spaces.Messages.list(spaceName, { 'pageSize': 100 });
  const messages = response.messages;
  // Fetch the display names of the message senders and returns a text
  // concatenating all the messages.
  let userMap = new Map();
  return messages
    .filter(message => message.slashCommand === undefined)
    .map(message => `${getUserDisplayName_(userMap, message.sender.name)}: ${message.text}`)
    .join('\n');
}

/**
 * Obtains the display name of a user by using the Admin Directory API.
 *
 * The fetched display name is cached in the provided map, so we only call the API
 * once per user.
 *
 * If the user does not have a display name, then the full name is used.
 *
 * @param {Map} userMap a map containing the display names previously fetched
 * @param {string} userName the resource name of the user
 * @return {string} the user's display name
 */
function getUserDisplayName_(userMap, userName) {
  if (userMap.has(userName)) {
    return userMap.get(userName);
  }
  let displayName = 'Unknown User';
  try {
    const user = AdminDirectory.Users.get(
      userName.replace("users/", ""),
      { projection: 'BASIC', viewType: 'domain_public' });
    displayName = user.name.displayName ? user.name.displayName : user.name.fullName;
  } catch (e) {
    // Ignore error if the API call fails (for example, because it's an
    // out-of-domain user or Chat app)) and just use 'Unknown User'.
  }
  userMap.set(userName, displayName);
  return displayName;
}
ChatSpaceCreator.gs

ユーザーがインシデント初期化ウェブページで入力したフォームデータを受け取り、それを使用して Chat スペースを作成して入力し、インシデントに関するメッセージを投稿します。

ChatSpaceCreator.gs のコードを表示

apps-script/incident-response/ChatSpaceCreator.gs
/**
 * Creates a space in Google Chat with the provided title and members, and posts an
 * initial message to it.
 *
 * @param {Object} formData the data submitted by the user. It should contain the fields
 *                          title, description, and users.
 * @return {string} the resource name of the new space.
 */
function createChatSpace(formData) {
  const users = formData.users.trim().length > 0 ? formData.users.split(',') : [];
  const spaceName = setUpSpace_(formData.title, users);
  addAppToSpace_(spaceName);
  createMessage_(spaceName, formData.description);
  return spaceName;
}

/**
 * Creates a space in Google Chat with the provided display name and members.
 *
 * @return {string} the resource name of the new space.
 */
function setUpSpace_(displayName, users) {
  const memberships = users.map(email => ({
    member: {
      name: `users/${email}`,
      type: "HUMAN"
    }
  }));
  const request = {
    space: {
      displayName: displayName,
      spaceType: "SPACE",
      externalUserAllowed: true
    },
    memberships: memberships
  };
  // Call Chat API method spaces.setup
  const space = Chat.Spaces.setup(request);
  return space.name;
}

/**
 * Adds this Chat app to the space.
 *
 * @return {string} the resource name of the new membership.
 */
function addAppToSpace_(spaceName) {
  const request = {
    member: {
      name: "users/app",
      type: "BOT"
    }
  };
  // Call Chat API method spaces.members.create
  const membership = Chat.Spaces.Members.create(request, spaceName);
  return membership.name;
}

/**
 * Posts a text message to the space on behalf of the user.
 *
 * @return {string} the resource name of the new message.
 */
function createMessage_(spaceName, text) {
  const request = {
    text: text
  };
  // Call Chat API method spaces.messages.create
  const message = Chat.Spaces.Messages.create(request, spaceName);
  return message.name;
}
DocsApi.gs

Google Docs API を呼び出してユーザーの Google ドライブに Google ドキュメント ドキュメントを作成し、VertexAiApi.gs で作成されたインシデント情報の概要をドキュメントに書き込みます。

DocsApi.gs のコードを表示

apps-script/incident-response/DocsApi.gs
/**
 * Creates a Doc in the user's Google Drive and writes a summary of the incident information to it.
 *
 * @param {string} title The title of the incident
 * @param {string} resolution Incident resolution described by the user
 * @param {string} chatHistory The whole Chat history be included in the document
 * @param {string} chatSummary A summary of the Chat conversation to be included in the document
 * @return {string} the URL of the created Doc
 */
function createDoc_(title, resolution, chatHistory, chatSummary) {
  let doc = DocumentApp.create(title);
  let body = doc.getBody();
  body.appendParagraph(`Post-Mortem: ${title}`).setHeading(DocumentApp.ParagraphHeading.TITLE);
  body.appendParagraph("Resolution").setHeading(DocumentApp.ParagraphHeading.HEADING1);
  body.appendParagraph(resolution);
  body.appendParagraph("Summary of the conversation").setHeading(DocumentApp.ParagraphHeading.HEADING1);
  body.appendParagraph(chatSummary);
  body.appendParagraph("Full Chat history").setHeading(DocumentApp.ParagraphHeading.HEADING1);
  body.appendParagraph(chatHistory);
  return doc.getUrl();
}
VertexAiApi.gs

Vertex AI を使用して Chat スペース内の 会話を要約しますこの概要は、DocsAPI.gs に特別に作成されたドキュメントに投稿されています。

VertexAiApi.gs のコードを表示

apps-script/incident-response/VertexAiApi.gs
/**
 * Summarizes a Chat conversation using the Vertex AI text prediction API.
 *
 * @param {string} chatHistory The Chat history that will be summarized.
 * @return {string} The content from the text prediction response.
 */
function summarizeChatHistory_(chatHistory) {
  const prompt =
    "Summarize the following conversation between Engineers resolving an incident"
      + " in a few sentences. Use only the information from the conversation.\n\n"
      + chatHistory;
  const request = {
    instances: [
      { prompt: prompt }
    ],
    parameters: {
      temperature: 0.2,
      maxOutputTokens: 256,
      topK: 40,
      topP: 0.95
    }
  }
  const fetchOptions = {
    method: 'POST',
    headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() },
    contentType: 'application/json',
    payload: JSON.stringify(request)
  }
  const response = UrlFetchApp.fetch(
    `https://${VERTEX_AI_LOCATION_ID}-aiplatform.googleapis.com/v1`
      + `/projects/${PROJECT_ID}/locations/${VERTEX_AI_LOCATION_ID}`
      + "/publishers/google/models/text-bison:predict",
    fetchOptions);
  const payload = JSON.parse(response.getContentText());
  return payload.predictions[0].content;
}
WebController.gs

インシデント初期化ウェブサイトを提供します。

WebController.gs のコードを表示

apps-script/incident-response/WebController.gs
/**
 * Serves the web page from Index.html.
 */
function doGet() {
  return HtmlService
    .createTemplateFromFile('Index')
    .evaluate();
}

/**
 * Serves the web content from the specified filename.
 */
function include(filename) {
  return HtmlService
    .createHtmlOutputFromFile(filename)
    .getContent();
}

/**
 * Returns the email address of the user running the script.
 */
function getUserEmail() {
  return Session.getActiveUser().getEmail();
}
Index.html

インシデント初期化ウェブサイトを含む HTML。

Index.html のコードを表示

apps-script/incident-response/Index.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet'>
    <?!= include('Stylesheet'); ?>
  </head>
  <body>
    <div class="container">
      <div class="content">
        <h1>Incident Manager</h1>
        <form id="incident-form" onsubmit="handleFormSubmit(this)">
          <div id="form">
            <p>
              <label for="title">Incident title</label><br/>
              <input type="text" name="title" id="title" />
            </p>
            <p>
              <label for="users">Incident responders</label><br/>
              <small>
                Please enter a comma-separated list of email addresses of the users
                that should be added to the space.
                Do not include <?= getUserEmail() ?> as it will be added automatically.
              </small><br/>
              <input type="text" name="users" id="users" />
            </p>
            <p>
              <label for="description">Initial message</label></br>
              <small>This message will be posted after the space is created.</small><br/>
              <textarea name="description" id="description"></textarea>
            </p>
            <p class="text-center">
              <input type="submit" value="CREATE CHAT SPACE" />
            </p>
          </div>
          <div id="output" class="hidden"></div>
          <div id="clear" class="hidden">
            <input type="reset" value="CREATE ANOTHER INCIDENT" onclick="onReset()" />
          </div>
        </form>
      </div>
    </div>
    <?!= include('JavaScript'); ?>
  </body>
</html>
JavaScript.html

インシデント初期化ウェブサイトのフォームの動作(送信、エラー、クリアなど)を処理します。WebController.gs のカスタム include 関数によって Index.html に組み込まれます。

JavaScript.html のコードを表示

apps-script/incident-response/JavaScript.html
<script>
  var formDiv = document.getElementById('form');
  var outputDiv = document.getElementById('output');
  var clearDiv = document.getElementById('clear');

  function handleFormSubmit(formObject) {
    event.preventDefault();
    outputDiv.innerHTML = 'Please wait while we create the space...';
    hide(formDiv);
    show(outputDiv);
    google.script.run
      .withSuccessHandler(updateOutput)
      .withFailureHandler(onFailure)
      .createChatSpace(formObject);
  }

  function updateOutput(response) {
    var spaceId = response.replace('spaces/', '');
    outputDiv.innerHTML =
      '<p>Space created!</p><p><a href="https://mail.google.com/chat/#chat/space/'
        + spaceId
        + '" target="_blank">Open space</a></p>';
    show(outputDiv);
    show(clearDiv);
  }

  function onFailure(error) {
    outputDiv.innerHTML = 'ERROR: ' + error.message;
    outputDiv.classList.add('error');
    show(outputDiv);
    show(clearDiv);
  }

  function onReset() {
    outputDiv.innerHTML = '';
    outputDiv.classList.remove('error');
    show(formDiv);
    hide(outputDiv);
    hide(clearDiv);
  }

  function hide(element) {
    element.classList.add('hidden');
  }

  function show(element) {
    element.classList.remove('hidden');
  }
</script>
Stylesheet.html

インシデント初期化ウェブサイトの CSS。これは、WebController.gs のカスタム include 関数によって Index.html に組み込まれます。

Stylesheet.html のコードを表示

apps-script/incident-response/Stylesheet.html
<style>
  * {
    box-sizing: border-box;
  }
  body {
    font-family: Roboto, Arial, Helvetica, sans-serif;
  }
  div.container {
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    top: 0; bottom: 0; left: 0; right: 0;
  }
  div.content {
    width: 80%;
    max-width: 1000px;
    padding: 1rem;
    border: 1px solid #999;
    border-radius: 0.25rem;
    box-shadow: 0 2px 2px 0 rgba(66, 66, 66, 0.08), 0 2px 4px 2px rgba(66, 66, 66, 0.16);
  }
  h1 {
    text-align: center;
    padding-bottom: 1rem;
    margin: 0 -1rem 1rem -1rem;
    border-bottom: 1px solid #999;
  }
 #output {
    text-align: center;
    min-height: 250px;
  }
  div#clear {
    text-align: center;
    padding-top: 1rem;
    margin: 1rem -1rem 0 -1rem;
    border-top: 1px solid #999;
  }
  input[type=text], textarea {
    width: 100%;
    padding: 1rem 0.5rem;
    margin: 0.5rem 0;
    border: 0;
    border-bottom: 1px solid #999;
    background-color: #f0f0f0;
  }
  textarea {
    height: 5rem;
  }
  small {
    color: #999;
  }
  input[type=submit], input[type=reset] {
    padding: 1rem;
    border: none;
    background-color: #6200ee;
    color: #fff;
    border-radius: 0.25rem;
    width: 25%;
  }
  .hidden {
    display: none;
  }
  .text-center {
    text-align: center;
  }
  .error {
    color: red;
  }
</style>

Cloud プロジェクト番号と ID を確認する

  1. Google Cloud コンソールで、Cloud プロジェクトに移動します。

    Google Cloud コンソールに移動

  2. 設定とユーティリティ > [プロジェクトの設定] をクリックします。

  3. [プロジェクト番号] フィールドと [プロジェクト ID] フィールドの値をメモします。これらは次のセクションで使用します。

Apps Script プロジェクトを作成する

Apps Script プロジェクトを作成して Cloud プロジェクトに接続するには:

  1. 次のボタンをクリックして、[Google Chat でインシデントに対応する] Apps Script プロジェクトを開きます。
    プロジェクトを開く
  2. [ 概要] をクリックします。
  3. 概要ページで [コピー作成アイコン コピーを作成] をクリックします。
  4. Apps Script プロジェクトのコピーに名前を付けます。

    1. [インシデント対応: Google Chat のコピー] をクリックします。

    2. [プロジェクトのタイトル] に「Incident Management Chat app」と入力します。

    3. [名前を変更] をクリックします。

  5. Apps Script プロジェクトのコピーで、Consts.gs ファイルに移動し、YOUR_PROJECT_ID を Cloud プロジェクトの ID に置き換えます。

Apps Script プロジェクトの Cloud プロジェクトを設定する

  1. Apps Script プロジェクトで、プロジェクト設定のアイコン [プロジェクトの設定] をクリックします。
  2. [Google Cloud Platform(GCP)プロジェクト] で、[プロジェクトを変更] をクリックします。
  3. [GCP のプロジェクト番号] に、Cloud プロジェクトのプロジェクト番号を貼り付けます。
  4. [プロジェクトを設定] をクリックします。これで、Cloud プロジェクトと Apps Script プロジェクトが接続されました。

Apps Script のデプロイを作成する

すべてのコードを配置したので、Apps Script プロジェクトをデプロイします。デプロイ ID は、Google Cloud で Chat アプリを構成するときに使用します。

  1. Apps Script でインシデント対応アプリのプロジェクトを開きます。

    Apps Script に移動

  2. [デプロイ] > [新しいデプロイ] をクリックします。

  3. [アドオン] と [ウェブアプリ] がまだ選択されていない場合は、[タイプを選択] の横でデプロイタイプ プロジェクト設定のアイコン をクリックし、[アドオン] と [ウェブアプリ] を選択します。

  4. [説明] に、このバージョンの説明を入力します(Complete version of incident management app など)。

  5. [Execute as] で、[User creating the web app] を選択します。

  6. [アクセスできるユーザー] で、[Workspace 組織内のすべてのユーザー] を選択します。ここで、「your Workspace 組織」は Google Workspace 組織の名前です。

  7. [デプロイ] をクリックします。Apps Script はデプロイの成功を報告し、デプロイ ID とインシデント初期化ウェブページの URL を提供します。

  8. 後でインシデントの開始時にアクセスできるように、ウェブアプリの URL をメモします。[デプロイ ID] をコピーします。この ID は、Google Cloud コンソールで Chat アプリを構成するときに使用します。

  9. [Done] をクリックします。

Google Cloud コンソールで Chat アプリを構成する

このセクションでは、Apps Script プロジェクトから作成したデプロイメントの ID など、Chat アプリに関する情報を使用して、Google Cloud コンソールで Google Chat API を構成する方法について説明します。

  1. Google Cloud コンソールで、メニュー アイコン > [その他のプロダクト] > [Google Workspace] > [プロダクト ライブラリ] > [Google Chat API] > [管理] > [構成] をクリックします。

    Chat API の構成に移動

  2. [アプリ名] に「Incident Management」と入力します。

  3. [アバターの URL] に「https://developers.google.com/chat/images/quickstart-app-avatar.png」と入力します。

  4. [説明] に「Responds to incidents.」と入力します。

  5. [インタラクティブ機能を有効にする] をクリックしてオンにします。

  6. [機能] で、[1 対 1 のメッセージを受信する]、[スペースとグループの会話に参加する] を選択します。

  7. [接続設定] で、[Apps Script プロジェクト] を選択します。

  8. [デプロイ ID] に、先ほど Apps Script プロジェクトのデプロイからコピーした Apps Script のデプロイ ID を貼り付けます。

  9. 完全に実装された Chat アプリで使用するスラッシュ コマンドを登録します。

    1. [スラッシュ コマンド] で、[スラッシュ コマンドを追加] をクリックします。

    2. [名前] に「/closeIncident」と入力します。

    3. [コマンド ID] に「1」と入力します。

    4. [説明] に「Closes the incident being discussed in the space.」と入力します。

    5. [ダイアログを開く] を選択します。

    6. [完了] をクリックします。スラッシュ コマンドが登録され、一覧表示されます。

  10. [公開設定] で、[この Chat アプリを Workspace ドメイン内の特定のユーザーとグループが使用できるようにする] をオンにして、メールアドレスを入力します。

  11. [ログ] で、[エラーを Logging に記録] を選択します。

  12. [保存] をクリックします。構成が保存されたことを示すメッセージが表示されます。これは、アプリのテストの準備ができたことを意味します。

Chat アプリをテストする

インシデント管理の Chat アプリをテストするには、ウェブページからインシデントを開始し、Chat アプリが期待どおりに動作することを確認します。

  1. Apps Script デプロイ ウェブアプリの URL に移動します。

  2. Apps Script からデータへのアクセス権限が求められたら、[権限を確認] をクリックし、Google Workspace ドメインの適切な Google アカウントでログインして、[許可] をクリックします。

  3. インシデントの初期化ウェブページが開きます。テスト情報を入力します。

    1. [Incident title] に「The First Incident」と入力します。
    2. 必要に応じて、[インシデント対応者] に他のインシデント対応者のメールアドレスを入力します。Google Workspace 組織の Google Chat アカウントを持つユーザーである必要があります。そうでない場合、スペースの作成に失敗します。メールアドレスは自動で登録されるため、ご自身のメールアドレスは入力しないでください。
    3. [最初のメッセージ] に「Testing the incident management Chat app.」と入力します。
  4. [Create Chat Space] をクリックします。creating space というメッセージが表示されます。

  5. スペースが作成されると、Space created! というメッセージが表示されます。[スペースを開く] をクリックして、Chat のスペースを新しいタブで開きます。

  6. 必要に応じて、自分と他のインシデント対応者はスペースでメッセージを送信できます。このアプリは、Vertex AI を使用してこれらのメッセージを要約し、事後ドキュメントを共有します。

  7. インシデント対応を終了して解決プロセスを開始するには、Chat スペースに「/closeIncident」と入力します。インシデント管理ダイアログが開きます。

  8. [インシデントを閉じる] に、インシデントの解決の説明を入力します(Test complete など)。

  9. [インシデントを閉じる] をクリックします。

インシデント管理アプリは、スペース内のメッセージを一覧表示して、Vertex AI で要約し、その要約を Google ドキュメント ドキュメントに貼り付けて、スペースでドキュメントを共有します。

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、Cloud プロジェクトを削除することをおすすめします。

  1. Google Cloud コンソールで、[リソースの管理] ページに移動します。メニュー > [IAM と管理] > [リソースの管理] をクリックします。

    Resource Manager に移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。
  • Google Chat API でできることの詳細やリファレンス ドキュメントをご覧ください。
  • ユーザーの役に立つ Chat アプリを作成する方法については、Chat アプリの設計原則をご覧ください。
  • 認証の詳細については、Chat アプリと Chat API リクエストの認証と承認をご覧ください。
  • インシデント対応アプリは、ユーザー認証情報を使用して認証を行い、API を呼び出し、Chat API や Vertex AI API などの Google Cloud サービスを呼び出します。つまり、インシデントを初期化するユーザーは、これらのサービスにアクセスできる必要があります。アプリをさらに堅牢にするには、/closeIncident スラッシュ コマンドを呼び出すユーザーのユーザー認証情報ではなく、サービス アカウントとして Vertex AI API を呼び出すことを検討してください。Vertex AI API のサービス アカウントの構成については、カスタム サービス アカウントを使用するをご覧ください。