Gemini Enterprise エージェントと Google Workspace を統合する

1. 始める前に

83e1c1629d14fb31.png

Gemini Enterprise とは何ですか?

Gemini Enterprise は、あらゆる従業員がすべてのワークフローで最先端の Google AI を利用できる高度なエージェント プラットフォームです。これにより、チームは 1 つの安全な環境で AI エージェントを発見、作成、共有、実行できるようになります。

  • 高度なモデルにアクセスする: ユーザーは、Gemini などの Google の最も強力なマルチモーダル AI にすぐにアクセスして、複雑なビジネス上の課題に取り組むことができます。
  • 専門のエージェントを活用する: このスイートには、調査、コーディング、メモ取り用の Google エージェントがすぐに使用できる状態で含まれており、すぐに価値を提供できます。
  • すべての従業員が活用できる: ノーコードとプロコードのオプションにより、すべての部門のスタッフがワークフローの自動化のための独自のカスタム エージェントを構築して管理できます。
  • エージェントをデータにグラウンディングする: エージェントを社内データやサードパーティ アプリケーションに安全に接続し、コンテキストに沿った正確な回答を生成できます。
  • 一元化されたガバナンス: 管理者は、すべてのエージェント アクティビティを可視化して監査し、組織が厳格なセキュリティとコンプライアンスの基準を満たしていることを確認できます。
  • エコシステムで拡張: このプラットフォームは、パートナー アプリケーションとサービス プロバイダの広範なネットワークと統合され、さまざまなシステムにわたって自動化を拡張します。

127f2ed7d484722c.png

Google Workspace とは

Google Workspace は、個人、学校、企業向けに設計されたクラウドベースの生産性とコラボレーションのソリューションです。

  • コミュニケーション: ビジネス用メールサービス(Gmail)、ビデオ会議(Meet)、チーム メッセージング(Chat)。
  • コンテンツ作成: ドキュメントの作成(ドキュメント)、スプレッドシートの作成(スプレッドシート)、プレゼンテーションの作成(スライド)を行うためのツール。
  • 整理: 共有カレンダー(カレンダー)とデジタル メモ(Keep)。
  • ストレージ: ファイルを安全に保存、共有できる一元化されたクラウド スペース(ドライブ)。
  • 管理: ユーザーとセキュリティ設定を管理するための管理ツール(Workspace 管理コンソール)。

どのようなカスタム統合ですか?

Google Workspace と Gemini Enterprise は、強力なフィードバック ループを形成します。Workspace はリアルタイムのデータとコラボレーション コンテキストを提供し、Gemini Enterprise はインテリジェントなワークフローの自動化に必要なモデル、エージェントによる推論、オーケストレーションを提供します。

  • スマートな接続性: Google が管理するデータストア、API、MCP サーバー(Google が管理するものとカスタムのもの)により、エージェントは Workspace データに安全かつシームレスにアクセスし、ユーザーに代わってアクションを実行できます。
  • カスタム エージェント: ノーコード デザイナーまたはプロコード フレームワークを使用して、管理者が管理する Workspace のデータとアクションに基づいて、特殊なエージェントを構築できます。
  • ネイティブ統合: Workspace アドオンは、専用の UI コンポーネントまたはバックグラウンド プロセスを介して、AI システムと Chat や Gmail などのアプリケーション間のギャップを埋めます。これにより、エージェントはユーザーの状況を正確に把握し、即座にコンテキストに応じたサポートを提供できます。

Google Workspace の堅牢な生産性エコシステムと Gemini Enterprise の高度なエージェント機能を組み合わせることで、組織は、チームが日常的に使用しているツール内で複雑なワークフローを自動化する、データに基づいたカスタム AI エージェントを通じて、業務を変革できます。

前提条件

ご自身の環境で手順をすべて行うには、次のものが必要です。

作成するアプリの概要

この Codelab では、Google Workspace と緊密に統合された Gemini Enterprise AI エージェントを使用して 3 つのソリューションを構築します。データ、アクション、UI とのやり取りに使用できるアーキテクチャ パターンについて説明します。

