1. 准备工作
什么是 Gemini Enterprise?
Gemini Enterprise 是一个先进的智能体平台,它将 Google AI 的卓越能力带给每一位员工,融入每一个工作流。它使团队能够在同一安全环境中发现、创建、共享和运行 AI 智能体。
- 使用高级模型:用户可以立即使用 Google 最强大的多模态 AI(包括 Gemini)来应对复杂的业务挑战。
- 利用专业智能体:该套件包含可直接使用的 Google 智能体,可用于研究、编码和记笔记,立即创造价值。
- 赋能每一位员工:通过无代码和专业代码选项,各部门的员工都可以构建和管理自己的自定义智能体,以实现工作流自动化。
- 让代理基于数据运行:代理可以安全地连接到公司内部数据和第三方应用,以确保其回答在上下文中准确无误。
- 集中式治理:管理员可以直观呈现和审核所有智能体活动,确保组织符合严格的安全和合规性标准。
- 通过生态系统进行扩展:该平台与广泛的合作伙伴应用和服务提供商网络集成,可在不同系统之间扩展自动化功能。
什么是 Google Workspace?
Google Workspace 是一套基于云的高效办公和协作解决方案,专为个人、学校和企业打造:
- 沟通:专业电子邮件服务 (Gmail)、视频会议 (Meet) 和团队消息服务 (Chat)。
- 内容创作:用于撰写文档 (Google 文档)、构建电子表格 (Google 表格) 和设计演示文稿 (Google 幻灯片) 的工具。
- 组织:共享日历(日历)和数字记事(Keep)。
- 存储空间:用于安全地保存和共享文件的集中式云空间(云端硬盘)。
- 管理:用于管理用户和安全设置的管理控件(Workspace 管理控制台)。
哪些类型的自定义集成?
Google Workspace 和 Gemini Enterprise 形成强大的反馈环路,其中 Workspace 提供实时数据和协作上下文,而 Gemini Enterprise 提供自动执行智能工作流所需的模型、智能体推理和编排功能。
- 智能连接:Google 管理的数据存储区、API 和 MCP 服务器(Google 管理的服务器和自定义服务器)可让代理安全无缝地访问 Workspace 数据,并代表用户执行操作。
- 自定义代理:团队可以使用无代码设计器或专业代码框架,基于管理员管控的 Workspace 数据和操作构建专业代理。
- 原生集成:无论是通过专用界面组件还是后台进程,Workspace 加购项都能弥合 AI 系统与 Chat 和 Gmail 等应用之间的差距。这样,客服人员就可以在用户需要时立即提供贴合情境的帮助。
通过将 Google Workspace 强大的效率生态系统与 Gemini Enterprise 先进的智能体功能相结合,组织可以借助自定义的、以数据为基础的 AI 智能体来转变运营方式,直接在团队日常使用的工具中自动执行复杂的工作流程。
前提条件
如果您想在自己的环境中完成所有步骤,则需要:
- 具备 Google Cloud 和 Python 基础知识。
- 您是所有者且已启用结算功能的 Google Cloud 项目。如需检查现有项目是否已启用结算功能,请参阅验证项目的结算状态。如需创建项目并设置结算,请参阅创建 Google Cloud 项目。如需更改项目所有权,请参阅管理项目成员或更改项目所有权。
- 已启用 Gemini Enterprise Standard 版 / Plus 版。如需比较 Gemini Enterprise 各个版本,请参阅比较 Gemini Enterprise 的各个版本。如果您没有 Gemini Enterprise 许可,则可以在下一步中选择创建试用许可。
- 拥有可访问 Google Chat 的 Google Workspace 商务版或企业版账号,并且已启用智能功能。
- Google Cloud CLI 已针对您的 Google Cloud 项目安装并初始化。
- 已安装 Python 3.11 及更高版本,请参阅官方 Python 网站上的说明。
构建内容
在此 Codelab 中,我们将构建三个解决方案,这些解决方案将 Gemini Enterprise AI 智能体与 Google Workspace 紧密集成。他们将展示可用于与数据、操作和界面互动的架构模式。
无代码自定义智能体
借助此代理,用户可以使用自然语言搜索数据并针对 Workspace 执行操作。它依赖于以下元素:
- 模型:Gemini。
- 数据和操作:Google Workspace(日历、Gmail、云端硬盘、NotebookLM)的 Gemini Enterprise 数据存储区、Google 搜索。
- 代理构建工具:Gemini Enterprise Agent Designer。
- 代理宿主:Gemini Enterprise。
- 界面:Gemini Enterprise Web 应用。


专业代码自定义代理
借助此代理,用户可以使用自定义工具和规则,以自然语言搜索数据并针对 Workspace 执行操作。它依赖于以下元素:
- 模型:Gemini。
- 数据和操作:Google Workspace(日历、Gmail、云端硬盘、NotebookLM)的 Gemini Enterprise 数据存储区、Google 搜索、Google 管理的 Vertex AI 搜索模型上下文协议 (MCP) 服务器、用于发送 Google Chat 消息(通过 Google Chat API)的自定义工具函数。
- 智能体构建工具:智能体开发套件 (ADK)。
- 代理主机:Vertex AI Agent Engine。
- 界面:Gemini Enterprise Web 应用。


