利用 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.Google 文档的 AI 摘要突发事件解决文档。

前提条件

如果您需要为组织启用以下前提条件,请让您的 Google Workspace 管理员为您启用:

  • 有权访问 Google ChatGoogle Workspace 帐号。
  • 为 Google Workspace 启用目录(联系人共享)。突发事件应用使用该目录查找突发事件响应者的联系信息,例如姓名和电子邮件地址。突发事件响应者必须是您的 Google Workspace 组织中拥有 Google Chat 帐号的用户。

目标

  • 构建响应突发事件的 Chat 应用。
  • 通过以下操作帮助用户响应突发事件:
    • 创建突发事件响应聊天室。
    • 发布汇总突发事件和响应的消息。
    • 通过交互式 Chat 应用功能支持协作。
  • 使用 Vertex AI 总结对话和解决方案。

架构

下图展示了突发事件响应 Google Chat 应用使用的 Google Workspace 和 Google Cloud 资源的架构。

突发事件响应 Google Chat 应用的架构

该架构展示了突发事件响应 Google Chat 应用如何处理突发事件和解决方案。

  1. 用户从 Apps 脚本上托管的外部网站发起突发事件。

  2. 网站向同样托管在 Apps 脚本上的 Google Chat 应用发送异步 HTTP 请求。

  3. Google Chat 突发事件响应应用会处理请求:

    1. Apps 脚本 Admin SDK 服务可获取团队成员信息,例如用户 ID 和电子邮件地址。

    2. 使用 Apps 脚本高级聊天服务向 Chat API 发送一组 HTTP 请求,突发事件响应 Google Chat 应用会创建突发事件 Chat 聊天室,向该聊天室填充团队成员,并向聊天室发送消息。

  4. 团队成员在 Chat 聊天室中讨论该事件。

  5. 团队成员会调用斜杠命令,以指示突发事件的解决方案。

    1. 使用 Apps 脚本高级聊天服务对 Chat API 进行 HTTP 调用时,系统会列出 Chat 聊天室的所有消息。

    2. Vertex AI 会接收列出的消息并生成摘要。

    3. Apps 脚本 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
  • 本地 Shell:如需使用本地开发环境,请安装initialize gcloud CLI。
    如需创建 Cloud 项目,请使用“gcloud projects create”命令:
    gcloud projects create PROJECT_ID
    为要创建的项目设置 ID,以替换 PROJECT_ID

为 Cloud 项目启用结算功能

Google Cloud 控制台

  1. 在 Google Cloud 控制台中,前往结算。点击 Menu 图标 > Billing > My Projects

    前往“我的项目”的“结算”页面

  2. 选择组织中,选择与您的 Google Cloud 项目关联的组织。
  3. 在项目行中,打开操作菜单 (),点击更改结算信息,然后选择相应的 Cloud Billing 帐号。
  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. 点击添加到表

  10. 点击更新

  11. 点击 Save and Continue(保存并继续)。

  12. 查看应用注册摘要,然后点击返回信息中心

创建和部署 Chat 应用

在下一部分中,您将复制和更新整个 Apps 脚本项目(其中包含 Chat 应用的所有必需应用代码),因此无需复制和粘贴每个文件。

某些函数的名称末尾包含下划线,例如 ChatApp.gs 中的 processSlashCommand_()。当函数在浏览器中打开时,下划线会在事件初始化网页中隐藏该函数。如需了解详情,请参阅私有函数

Apps 脚本支持两种文件类型:.gs 脚本和 .html 文件。为了遵循此支持,应用的客户端 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 脚本中的 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 文档 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 脚本项目

如需创建 Apps 脚本项目并将其与您的 Cloud 项目关联,请执行以下操作:

  1. 点击以下按钮,打开 Reply to events with Google Chat Apps Script 项目。
    打开项目
  2. 点击 Overview
  3. 在概览页面上,点击 用于制作副本的图标 复制
  4. 为您的 Apps 脚本项目副本命名:

    1. 点击使用 Google Chat 回复突发事件的副本

    2. 项目名称中,输入 Incident Management Chat app

    3. 点击重命名

  5. 在 Apps 脚本项目的副本中,转到 Consts.gs 文件并将 YOUR_PROJECT_ID 替换为您的 Cloud 项目的 ID。

设置 Apps 脚本项目的 Cloud 项目

  1. 在 Apps 脚本项目中,点击 项目设置的图标 项目设置
  2. Google Cloud Platform (GCP) 项目下,点击更改项目
  3. GCP 项目编号中,粘贴您的 Cloud 项目的项目编号。
  4. 点击设置项目。Cloud 项目与 Apps 脚本项目现已关联。