ノーコード カスタム エージェント

このエージェントを使用すると、ユーザーは自然言語でデータを検索し、Workspace のアクションを実行できます。次の要素に依存します。

  • モデル: Gemini。
  • データとアクション: Google Workspace(カレンダー、Gmail、ドライブ、NotebookLM)用の Gemini Enterprise データストア、Google 検索
  • エージェント構築ツール: Gemini Enterprise Agent Designer。
  • エージェント ホスト: Gemini Enterprise。
  • UI: Gemini Enterprise ウェブアプリ。

90e42539e5959634.png

60e62437ce29a818.png

プロコード カスタム エージェント

このエージェントを使用すると、ユーザーはカスタムツールとルールを使用して、自然言語でデータを検索し、Workspace の操作を行うことができます。次の要素に依存します。

  • モデル: Gemini。
  • データとアクション: Google Workspace(カレンダー、Gmail、ドライブ、NotebookLM)用の Gemini Enterprise データストア、Google 検索、Google 管理の Vertex AI Search Model Context Protocol(MCP)サーバー、Google Chat メッセージを送信するカスタム ツール関数(Google Chat API 経由)。
  • エージェント構築ツール: Agent Development Kit(ADK)。
  • エージェント ホスト: Vertex AI Agent Engine。
  • UI: Gemini Enterprise ウェブアプリ。

1647ebff031c42e7.png

a8087d2351e77fb4.png

デフォルト エージェントとしての Google Workspace アドオン

このエージェントを使用すると、ユーザーは Workspace アプリの UI のコンテキスト内で、自然言語で Workspace のデータを検索できます。次の要素に依存します。

  • モデル: Gemini。
  • データ: Google Workspace(カレンダー、Gmail、ドライブ、NotebookLM)用の Gemini Enterprise データストア、Google 検索
  • エージェント ホスト: Gemini Enterprise。
  • UI: Chat と Gmail 用の Google Workspace アドオン(カレンダー、ドライブ、ドキュメント、スプレッドシート、スライドにも簡単に拡張可能)。
  • Google Workspace アドオン: Apps Script、Gemini Enterprise と Vertex AI の API、コンテキスト(ユーザー メタデータ、選択した Gmail メッセージ)。

c8c63fb3f324fecf.png

d33b8cb50ee251b7.png

学習内容

  • データとアクションを可能にする Gemini Enterprise と Google Workspace 間の統合ポイント。
  • Gemini Enterprise でホストされるカスタム エージェントを構築するためのノーコード オプションとプロコード オプション。
  • ユーザーが Gemini Enterprise ウェブアプリと Google Workspace アプリケーションからエージェントにアクセスする方法。

2. セットアップする

コンセプトを確認する

Gemini Enterprise アプリ

Gemini Enterprise アプリは、検索結果、アクション、エージェントをエンドユーザーに提供します。API のコンテキストでは、「アプリ」という用語と「エンジン」という用語を同じ意味で使用できます。アプリは、データストアのデータを使用して検索結果、回答、アクションを提示するために、データストアに接続されている必要があります。

Gemini Enterprise ウェブアプリ

Gemini Enterprise ウェブアプリは Gemini Enterprise アプリに関連付けられています。これは、従業員が単一のチャット インターフェースを使用して、サイロ化された企業データを検索し、複雑なワークフロー用の専門的な AI エージェントを実行し、エンタープライズ レベルのプライバシーでプロフェッショナル グレードのコンテンツを生成する、一元化された AI ホームベースとして機能します。

リソースを初期化してアクセスする

このセクションでは、お使いのウェブブラウザから次のリソースにアクセスして構成します。

Gemini Enterprise アプリ

新しいタブで Google Cloud コンソールを開き、次の操作を行います。

  1. プロジェクトを選択します。
  2. Google Cloud の検索フィールドで Gemini Enterprise を検索して選択し、[+ アプリを作成] をクリックします。Gemini Enterprise のライセンスがない場合は、30 日間の無料トライアル ライセンスを有効にするよう求められます。

  1. [アプリ名] を codelab に設定します。
  2. ID は名前に基づいて生成され、フィールドの下に表示されます。コピーします。
  3. [マルチリージョン] を global (Global) に設定します。
  4. [作成] をクリックします。