作为 Google Workspace 加购项的默认代理
借助此代理,用户可以在 Workspace 应用界面中以自然语言搜索 Workspace 数据。它依赖于以下元素:
- 模型:Gemini。
- 数据:Google Workspace(日历、Gmail、云端硬盘、NotebookLM)的 Gemini Enterprise 数据存储区、Google 搜索。
- 代理宿主:Gemini Enterprise。
- 界面:适用于 Chat 和 Gmail 的 Google Workspace 加购项(可轻松扩展到 Google 日历、云端硬盘、文档、表格和幻灯片)。
- Google Workspace 加购项:Apps 脚本、Gemini Enterprise 和 Vertex AI API、上下文(用户元数据、所选 Gmail 邮件)。


学习内容
- Gemini Enterprise 与 Google Workspace 之间可实现数据和操作的集成点。
- 用于构建托管在 Gemini Enterprise 中的自定义智能体的无代码和专业代码选项。
- 用户可以通过哪些方式从 Gemini Enterprise Web 应用和 Google Workspace 应用访问代理。
2. 进行设置
查看概念
Gemini Enterprise 应用
Gemini Enterprise 应用可为最终用户提供搜索结果、操作和智能体。在 API 的上下文中,“应用”一词可以与“引擎”一词互换使用。应用必须连接到数据存储区,才能使用其中的数据来提供搜索结果、答案或操作。
Gemini Enterprise Web 应用
Gemini Enterprise Web 应用与 Gemini Enterprise 应用相关联。它是一个集中式 AI 主页,员工可在此处使用单个聊天界面搜索孤立的公司数据、运行专门的 AI 代理来处理复杂的工作流程,并生成具有企业级隐私保护功能的专业级内容。
初始化和访问资源
在本部分中,您可以使用自己偏好的网络浏览器访问和配置以下资源。
Gemini Enterprise 应用
在新标签页中打开 Google Cloud 控制台,然后按以下步骤操作:
- 选择您的项目。
- 在 Google Cloud 搜索字段中,搜索并选择 Gemini Enterprise,然后点击 + 创建应用。如果您没有 Gemini Enterprise 许可,系统会提示您激活 30 天免费试用许可。
- 将应用名称设置为
codelab。 - 系统会根据名称生成 ID,并显示在相应字段下方,请复制该 ID。
- 将多区域设置为
global (Global)。 - 点击创建。

- 应用创建完成后,系统会自动将您重定向到 Gemini Enterprise > 概览。
- 在获取完整访问权限下,点击设置身份。
- 在新界面中,选择使用 Google Identity,然后点击确认员工身份。

- 系统会保存配置,并自动将您重定向到 Gemini Enterprise > 概览。
- 前往配置。
- 在功能管理标签页中,开启启用代理设计工具,然后点击保存。

Gemini Enterprise Web 应用
在 Cloud 控制台中打开 Gemini Enterprise,然后按以下步骤操作:
- 点击名为
codelab的应用。 - 复制显示的网址,因为我们将在后续步骤中使用该网址来前往 Gemini Enterprise Web 应用。

3. 无代码自定义代理
借助此代理,用户可以使用自然语言搜索数据并针对 Workspace 执行操作。它依赖于以下元素:
- 模型:Gemini。
- 数据和操作:Google Workspace(日历、Gmail、云端硬盘、NotebookLM)的 Gemini Enterprise 数据存储区、Google 搜索。
- 代理构建工具:Gemini Enterprise Agent Designer。
- 代理宿主:Gemini Enterprise。
- 界面:Gemini Enterprise Web 应用。
查看概念
Gemini
Gemini 是 Google 推出的一款多模态 LLM。它能帮助人们释放潜力,从而加强想象力、增加好奇心并提高工作效率。
Gemini Enterprise 数据存储区
Gemini Enterprise 数据存储区是一种实体,其中包含从第一方数据源(例如 Google Workspace)或第三方应用(例如 Jira 或 Salesforce)提取的数据。包含第三方应用数据的数据存储区也称为数据连接器。
Gemini Enterprise Agent Designer
Gemini Enterprise 智能体设计工具是一个交互式无代码/低代码平台,用于在 Gemini Enterprise 中创建、管理和启动单步和多步智能体。
查看解决方案架构

启用 API
Gemini Enterprise Workspace 数据存储区需要启用以下 API:
- 在 Google Cloud 控制台中,启用 Calendar API、Gmail API 和 People API:

- 依次点击菜单 ☰ > API 和服务 > 已启用的 API 和服务,然后确认列表中包含 Google Calendar API、Gmail API 和 People API。
配置 OAuth 权限请求屏幕
Gemini Enterprise Workspace 日历和 Gmail 操作需要配置权限请求页面:
- 在 Google Cloud 控制台中,依次点击菜单 ☰ > Google Auth Platform > 品牌推广。
- 点击开始使用。
- 在应用信息下,将应用名称设置为
Codelab。 - 在用户支持电子邮件中,选择一个支持电子邮件地址,以便用户在对自己的同意情况有疑问时与您联系。
- 点击下一步。
- 在受众下,选择内部。
- 点击下一步。
- 在联系信息下,输入一个电子邮件地址,以便您接收有关项目变更的通知。
- 点击下一步。
- 在完成部分,查看 Google API 服务用户数据政策,如果您同意该政策,请选择我同意 Google API 服务:用户数据政策。
- 依次点击继续和创建。

- 系统会保存配置,并自动将您重定向到 Google Auth Platform > 概览。
- 前往数据访问权限。
- 点击添加或移除范围。
- 复制以下授权范围,然后将其粘贴到手动添加授权范围字段中。
https://www.googleapis.com/auth/calendar.readonly
https://www.googleapis.com/auth/calendar.events
https://www.googleapis.com/auth/calendar.calendars
https://www.googleapis.com/auth/gmail.send
https://www.googleapis.com/auth/gmail.readonly
- 依次点击添加到表格、更新和保存。