创建 Apps 脚本部署

现在所有代码已准备就绪,请部署 Apps 脚本项目。在 Google Cloud 中配置 Chat 应用时,您将使用部署 ID。

  1. 在 Apps 脚本中,打开突发事件响应应用的项目。

    前往 Apps 脚本

  2. 点击部署 > 新建部署

  3. 如果尚未选择插件 Web 应用,请在选择类型旁边点击部署类型 项目设置的图标,然后选择插件Web 应用

  4. 说明中,输入此版本的说明,例如 Complete version of incident management app

  5. 执行方式中,选择访问 Web 应用的用户

  6. 有权使用的人中,选择您 Workspace 组织中的任何人,其中“您的 Workspace 组织”是您的 Google Workspace 组织的名称。

  7. 点击部署。Apps 脚本会报告部署成功,并提供部署 ID 和突发事件初始化网页的网址。

  8. 记下 Web 应用网址,以便稍后在启动突发事件时访问。 复制部署 ID。在 Google Cloud 控制台中配置 Chat 应用时,您将使用此 ID。

  9. 点击完成

在 Google Cloud 控制台中配置 Chat 应用

本部分介绍如何使用您的 Chat 应用的相关信息(包括您刚刚在 Apps 脚本项目中创建的部署的 ID)在 Google Cloud 控制台中配置 Google Chat API。

  1. 在 Google Cloud 控制台中,点击菜单 > 更多产品 > Google Workspace > 产品库 > Google Chat API > 管理 > 配置

    前往 Chat API 配置

  2. 应用名称中,输入 Incident Management

  3. 头像网址中输入 https://developers.google.com/chat/images/quickstart-app-avatar.png

  4. 说明中,输入 Responds to incidents.

  5. 点击启用互动功能开关,将其切换到开启位置。

  6. 功能下方,依次选择接收 1 对 1 消息加入聊天室和群组对话

  7. 连接设置下,选择 Apps 脚本项目

  8. 部署 ID 中,粘贴您之前从 Apps 脚本项目部署中复制的 Apps 脚本部署 ID。

  9. 注册完全实现的 Chat 应用使用的斜杠命令

    1. 斜杠命令下,点击添加斜杠命令

    2. 名称中,输入 /closeIncident

    3. 命令 ID 中,输入 1

    4. 说明中,输入 Closes the incident being discussed in the space.

    5. 选择打开对话框

    6. 点击完成。斜杠命令已注册并列出。

  10. 可见性下,选择将此聊天应用提供给您的 Workspace 网域中的特定用户和群组使用,然后输入您的电子邮件地址。

  11. 日志下,选择将错误记录到 Logging

  12. 点击保存。系统会显示一条“已保存配置”消息,这意味着应用已准备好进行测试。

测试 Chat 应用

如需测试突发事件管理 Chat 应用,请从网页中发起突发事件,并验证 Chat 应用是否按预期运行:

  1. 前往 Apps 脚本部署 Web 应用网址。

  2. 当 Apps 脚本请求访问您的数据时,请点击查看权限,使用 Google Workspace 网域中的相应 Google 帐号登录,然后点击允许

  3. 系统会打开突发事件初始化网页。输入测试信息:

    1. 突发事件标题中,输入 The First Incident
    2. (可选)在事件响应者中,输入其他突发事件响应者的电子邮件地址。他们必须是您的 Google Workspace 组织中拥有 Google Chat 帐号的用户,否则聊天室创建失败。请勿输入您自己的电子邮件地址,因为该地址会自动包含在内。
    3. 初始消息中,输入 Testing the incident management Chat app.
  4. 点击创建 Chat 聊天室。系统随即会显示一条 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,然后点击关停以删除项目。
  • 阅读 Chat 应用设计原则,了解如何构建帮助和取悦用户的 Chat 应用。
  • 如需详细了解身份验证,请参阅对 Chat 应用和 Chat API 请求进行身份验证和授权
  • 突发事件响应应用使用用户凭据进行身份验证,以调用 API 和调用 Chat API 和 Vertex AI API 等 Google Cloud 服务。这意味着初始化突发事件的用户必须具有这些服务的访问权限。为了提高应用的可靠性,不妨考虑使用服务帐号(而不是调用 /closeIncident 斜杠命令的用户的用户凭据)来调用 Vertex AI API。如需获得有关为 Vertex AI API 配置服务帐号的帮助,请参阅使用自定义服务帐号