8712ada39377205e.png

  1. アプリが作成され、[Gemini Enterprise] > [概要] に自動的にリダイレクトされます。
  2. [フルアクセス権を取得] で [ID を設定] をクリックします。
  3. 新しい画面で、[Google Identity を使用する] を選択し、[Workforce Identity を確認する] をクリックします。

3209c156eff4ba43.png

  1. 構成が保存され、[Gemini Enterprise] > [概要] に自動的にリダイレクトされます。
  2. [構成] に移動します。
  3. [Feature Management] タブで、[Enable agent designer] をオンにして、[Save] をクリックします。

f0cd9da419b41cb6.png

Gemini Enterprise ウェブアプリ

Cloud コンソールから新しいタブで Gemini Enterprise を開き、次の操作を行います。

  1. codelab という名前のアプリをクリックします。
  2. 表示された URL をコピーします。この URL は、次の手順で Gemini Enterprise ウェブアプリに移動するために使用します。

b46ee6176744565d.png

3. ノーコード カスタム エージェント

このエージェントを使用すると、ユーザーは自然言語でデータを検索し、Workspace のアクションを実行できます。次の要素に依存します。

  • モデル: Gemini。
  • データとアクション: Google Workspace(カレンダー、Gmail、ドライブ、NotebookLM)用の Gemini Enterprise データストア、Google 検索
  • エージェント構築ツール: Gemini Enterprise Agent Designer。
  • エージェント ホスト: Gemini Enterprise。
  • UI: Gemini Enterprise ウェブアプリ。

コンセプトを確認する

Gemini

Gemini は、Google のマルチモーダル LLM です。人々が、人間の可能性を解き放ち、想像力を高め、好奇心を刺激し、生産性を高めるお手伝いをします。

Gemini Enterprise データストア

Gemini Enterprise データストアは、Google Workspace などのファーストパーティ データソースや、Jira や Salesforce などのサードパーティ アプリケーションから取り込まれたデータを含むエンティティです。サードパーティ アプリケーションのデータを含むデータストアは、データコネクタとも呼ばれます。

Gemini Enterprise エージェント デザイナー

Gemini Enterprise Agent Designer は、Gemini Enterprise でシングルステップとマルチステップのエージェントを作成、管理、起動するためのインタラクティブなノーコード / ローコード プラットフォームです。

ソリューション アーキテクチャを確認する

e77aafb772502aaf.png

API を有効にする

Gemini Enterprise Workspace データストアでは、次の API を有効にする必要があります。

  1. Google Cloud コンソールで、カレンダー、Gmail、People API を有効にします。

573322606b715a69.png

  1. メニュー アイコン ☰ > [API とサービス] > [有効な API とサービス] をクリックし、Google Calendar APIGmail APIPeople API がリストに含まれていることを確認します。

Gemini Enterprise Workspace カレンダーと Gmail のアクションには、同意画面の構成が必要です。

  1. Google Cloud コンソールで、メニュー アイコン ☰ > Google 認証プラットフォーム > ブランディング をクリックします。

  1. [開始] をクリックします。
  2. [アプリ情報] で、[アプリ名] を Codelab に設定します。
  3. [ユーザー サポートメール] で、ユーザーが同意について問い合わせる際に使用するサポートのメールアドレスを選択します。
  4. [次へ] をクリックします。
  5. [対象] で [内部] を選択します。
  6. [次へ] をクリックします。
  7. [連絡先情報] で、プロジェクトに対する変更の通知を受け取るメールアドレスを入力します。
  8. [次へ] をクリックします。
  9. [完了] で、Google API サービスのユーザーデータに関するポリシーを確認し、同意する場合は [Google API サービス: ユーザーデータに関するポリシーに同意します] を選択します。
  10. [続行]、[作成] の順にクリックします。