如需了解详情,请参阅完整的配置 OAuth 权限请求指南。
创建 OAuth 客户端凭据
为 Gemini Enterprise 创建新的 OAuth 客户端以对用户进行身份验证:
- 在 Google Cloud 控制台中,依次点击菜单 ☰ > Google Auth 平台 > 客户端。
- 点击 + 创建客户端。
- 在应用类型部分,选择 Web 应用。
- 将名称设置为
codelab。 - 跳过已获授权的 JavaScript 来源。
- 在已获授权的重定向 URI 部分中,点击添加 URI,然后输入
https://vertexaisearch.cloud.google.com/oauth-redirect。 - 点击创建。
- 系统会显示一个对话框,其中包含您新创建的 OAuth 客户端 ID 和密钥。请妥善保存此信息。

创建数据存储区
在 Cloud 控制台中打开 Gemini Enterprise,然后按以下步骤操作:
- 点击名为
codelab的应用。 - 在导航菜单中,点击关联的数据存储区。
- 点击 + 新建数据存储区。
- 在来源中,搜索 Google 日历,然后点击选择。
- 在操作部分,输入之前步骤中保存的客户端 ID 和客户端密钥,然后点击确认身份验证,并按照相应步骤对 OAuth 客户端进行身份验证和授权。
- 启用“创建日历活动”和“更新日历活动”操作。
- 点击继续。

- 在配置部分中,将数据连接器名称设置为
calendar。 - 点击创建。
- 系统会自动将您重定向到已关联的数据存储区,您可以在其中看到新添加的数据存储区。
创建 Google Gmail 数据存储区:
- 点击 + 新建数据存储区。
- 在来源中,搜索 Google Gmail,然后点击选择。
- 在操作部分,输入之前步骤中保存的客户端 ID 和客户端密钥,然后点击验证身份验证。
- 启用发送电子邮件操作。
- 点击继续。
- 在配置部分中,将数据连接器名称设置为
gmail。 - 点击创建。
- 系统会自动将您重定向到已关联的数据存储区,您可以在其中看到新添加的数据存储区。
创建 Google 云端硬盘数据存储区:
- 点击 + 新建数据存储区。
- 在来源中,搜索 Google 云端硬盘,然后点击选择。
- 在数据部分,选择全部,然后点击继续。
- 在配置部分中,将数据连接器名称设置为
drive。 - 点击创建。
- 系统会自动将您重定向到已关联的数据存储区,您可以在其中看到新添加的数据存储区。
创建 NotebookLM 数据存储区:
- 点击 + 新建数据存储区。
- 在来源中,搜索 NotebookLM,然后点击选择。
- 在配置部分中,将数据连接器名称设置为
notebooklm。 - 点击创建。
- 系统会自动将您重定向到已关联的数据存储区,您可以在其中看到新添加的数据存储区。
几分钟后,所有已连接的数据存储区(NotebookLM 除外)的状态都将变为有效。如果您看到任何错误,可以点击相应数据源来查看错误详情。

测试数据存储区
打开我们之前复制的 Gemini Enterprise Web 应用网址:
- 依次点击菜单 ☰ > 新对话。
- 在新聊天消息字段的页脚中,点击连接器图标,然后启用所有连接器。
- 您现在可以尝试使用与连接器相关的提示。例如,在对话中,输入
Do I have any meetings today?并按enter。 - 接下来,尝试输入
How many emails did I receive today?并按enter。 - 最后,输入
Give me the title of the last Drive file I created,然后按enter。

创建自定义代理
在 Gemini Enterprise Web 应用中,使用代理设计工具创建新代理:
- 依次点击“菜单”图标 ☰ >“+ 新建代理”。
- 在聊天中,输入
An agent that always sends pirate-themed emails but use normal English otherwise,然后按enter。

- Agent Designer 会根据提示起草代理,并在编辑器中打开该代理。
- 点击创建
试用自定义代理
- 在 Gemini Enterprise Web 应用中,与新创建的代理对话:
- 依次点击“菜单”图标 ☰ >“代理”。
- 在您的代理下选择相应代理。
- 在新对话消息字段的页脚中,点击连接器图标,然后点击启用操作(针对邮件),并按照说明授权代理
- 在聊天中,输入
Send an email to someone@example.com saying I'll see them at Cloud Next, generate some subject and body yourself,然后按enter。您可以将示例电子邮件地址替换为您的电子邮件地址。 - 点击“✔️”即可发送电子邮件。


4. 专业代码自定义代理
借助此代理,用户可以使用自定义工具和规则,以自然语言搜索数据并针对 Workspace 执行操作。它依赖于以下元素:
- 模型:Gemini。
- 数据和操作:Google Workspace(日历、Gmail、云端硬盘、NotebookLM)的 Gemini Enterprise 数据存储区、Google 搜索、Google 管理的 Vertex AI 搜索模型上下文协议 (MCP) 服务器、用于发送 Google Chat 消息(通过 Google Chat API)的自定义工具函数。
- 智能体构建工具:智能体开发套件 (ADK)。
- 代理主机:Vertex AI Agent Engine。
- 界面:Gemini Enterprise Web 应用。
它将使用 自带功能集成到 Gemini Enterprise 中,因此我们需要完成部署、注册和配置步骤。
查看概念
Vertex AI
Vertex AI 提供构建和使用生成式 AI 所需的一切内容,包括 AI 解决方案、搜索和对话、130 多个基础模型以及统一的 AI 平台。