578c2b38219b2f7b.png

  1. 構成が保存され、[Google Auth Platform] > [概要] に自動的にリダイレクトされます。
  2. [データアクセス] に移動します。
  3. [スコープを追加または削除] をクリックします。
  4. 次のスコープをコピーして、[Manually add scopes] フィールドに貼り付けます。
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
  1. [テーブルに追加]、[更新]、[保存] の順にクリックします。

874b1dda14e8f379.png

詳細については、OAuth 同意画面を構成するをご覧ください。

OAuth クライアント認証情報を作成する

Gemini Enterprise 用の新しい OAuth クライアントを作成して、ユーザーを認証します。

  1. Google Cloud コンソールで、メニュー アイコン ☰ > Google Auth Platform > クライアントをクリックします。

  1. [+ クライアントを作成] をクリックします。
  2. [アプリケーションの種類] で [ウェブ アプリケーション] を選択します。
  3. [名前] を codelab に設定します。
  4. [承認済みの JavaScript 生成元] はスキップします。
  5. [承認済みのリダイレクト URI] セクションで、[URI を追加] をクリックして https://vertexaisearch.cloud.google.com/oauth-redirect と入力します。
  6. [作成] をクリックします。
  7. 新しく作成された OAuth クライアント ID とシークレットがダイアログに表示されます。この情報は安全な場所に保管してください。

a46e5ebfb851aea5.png

データストアを作成する

Cloud コンソールから新しいタブで Gemini Enterprise を開き、次の操作を行います。

  1. codelab という名前のアプリをクリックします。
  2. ナビゲーション メニューで [接続されたデータストア] をクリックします。
  3. [+ 新しいデータストア] をクリックします。
  4. [ソース] で「Google カレンダー」を検索し、[選択] をクリックします。
  5. [アクション] セクションで、前の手順で保存したクライアント IDクライアント シークレットを入力し、[認証を確認する] をクリックして、OAuth クライアントを認証して承認する手順に沿って操作します。
  6. [カレンダーの予定を作成] と [カレンダーの予定を更新] のアクションを有効にします。
  7. [続行] をクリックします。

a1d76e70edec0cf.png

  1. [構成] セクションで、[データコネクタ名] を calendar に設定します。
  2. [作成] をクリックします。
  3. [接続されたデータストア] に自動的にリダイレクトされ、新しく追加されたデータストアが表示されます。

Google Gmail データストアを作成します。

  1. [+ 新しいデータストア] をクリックします。
  2. [ソース] で [Google Gmail] を検索し、[選択] をクリックします。
  3. [アクション] セクションで、前の手順で保存したクライアント IDクライアント シークレットを入力し、[認証を確認する] をクリックします。
  4. アクション [メールを送信] を有効にします。
  5. [続行] をクリックします。
  6. [構成] セクションで、[データコネクタ名] を gmail に設定します。
  7. [作成] をクリックします。
  8. [接続されたデータストア] に自動的にリダイレクトされ、新しく追加されたデータストアが表示されます。

Google ドライブのデータストアを作成します。

  1. [+ 新しいデータストア] をクリックします。
  2. [ソース] で「Google ドライブ」を検索し、[選択] をクリックします。
  3. [データ] セクションで [すべて] を選択し、[続行] をクリックします。
  4. [構成] セクションで、[データコネクタ名] を drive に設定します。
  5. [作成] をクリックします。
  6. [接続されたデータストア] に自動的にリダイレクトされ、新しく追加されたデータストアが表示されます。

NotebookLM データストアを作成します。

  1. [+ 新しいデータストア] をクリックします。
  2. [ソース] で「NotebookLM」を検索し、[選択] をクリックします。
  3. [構成] セクションで、[データコネクタ名] を notebooklm に設定します。
  4. [作成] をクリックします。
  5. [接続されたデータストア] に自動的にリダイレクトされ、新しく追加されたデータストアが表示されます。

数分後、接続されているすべてのデータストア(NotebookLM を除く)のステータスが [アクティブ] になります。エラーが表示された場合は、データソースをクリックしてエラーの詳細を表示できます。

ceba9eb2480a2696.png

テストデータストア