智能体开发套件 (ADK)
智能体开发套件 (ADK) 是一套专门的工具和框架,旨在通过提供用于推理、内存管理和工具集成的预构建模块,简化自主 AI 智能体的创建过程。
Model Context Protocol (MCP)
Model Context Protocol (MCP) 是一种开放标准,旨在通过通用的“即插即用”接口,在 AI 应用与各种数据源或工具之间实现无缝、安全的集成。
函数工具
函数工具是一种预定义的执行例程,AI 模型可以触发该例程来执行特定操作或从外部系统检索实时数据,从而将其功能扩展到简单的文本生成之外。
查看解决方案架构

查看源代码
agent.py
...
MODEL = "gemini-2.5-flash"
# Gemini Enterprise authentication injects a bearer token into the ToolContext state.
# The key pattern is "GE_AUTH_NAME_<random_digits>".
# We dynamically parse this token to authenticate our MCP and API calls.
GE_AUTH_NAME = "enterprise-ai"
VERTEXAI_SEARCH_TIMEOUT = 15.0
def get_project_id():
"""Fetches the consumer project ID from the environment natively."""
_, project = google.auth.default()
if project:
return project
raise Exception(f"Failed to resolve GCP Project ID from environment.")
def find_serving_config_path():
"""Dynamically finds the default serving config in the engine."""
project_id = get_project_id()
engines = discoveryengine_v1.EngineServiceClient().list_engines(
parent=f"projects/{project_id}/locations/global/collections/default_collection"
)
for engine in engines:
# engine.name natively contains the numeric Project Number
return f"{engine.name}/servingConfigs/default_serving_config"
raise Exception(f"No Discovery Engines found in project {project_id}")
def _get_access_token_from_context(tool_context: ToolContext) -> str:
"""Helper method to dynamically parse the intercepted bearer token from the context state."""
escaped_name = re.escape(GE_AUTH_NAME)
pattern = re.compile(fr"^{escaped_name}_\d+$")
# Handle ADK varying state object types (Raw Dict vs ADK State)
state_dict = tool_context.state.to_dict() if hasattr(tool_context.state, 'to_dict') else tool_context.state
matching_keys = [k for k in state_dict.keys() if pattern.match(k)]
if matching_keys:
return state_dict.get(matching_keys[0])
raise Exception(f"No bearer token found in ToolContext state matching pattern {pattern.pattern}")
def auth_header_provider(tool_context: ToolContext) -> dict[str, str]:
token = _get_access_token_from_context(tool_context)
return {"Authorization": f"Bearer {token}"}
def send_direct_message(email: str, message: str, tool_context: ToolContext) -> dict:
"""Sends a Google Chat Direct Message (DM) to a specific user by email address."""
chat_client = chat_v1.ChatServiceClient(
credentials=Credentials(token=_get_access_token_from_context(tool_context))
)
# 1. Setup the DM space or find existing one
person = chat_v1.User(
name=f"users/{email}",
type_=chat_v1.User.Type.HUMAN
)
membership = chat_v1.Membership(member=person)
space_req = chat_v1.Space(space_type=chat_v1.Space.SpaceType.DIRECT_MESSAGE)
setup_request = chat_v1.SetUpSpaceRequest(
space=space_req,
memberships=[membership]
)
space_response = chat_client.set_up_space(request=setup_request)
space_name = space_response.name
# 2. Send the message
msg = chat_v1.Message(text=message)
message_request = chat_v1.CreateMessageRequest(
parent=space_name,
message=msg
)
message_response = chat_client.create_message(request=message_request)
return {"status": "success", "message_id": message_response.name, "space": space_name}
vertexai_mcp = McpToolset(
connection_params=StreamableHTTPConnectionParams(
url="https://discoveryengine.googleapis.com/mcp",
timeout=VERTEXAI_SEARCH_TIMEOUT,
sse_read_timeout=VERTEXAI_SEARCH_TIMEOUT
),
tool_filter=['search'],
# The auth_header_provider dynamically injects the bearer token from the ToolContext
# into the MCP call for authentication.
header_provider=auth_header_provider
)
# Answer nicely the following user queries:
# - Please find my meetings for today, I need their titles and links
# - What is the latest Drive file I created?
# - What is the latest Gmail message I received?
# - Please send the following message to someone@example.com: Hello, this is a test message.
root_agent = LlmAgent(
model=MODEL,
name='enterprise_ai',
instruction=f"""
You are a helpful assistant that always uses the Vertex AI MCP search tool to answer the user's message, unless the user asks you to send a message to someone.
If the user asks you to send a message to someone, use the send_direct_message tool to send the message.
You MUST unconditionally use the Vertex AI MCP search tool to find answer, even if you believe you already know the answer or believe the Vertex AI MCP search tool does not contain the data.
The Vertex AI MCP search tool accesses the user's data through datastores including Google Drive, Google Calendar, and Gmail.
Only use the Vertex AI MCP search tool with servingConfig and query parameters, do not use any other parameters.
Always use the servingConfig {find_serving_config_path()} while using the Vertex AI MCP search tool.
""",
tools=[vertexai_mcp, FunctionTool(send_direct_message)]
)
启用 API
此解决方案需要启用其他 API:
- 在 Google Cloud 控制台中,启用 Vertex AI、Cloud Resource Manager 和 Google Chat API:

- 依次点击菜单 ☰ > API 和服务 > 已启用的 API 和服务,然后确认列表中包含 Vertex AI API、Cloud Resource Manager API 和 Google Chat API。
更新 OAuth 权限请求页面
此解决方案需要额外的数据访问权限:
- 在 Google Cloud 控制台中,依次点击菜单 ☰ > Google Auth Platform > 数据访问权限。
- 点击添加或移除范围。
- 复制以下授权范围,然后将其粘贴到手动添加授权范围字段中。
- 依次点击添加到表格、更新和保存。
https://www.googleapis.com/auth/cloud-platform
https://www.googleapis.com/auth/chat.messages.create
https://www.googleapis.com/auth/chat.spaces.create
- 依次点击添加到表格、更新和保存。

更新 OAuth 客户端凭据
此解决方案需要额外的已获授权的重定向 URI:
- 在 Google Cloud 控制台中,依次点击菜单 ☰ > Google Auth 平台 > 客户端。
- 点击客户名称
codelab。 - 在已获授权的重定向 URI 部分中,点击添加 URI,然后输入
https://vertexaisearch.cloud.google.com/static/oauth/oauth.html。 - 点击保存。

启用 Vertex AI Search MCP
- 在终端中,执行以下命令:
gcloud beta services mcp enable discoveryengine.googleapis.com \
--project=$(gcloud config get-value project)
配置 Chat 应用
- 在 Google Cloud 控制台中,在 Google Cloud 搜索字段中搜索
Google Chat API,点击 Google Chat API,然后依次点击管理和配置。
- 将应用名称和说明设置为
Gemini Enterprise。 - 将头像网址设置为
https://developers.google.com/workspace/add-ons/images/quickstart-app-avatar.png。 - 取消选中启用互动功能,然后在随即显示的模态对话框中点击停用。
- 选择将错误记录到 Logging。
- 点击保存。

在 Vertex AI Agent Engine 中部署智能体
- 下载此 GitHub 代码库。
- 在终端中,打开
solutions/enterprise-ai-agent目录,然后执行以下命令:
# 1. Create and activate a new virtual environment python3 -m venv .venv source .venv/bin/activate # 2. Install poetry and project dependencies pip install poetry poetry install # 3. Deploy the agent adk deploy agent_engine \ --project=$(gcloud config get-value project) \ --region=us-central1 \ --display_name="Enterprise AI" \ enterprise_ai

- 当您在日志中看到“正在部署到代理引擎...”这一行时,请打开新终端并执行以下命令,以向 Vertex AI Reasoning Engine 服务代理添加所需权限:
# 1. Get the current Project ID
PROJECT_ID=$(gcloud config get-value project)
# 2. Extract the Project Number for that ID
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
# 3. Construct the Service Account name
SERVICE_ACCOUNT="service-${PROJECT_NUMBER}@gcp-sa-aiplatform-re.iam.gserviceaccount.com"
# 4. Apply the IAM policy binding
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT" \
--role="roles/discoveryengine.viewer"
- 等待 adk deploy 命令完成,然后从命令输出中以绿色显示的已部署的新智能体的资源名称。

在 Gemini Enterprise 中注册代理
在 Cloud 控制台中打开 Gemini Enterprise,然后按以下步骤操作:
- 点击名为
codelab的应用。 - 在导航菜单中,点击代理。
- 点击 + 添加代理。
- 对于通过 Agent Engine 构建的自定义代理,点击添加。系统会显示授权部分。
- 点击添加授权。
- 将授权名称设置为
enterprise-ai。系统会根据名称生成 ID,并显示在相应字段下方,请复制该 ID。 - 将客户端 ID 设置为与之前步骤中创建和更新的 OAuth 客户端相同的值。
- 将客户端密钥设置为与之前步骤中创建和更新的 OAuth 客户端相同的值。
- 将令牌 URI 设置为
https://oauth2.googleapis.com/token。 - 将授权 URI 设置为以下值,并将 <CLIENT_ID> 替换为在上一步中创建和更新的 OAuth 客户端 ID。
https://accounts.google.com/o/oauth2/v2/auth?client_id=<CLIENT_ID>&redirect_uri=https%3A%2F%2Fvertexaisearch.cloud.google.com%2Fstatic%2Foauth%2Foauth.html&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar.calendars%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar.events%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.send%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fchat.messages.create%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fchat.spaces.create&include_granted_scopes=true&response_type=code&access_type=offline&prompt=consent
- 点击完成,然后点击下一步。系统会显示配置部分。
- 将代理名称和代理说明设置为
Enterprise AI。 - 将代理引擎推理引擎设置为在之前的步骤中复制的推理引擎资源名称。其格式如下:
projects/<PROJECT_ID>/locations/<LOCATION>/reasoningEngines/<REASONING_ENGINE_ID>
- 点击创建。新添加的代理现已列在代理下。
试用代理
- 在 Gemini Enterprise Web 应用中,与新注册的代理对话:
- 依次点击“菜单”图标 ☰ >“代理”。
- 选择来自您的组织下方的代理。
- 在聊天中,输入
Please find my meetings for today, I need their titles and links,然后按enter。 - 点击授权,然后按照授权流程操作。

- 智能体回答时会列出日历活动(具体取决于用户的账号)。
- 在聊天中,输入
Please send a Chat message to someone@example.com with the following text: Hello!,然后按enter。 - 代理会以确认消息做出回答。