先ほどコピーした Gemini Enterprise ウェブアプリの URL を開きます。

  1. メニュー アイコン ☰ > [チャットを新規作成] をクリックします。
  2. 新しいチャット メッセージ フィールドのフッターで、[コネクタ] アイコンをクリックし、すべてのコネクタを有効にします。
  3. コネクタに関連するプロンプトを試せるようになりました。たとえば、チャットで「Do I have any meetings today?」と入力して enter キーを押します。
  4. 次に、「How many emails did I receive today?」と入力して enter キーを押します。
  5. 最後に、「Give me the title of the last Drive file I created」と入力して enter キーを押します。

90e42539e5959634.png

カスタム エージェントを作成する

Gemini Enterprise ウェブアプリで、Agent Designer を使用して新しいエージェントを作成します。

  1. メニュー アイコン ☰ > [+ 新しいエージェント] をクリックします。
  2. チャットで「An agent that always sends pirate-themed emails but use normal English otherwise」と入力し、enter キーを押します。

2803c1dedd20433e.png

  1. Agent Designer は、プロンプトに基づいてエージェントをドラフトし、エディタで開きます。
  2. [作成] をクリックします。

カスタム エージェントを試す

  1. Gemini Enterprise ウェブアプリで、新しく作成したエージェントとチャットします。
  2. メニュー アイコン ☰ > [エージェント] をクリックします。
  3. [エージェント] でエージェントを選択します。
  4. 新しいチャット メッセージ フィールドのフッターで、[コネクタ] アイコンをクリックし、[メール] の [アクションを有効にする] をクリックして、手順に沿ってエージェントを承認します。
  5. チャットで「Send an email to someone@example.com saying I'll see them at Cloud Next, generate some subject and body yourself」と入力し、enter キーを押します。サンプル メールは、ご自身のメールアドレスに置き換えることができます。
  6. ✔️ をクリックしてメールを送信します。

60e62437ce29a818.png

d4fb65d14fdf27da.png

4. プロコード カスタム エージェント

このエージェントを使用すると、ユーザーはカスタムツールとルールを使用して、自然言語でデータを検索し、Workspace の操作を行うことができます。次の要素に依存します。

  • モデル: Gemini。
  • データとアクション: Google Workspace(カレンダー、Gmail、ドライブ、NotebookLM)用の Gemini Enterprise データストア、Google 検索、Google 管理の Vertex AI Search Model Context Protocol(MCP)サーバー、Google Chat メッセージを送信するカスタム ツール関数(Google Chat API 経由)。
  • エージェント構築ツール: Agent Development Kit(ADK)。
  • エージェント ホスト: Vertex AI Agent Engine。
  • UI: Gemini Enterprise ウェブアプリ。

これは Bring Your Own 機能を使用して Gemini Enterprise に統合されるため、デプロイ、登録、構成の手順を行う必要があります。

コンセプトを確認する

Vertex AI

Vertex AI は、AI ソリューション、検索と会話、130 を超える基盤モデル、統合 AI プラットフォームなど、生成 AI を構築して使用するために必要なすべてを提供します。

4670fcf7a826af4d.png

Agent Development Kit(ADK)

Agent Development Kit(ADK)は、推論、メモリ管理、ツール統合用の事前構築済みモジュールを提供することで、自律型 AI エージェントの作成を簡素化するように設計された、ツールとフレームワークの専用スイートです。

Model Context Protocol(MCP)

Model Context Protocol(MCP)は、ユニバーサルな「プラグ アンド プレイ」インターフェースを通じて、AI アプリケーションとさまざまなデータソースやツール間のシームレスで安全な統合を実現するように設計されたオープン スタンダードです。

関数ツール

関数ツールは、AI モデルがトリガーして特定のアクションを実行したり、外部システムからリアルタイム データを取得したりできる、事前定義された実行可能なルーティンです。これにより、単純なテキスト生成を超えて機能を拡張できます。

ソリューション アーキテクチャを確認する

43df337e0f3d64e8.png

ソースコードを確認する

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 を有効にする必要があります。

  1. Google Cloud コンソールで、Vertex AI、Cloud Resource Manager、Google Chat の各 API を有効にします。

4f02a36b050bab00.png

  1. メニュー アイコン ☰ > [API とサービス] > [有効な API とサービス] をクリックし、Vertex AI APICloud Resource Manager APIGoogle Chat API がリストに含まれていることを確認します。

このソリューションには、追加のデータアクセスが必要です。

  1. Google Cloud コンソールで、メニュー ☰ > Google Auth プラットフォーム > データアクセスをクリックします。

  1. [スコープを追加または削除] をクリックします。
  2. 次のスコープをコピーして、[Manually add scopes] フィールドに貼り付けます。
  3. [テーブルに追加]、[更新]、[保存] の順にクリックします。
https://www.googleapis.com/auth/cloud-platform
https://www.googleapis.com/auth/chat.messages.create
https://www.googleapis.com/auth/chat.spaces.create
  1. [テーブルに追加]、[更新]、[保存] の順にクリックします。

56fbba733139acfe.png

OAuth クライアント認証情報を更新する

このソリューションには、追加の承認済みリダイレクト URI が必要です。

  1. Google Cloud コンソールで、メニュー アイコン ☰ > Google Auth Platform > クライアントをクリックします。

  1. クライアント名 codelab をクリックします。
  2. [承認済みのリダイレクト URI] セクションで、[URI を追加] をクリックして https://vertexaisearch.cloud.google.com/static/oauth/oauth.html を入力します。
  3. [保存] をクリックします。

deed597aa54fec91.png

Vertex AI Search MCP を有効にする

  1. ターミナルで次のコマンドを実行します。
gcloud beta services mcp enable discoveryengine.googleapis.com \
     --project=$(gcloud config get-value project)

Chat 用アプリを構成する

  1. Google Cloud コンソールで、Google Cloud 検索フィールドに Google Chat API を入力し、[Google Chat API]、[管理]、[構成] の順にクリックします。

  1. [アプリ名] と [説明] を Gemini Enterprise に設定します。
  2. [アバターの URL] を https://developers.google.com/workspace/add-ons/images/quickstart-app-avatar.png に設定します。
  3. [インタラクティブ機能を有効にする] の選択を解除し、表示されたモーダル ダイアログで [無効にする] をクリックします。
  4. [エラーを Logging に記録する] を選択します。
  5. [保存] をクリックします。

90cb612e51bce4e6.png

Vertex AI Agent Engine にエージェントをデプロイする

  1. この GitHub リポジトリをダウンロードします。

  1. ターミナルで 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

eafd2f9c4fbf305.png

  1. ログに「Deploying to agent engine...」という行が表示されたら、新しいターミナルを開き、次のコマンドを実行して、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"
  1. adk deploy コマンドが完了するまで待ち、コマンド出力の緑色の部分から、新しくデプロイされたエージェントのリソース名をコピーします。

d098fe1347d6581b.png

Gemini Enterprise にエージェントを登録する

Cloud コンソールから新しいタブで Gemini Enterprise を開き、次の操作を行います。

  1. codelab という名前のアプリをクリックします。
  2. ナビゲーション メニューで [エージェント] をクリックします。
  3. [+ エージェントを追加] をクリックします。
  4. [Agent Engine によるカスタム エージェント] の [追加] をクリックします。[承認] セクションが表示されます。
  5. [承認を追加] をクリックします。
  6. [Authorization name] を enterprise-ai に設定します。ID は名前に基づいて生成され、フィールドの下に表示されます。コピーします。
  7. [クライアント ID] に、前の手順で作成して更新した OAuth クライアントと同じ値を設定します。
  8. [クライアント シークレット] を、前の手順で作成して更新した OAuth クライアントと同じ値に設定します。
  9. [トークン URI] を https://oauth2.googleapis.com/token に設定します。
  10. 前の手順で作成して更新した OAuth クライアント ID で <CLIENT_ID> を置き換えてから、[認可 URI] を次の値に設定します。
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
  1. [完了]、[次へ] の順にクリックします。[構成] セクションが表示されます。
  2. [エージェント名] と [エージェントの説明] を Enterprise AI に設定します。
  3. [Agent Engine 推論エンジン] を、前の手順でコピーした推論エンジンのリソース名に設定します。次の形式となります。