5. 作为 Google Workspace 插件的默认代理
借助此代理,用户可以在 Workspace 应用界面中以自然语言搜索 Workspace 数据。它依赖于以下元素:
- 模型:Gemini。
- 数据:Google Workspace(日历、Gmail、云端硬盘、NotebookLM)的 Gemini Enterprise 数据存储区、Google 搜索。
- 代理宿主:Gemini Enterprise。
- 界面:适用于 Chat 和 Gmail 的 Google Workspace 加购项(可轻松扩展到 Google 日历、云端硬盘、文档、表格和幻灯片)。
- Google Workspace 加购项:Apps 脚本、Gemini Enterprise 和 Vertex AI API、上下文(用户元数据、所选 Gmail 邮件)。
Google Workspace 加载项将使用 StreamAssist API 连接到 Gemini Enterprise。
查看概念
Google Workspace 加购项
Google Workspace 加购项是一种自定义应用,可扩展一个或多个 Google Workspace 应用(Gmail、Chat、Google 日历、Google 文档、云端硬盘、Meet、Google 表格和 Google 幻灯片)。
Apps 脚本
Apps 脚本是一个基于云的 JavaScript 平台,由 Google 云端硬盘提供支持,可让您与 Google 各项产品集成并自动执行任务。
Google Workspace 卡片框架
Google Workspace 中的 Card 框架可让开发者创建丰富多样的互动式界面。借助它,您可以构建包含文本、图片、按钮和其他 widget 的有条理且美观的卡片。这些卡片可提供结构化信息,并直接在 Workspace 应用中启用快捷操作,从而提升用户体验。
查看解决方案架构