projects/<PROJECT_ID>/locations/<LOCATION>/reasoningEngines/<REASONING_ENGINE_ID>
  1. [作成] をクリックします。新しく追加されたエージェントが [エージェント] に表示されます。

エージェントを試す

  1. Gemini Enterprise ウェブアプリで、新しく登録したエージェントとチャットします。
  2. メニュー アイコン ☰ > [エージェント] をクリックします。
  3. [組織から] でエージェントを選択します。
  4. チャットで「Please find my meetings for today, I need their titles and links」と入力し、enter キーを押します。
  5. [Authorize] をクリックし、認証フローに沿って操作します。

ed61cf654cbcd76c.png

  1. エージェントは、カレンダーの予定のリストで回答します(ユーザーのアカウントによって異なります)。
  2. チャットで「Please send a Chat message to someone@example.com with the following text: Hello!」と入力し、enter キーを押します。
  3. エージェントが確認メッセージで回答します。

1647ebff031c42e7.png

a8087d2351e77fb4.png

5. デフォルト エージェントとしての Google Workspace アドオン

このエージェントを使用すると、ユーザーは Workspace アプリの UI のコンテキストで、Workspace のデータを自然言語で検索できます。次の要素に依存します。

  • モデル: Gemini。
  • データ: Google Workspace(カレンダー、Gmail、ドライブ、NotebookLM)用の Gemini Enterprise データストア、Google 検索
  • エージェント ホスト: Gemini Enterprise。
  • UI: Chat と Gmail 用の Google Workspace アドオン(カレンダー、ドライブ、ドキュメント、スプレッドシート、スライドにも簡単に拡張可能)。
  • Google Workspace アドオン: Apps Script、Gemini Enterprise と Vertex AI の API、コンテキスト(ユーザー メタデータ、選択した Gmail メッセージ)。

Google Workspace アドオンは、StreamAssist API を使用して Gemini Enterprise に接続されます。

コンセプトを確認する

Google Workspace アドオン

Google Workspace アドオンは、1 つ以上の Google Workspace アプリケーション(Gmail、Chat、カレンダー、ドキュメント、ドライブ、Meet、スプレッドシート、スライド)を拡張するカスタマイズされたアプリケーションです。

Apps Script

Apps Script は、Google ドライブを基盤とするクラウドベースの JavaScript プラットフォームで、Google プロダクト全体でタスクの統合と自動化を可能にします。

Google Workspace カード フレームワーク

Google Workspace のカード フレームワークを使用すると、デベロッパーはリッチでインタラクティブなユーザー インターフェースを作成できます。テキスト、画像、ボタンなどのウィジェットを含めることができる、整理された視覚的に魅力的なカードを構築できます。これらのカードは、構造化された情報を提供し、Workspace アプリケーション内で直接クイック アクションを実行できるようにすることで、ユーザー エクスペリエンスを向上させます。

ソリューション アーキテクチャを確認する

1798c39f7aaed8fc.png

ソースコードを確認する

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 コンソールで、次の手順を行います。

  1. メニュー アイコン ☰ > [IAM と管理] > [サービス アカウント] > [+ サービス アカウントを作成] をクリックします。

  1. [サービス アカウント名] を ge-add-on に設定します。

d44d6aae29e2464c.png

  1. [作成して続行] をクリックします。
  2. 権限に ディスカバリー エンジン閲覧者ロールを追加します。

f1374efa4f326ef5.png

  1. [続行]、[完了] の順にクリックします。[サービス アカウント] ページにリダイレクトし、作成したサービス アカウントが表示されます。

b9496085f1404c5c.png

  1. 新しく作成したサービス アカウントを選択し、[キー] タブを選択します。
  2. [鍵を追加]、[新しい鍵を作成] の順にクリックします。
  3. [JSON] を選択し、[作成] をクリックします。

f4280f5533a08821.png

  1. ダイアログが閉じ、新しく作成された公開鍵/秘密鍵ペアが JSON ファイルとしてローカル環境に自動的にダウンロードされます。

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

  1. 次のボタンをクリックして、Enterprise AI アドオンの Apps Script プロジェクトを開きます。

  1. [概要] > [コピーを作成] をクリックします。
  2. Apps Script プロジェクトで、[プロジェクトの設定] > [スクリプトのプロパティを編集] > [スクリプトのプロパティを追加] の順にクリックして、スクリプトのプロパティを追加します。
  3. 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>
  1. APP_SERVICE_ACCOUNT_KEY を、前の手順でダウンロードしたサービス アカウント ファイルの JSON キーに設定します。
  2. [スクリプト プロパティを保存] をクリックします。

Gmail と Chat にデプロイする

Apps Script プロジェクトで、次の手順を行います。

  1. [デプロイ] > [デプロイをテスト] をクリックし、[インストール] をクリックします。この機能が Gmail で利用できるようになりました。
  2. [Head Deployment ID] の [Copy] をクリックします。

2ed2df972ad92715.png

Google Cloud コンソールで、次の手順を行います。

  1. Google Cloud の検索フィールドで Google Chat API を検索し、[Google Chat API]、[管理]、[構成] の順にクリックします。

  1. [インタラクティブ機能を有効にする] を選択します。
  2. [スペースとグループの会話に参加する] のチェックボックスをオフにします。
  3. [接続設定] で [Apps Script] を選択します。
  4. [Deployment ID] を、前の手順でコピーした [Head Deployment ID] に設定します。
  5. [公開設定] で、[Workspace ドメイン内の特定のユーザーとグループにこの Chat 用アプリの利用を許可する] を選択し、メールアドレスを入力します。
  6. [保存] をクリックします。

3b7d461c423f7c51.png

アドオンを試す

新しいタブで Google Chat を開き、次の手順を行います。

  1. Chat 用アプリの Gemini Enterprise とのダイレクト メッセージ スペースを開きます。

3da8690d19baf2d0.png

  1. [構成] をクリックして、認証フローを行います。
  2. What are my meetings for today?」と入力して enter キーを押します。Gemini Enterprise チャットアプリが結果を返します。

c8c63fb3f324fecf.png

新しいタブで Gmail を開き、次の手順を行います。

  1. 件名We need to talk に、本文Are you available today between 8 and 9 AM? に設定して、自分宛てにメールを送信します。
  2. 受信したメールを開きます。
  3. [Enterprise AI] アドオンのサイドバーを開きます。
  4. [メッセージ] を Am I? に設定します。
  5. [メッセージを送信] をクリックします。
  6. 答えはボタンの後に表示されます。

d33b8cb50ee251b7.png

6. クリーンアップ

Google Cloud プロジェクトを削除する

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

Google Cloud コンソールで、次の手順を行います。

  1. メニュー アイコン ☰ > [IAM と管理] > [設定] をクリックします。

  1. [シャットダウン] をクリックします。
  2. プロジェクト ID を入力します。
  3. [このままシャットダウン] をクリックします。

3b9492d97f771b2c.png

7. 完了

おめでとうございます!Gemini Enterprise と Google Workspace を連携させて、従業員向けのソリューションを構築しました。

次のステップ

この Codelab では最も一般的なユースケースのみを紹介しましたが、ソリューションはさまざまな形で拡張して使用できます。たとえば次のような例があります。

  • Gemini CLI や Antigravity などの AI を活用したデベロッパー ツールを使用します。
  • カスタム MCP、カスタム関数呼び出し、生成 UI などの他のエージェント フレームワークやツールと統合します。
  • Vertex AI などの専用プラットフォームでホストされているカスタムを含む他の AI モデルと統合します。
  • Dialogflow などの専用プラットフォームでホストされている他のエージェントや、Cloud Marketplace を介してサードパーティがホストしている他のエージェントと統合します。
  • Cloud Marketplace でエージェントを公開して、チーム、組織、一般ユーザーを支援します。

その他の情報

YouTube 動画、ドキュメント ウェブサイト、コードサンプル、チュートリアルなど、さまざまなデベロッパー向けリソースがあります。