查看源代码
appsscript.json
...
"addOns": {
"common": {
"name": "Enterprise AI",
"logoUrl": "https://developers.google.com/workspace/add-ons/images/quickstart-app-avatar.png"
},
"chat": {},
"gmail": {
"contextualTriggers": [
{
"unconditional": {},
"onTriggerFunction": "onAddonEvent"
}
]
}
},
"oauthScopes": [
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/discoveryengine.assist.readwrite",
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/gmail.addons.current.message.readonly"
]
...
Chat.gs
...
// Service that handles Google Chat operations.
// Handle incoming Google Chat message events, actions will be taken via Google Chat API calls
function onMessage(event) {
if (isInDebugMode()) {
console.log(`MESSAGE event received (Chat): ${JSON.stringify(event)}`);
}
// Extract data from the event.
const chatEvent = event.chat;
setChatConfig(chatEvent.messagePayload.space.name);
// Request AI agent to answer the message
requestAgent(chatEvent.messagePayload.message);
// Respond with an empty response to the Google Chat platform to acknowledge execution
return null;
}
// --- Utility functions ---
// The Chat direct message (DM) space associated with the user
const SPACE_NAME_PROPERTY = "DM_SPACE_NAME"
// Sets the Chat DM space name for subsequent operations.
function setChatConfig(spaceName) {
const userProperties = PropertiesService.getUserProperties();
userProperties.setProperty(SPACE_NAME_PROPERTY, spaceName);
console.log(`Space is set to ${spaceName}`);
}
// Retrieved the Chat DM space name to sent messages to.
function getConfiguredChat() {
const userProperties = PropertiesService.getUserProperties();
return userProperties.getProperty(SPACE_NAME_PROPERTY);
}
// Finds the Chat DM space name between the Chat app and the given user.
function findChatAppDm(userName) {
return Chat.Spaces.findDirectMessage(
{ 'name': userName },
{'Authorization': `Bearer ${getAddonCredentials().getAccessToken()}`}
).name;
}
// Creates a Chat message in the configured space.
function createMessage(message) {
const spaceName = getConfiguredChat();
console.log(`Creating message in space ${spaceName}...`);
return Chat.Spaces.Messages.create(
message,
spaceName,
{},
{'Authorization': `Bearer ${getAddonCredentials().getAccessToken()}`}
).name;
}
Sidebar.gs
...
// Service that handles Gmail operations.
// Triggered when the user opens the Gmail Add-on or selects an email.
function onAddonEvent(event) {
// If this was triggered by a button click, handle it
if (event.parameters && event.parameters.action === 'send') {
return handleSendMessage(event);
}
// Otherwise, just render the default initial sidebar
return createSidebarCard();
}
// Creates the standard Gmail sidebar card consisting of a text input and send button.
// Optionally includes an answer section if a response was generated.
function createSidebarCard(optionalAnswerSection) {
const card = CardService.newCardBuilder();
const actionSection = CardService.newCardSection();
// Create text input for the user's message
const messageInput = CardService.newTextInput()
.setFieldName("message")
.setTitle("Message")
.setMultiline(true);
// Create action for sending the message
const sendAction = CardService.newAction()
.setFunctionName('onAddonEvent')
.setParameters({ 'action': 'send' });
const sendButton = CardService.newTextButton()
.setText("Send message")
.setTextButtonStyle(CardService.TextButtonStyle.FILLED)
.setOnClickAction(sendAction);
actionSection.addWidget(messageInput);
actionSection.addWidget(CardService.newButtonSet().addButton(sendButton));
card.addSection(actionSection);
// Attach the response at the bottom if we have one
if (optionalAnswerSection) {
card.addSection(optionalAnswerSection);
}
return card.build();
}
// Handles clicks from the Send message button.
function handleSendMessage(event) {
const commonEventObject = event.commonEventObject || {};
const formInputs = commonEventObject.formInputs || {};
const messageInput = formInputs.message;
let userMessage = "";
if (messageInput && messageInput.stringInputs && messageInput.stringInputs.value.length > 0) {
userMessage = messageInput.stringInputs.value[0];
}
if (!userMessage || userMessage.trim().length === 0) {
return CardService.newActionResponseBuilder()
.setNotification(CardService.newNotification().setText("Please enter a message."))
.build();
}
let finalQueryText = `USER MESSAGE TO ANSWER: ${userMessage}`;
// If we have an email selected in Gmail, append its content as context
if (event.gmail && event.gmail.messageId) {
try {
GmailApp.setCurrentMessageAccessToken(event.gmail.accessToken);
const message = GmailApp.getMessageById(event.gmail.messageId);
const subject = message.getSubject();
const bodyText = message.getPlainBody() || message.getBody();
finalQueryText += `\n\nEMAIL THE USER HAS OPENED ON SCREEN:\nSubject: ${subject}\nBody:\n---\n${bodyText}\n---`;
} catch (e) {
console.error("Could not fetch Gmail context: " + e);
// Invalidate the token explicitly so the next prompt requests the missing scopes
ScriptApp.invalidateAuth();
CardService.newAuthorizationException()
.setResourceDisplayName("Enterprise AI")
.setAuthorizationUrl(ScriptApp.getAuthorizationUrl())
.throwException();
}
}
try {
const responseText = queryAgent({ text: finalQueryText, forceNewSession: true });
// We leverage the 'showdown' library to parse the LLM's Markdown output into HTML
// We also substitute markdown listings with arrows and adjust newlines for clearer rendering in the sidebar
let displayedText = substituteListingsFromMarkdown(responseText);
displayedText = new showdown.Converter().makeHtml(displayedText).replace(/\n/g, '\n\n');
const textParagraph = CardService.newTextParagraph();
textParagraph.setText(displayedText);
const answerSection = CardService.newCardSection()
.addWidget(textParagraph);
const updatedCard = createSidebarCard(answerSection);
return CardService.newActionResponseBuilder()
.setNavigation(CardService.newNavigation().updateCard(updatedCard))
.build();
} catch (err) {
return CardService.newActionResponseBuilder()
.setNotification(CardService.newNotification().setText("Error fetching response: " + err.message))
.build();
}
}
...
AgentHandler.gs
...
// Service that handles Gemini Enterprise AI Agent operations.
// Submits a query to the AI agent and returns the response string synchronously
function queryAgent(input) {
const isNewSession = input.forceNewSession || !PropertiesService.getUserProperties().getProperty(AGENT_SESSION_NAME);
const sessionName = input.forceNewSession ? createAgentSession() : getOrCreateAgentSession();
let systemPrompt = "SYSTEM PROMPT START Do not respond with tables but use bullet points instead.";
if (input.forceNewSession) {
systemPrompt += " Do not ask the user follow-up questions or converse with them as history is not kept in this interface.";
}
systemPrompt += " SYSTEM PROMPT END\n\n";
const queryText = isNewSession ? systemPrompt + input.text : input.text;
const requestPayload = {
"session": sessionName,
"userMetadata": { "timeZone": Session.getScriptTimeZone() },
"query": { "text": queryText },
"toolsSpec": { "vertexAiSearchSpec": { "dataStoreSpecs": getAgentDataStores().map(ds => { dataStore: ds }) } },
"agentsSpec": { "agentSpecs": [{ "agentId": getAgentId() }] }
};
const responseContentText = UrlFetchApp.fetch(
`https://${getLocation()}-discoveryengine.googleapis.com/v1alpha/${getReasoningEngine()}/assistants/default_assistant:streamAssist?alt=sse`,
{
method: 'post',
headers: { 'Authorization': `Bearer ${ScriptApp.getOAuthToken()}` },
contentType: 'application/json',
payload: JSON.stringify(requestPayload),
muteHttpExceptions: true
}
).getContentText();
if (isInDebugMode()) {
console.log(`Response: ${responseContentText}`);
}
const events = responseContentText.split('\n').map(s => s.replace(/^data:\s*/, '')).filter(s => s.trim().length > 0);
console.log(`Received ${events.length} agent events.`);
let answerText = "";
for (const eventJson of events) {
if (isInDebugMode()) {
console.log("Event: " + eventJson);
}
const event = JSON.parse(eventJson);
// Ignore internal events
if (!event.answer) {
console.log(`Ignored: internal event`);
continue;
}
// Handle text replies
const replies = event.answer.replies || [];
for (const reply of replies) {
const content = reply.groundedContent.content;
if (content) {
if (isInDebugMode()) {
console.log(`Processing content: ${JSON.stringify(content)}`);
}
if (content.thought) {
console.log(`Ignored: thought event`);
continue;
}
answerText += content.text;
}
}
if (event.answer.state === "SUCCEEDED") {
console.log(`Answer text: ${answerText}`);
return answerText;
} else if (event.answer.state !== "IN_PROGRESS") {
throw new Error("Something went wrong, check the Apps Script logs for more info.");
}
}
return answerText;
}
// Gets the list of data stores configured for the agent to include in the request.
function getAgentDataStores() {
const responseContentText = UrlFetchApp.fetch(
`https://${getLocation()}-discoveryengine.googleapis.com/v1/${getReasoningEngine().split('/').slice(0, 6).join('/')}/dataStores`,
{
method: 'get',
// Use the add on service account credentials for data store listing access
headers: { 'Authorization': `Bearer ${getAddonCredentials().getAccessToken()}` },
contentType: 'application/json',
muteHttpExceptions: true
}
).getContentText();
if (isInDebugMode()) {
console.log(`Response: ${responseContentText}`);
}
const dataStores = JSON.parse(responseContentText).dataStores.map(ds => ds.name);
if (isInDebugMode()) {
console.log(`Data stores: ${dataStores}`);
}
return dataStores;
}
...
启动服务账号
在 Google Cloud 控制台中,按以下步骤操作:
- 依次点击菜单 ☰ > IAM 和管理 > 服务账号 > + 创建服务账号。
- 将服务账号名称设置为
ge-add-on。

- 点击创建并继续。
- 在权限中添加 Discovery Engine 查看者角色。

- 依次点击继续和完成。您将会转到服务账号页面,并看到创建的服务账号。

- 选择新创建的服务账号,然后选择密钥标签页。
- 依次点击添加密钥和创建新密钥。
- 选择 JSON,然后点击创建。

- 对话框随即关闭,新创建的公钥/私钥对将以 JSON 文件的形式自动下载到本地环境。
创建和配置 Apps 脚本项目
- 点击以下按钮,打开 Enterprise AI 加购项 Apps 脚本项目:
- 依次点击概览 > 复制。
- 在您的 Apps 脚本项目中,依次点击项目设置 > 修改脚本属性 > 添加脚本属性,以添加脚本属性。
- 将 REASONING_ENGINE_RESOURCE_NAME 设置为 Gemini Enterprise 应用资源名称。其格式如下:
# 1. Replace PROJECT_ID with the Google Cloud project ID. # 2. Replace GE_APP_ID with the codelab app ID found in Google Cloud console > Gemini Enterprise > Apps. projects/<PROJECT_ID>/locations/global/collections/default_collection/engines/<GE_APP_ID>
- 将 APP_SERVICE_ACCOUNT_KEY 设置为在之前的步骤中下载的服务账号文件中的 JSON 密钥。
- 点击保存脚本属性
部署到 Gmail 和 Chat
在您的 Apps 脚本项目中,按以下步骤操作:
- 依次点击部署 > 测试部署,然后点击安装。现在,您可以在 Gmail 中使用此功能。
- 点击主要部署 ID 下方的复制。

在 Google Cloud 控制台中,按以下步骤操作:
- 在 Google Cloud 搜索字段中搜索
Google Chat API,点击 Google Chat API,然后依次点击管理和配置。
- 选择启用互动功能。
- 取消选择加入聊天室和群组对话。
- 在连接设置下,选择 Apps 脚本。
- 将部署 ID 设置为在上一步中复制的头部部署 ID。
- 在公开范围下,选择面向您 Workspace 网域中的特定人员和群组提供此 Chat 扩展应用,然后输入您的电子邮件地址。
- 点击保存。

试用加购项
在新标签页中打开 Google Chat,然后按以下步骤操作:
- 打开与 Chat 应用 Gemini Enterprise 的私信对话。

- 点击配置,然后完成身份验证流程。
- 输入
What are my meetings for today?,然后按enter。Gemini Enterprise 聊天应用应回复结果。

在新标签页中打开 Gmail,然后按以下步骤操作:
- 给自己发送一封电子邮件,其中主题设置为
We need to talk,正文设置为Are you available today between 8 and 9 AM? - 打开新收到的电子邮件。
- 打开企业版 AI 加购项边栏。
- 将消息设置为
Am I? - 点击发送讯息。
- 答案会显示在按钮下方。

6. 清理
删除 Google Cloud 项目
为避免系统因此 Codelab 中使用的资源向您的 Google Cloud 账号收取费用,我们建议您删除该 Google Cloud 项目。
在 Google Cloud 控制台中,按以下步骤操作:
- 依次点击菜单 ☰ > IAM 和管理 > 设置。
- 点击关停。
- 输入项目 ID。
- 点击仍要关停。

7. 恭喜
恭喜!您构建的解决方案可让 Gemini Enterprise 和 Google Workspace 更紧密地结合在一起,从而为员工提供强大助力!
后续操作
在此 Codelab 中,我们仅展示了最典型的用例,但您或许需要在解决方案中考虑很多值得扩展的地方,例如:
- 使用 Gemini CLI 和 Antigravity 等 AI 赋能的开发者工具。
- 与其他代理框架和工具集成,例如自定义 MCP、自定义函数调用和生成式界面。
- 与在 Vertex AI 等专用平台上托管的其他 AI 模型(包括自定义模型)集成。
- 与托管在专用平台(例如 Dialogflow)中或由第三方通过 Cloud Marketplace 托管的其他代理集成。
- 在 Cloud Marketplace 上发布代理,赋能团队、组织或公共用户。
了解详情
我们为开发者提供了大量资源,例如 YouTube 视频、文档网站、代码示例和教程:
- Google Cloud 开发者中心
- 支持的产品 | Google Cloud MCP 服务器
- A2UI
- Vertex AI 上的 Model Garden | Google Cloud
- 智能体概览 | Gemini Enterprise | Google Cloud 文档
- 通过 Google Cloud Marketplace 和 Gemini Enterprise 扩展 AI 智能体
- 通过 Google Cloud Marketplace 提供 AI 代理
- Google Workspace 开发者 YouTube 频道 - 欢迎开发者!
- Google Workspace 开发者网站
- 适用于所有 Google Workspace 加载项示例的 GitHub 代码库

