Technical Blog テクニカルブログ
  1. HOME
  2. テクニカルブログ
  3. LookerのVertex AI ActionsでSlack連携してみた:Gitの手順でハマったエラーと解決策まとめ

LookerのVertex AI ActionsでSlack連携してみた:Gitの手順でハマったエラーと解決策まとめ

投稿者:杉山

目次

  1. はじめに
  2. Githubの手順で発生したエラーと最終的なコード
  3. Slack通知へのカスタマイズ
  4. 実行結果
  5. さいごに

1.はじめに

こんにちは、杉山です。
Lookerのデータを活用して、Googleの強力なAI「Gemini」で分析できる『Vertex AI Actions』。公開されているGithubの手順(github.com/looker-open-source/vertex-ai-actions)通りに進めたのに、なぜか動かない…。そんなよくあるエラー地獄(笑)を乗り越え、最終的に標準のメール通知をSlack通知にカスタマイズするまでの記録をまとめました。

同じ問題に直面している方の助けになれば幸いです!

2.Githubの手順で発生したエラーと最終的なコード

公式手順では、Cloud Functionがシークレット(APIキーなど)をファイルとして読み込むことを想定していますが、私たちの環境ではこれがうまく機能しませんでした。最終的に、コード側でSecret Manager APIを直接呼び出すことで、この問題を根本的に解決しました。

【修正1】GCPサービスアカウントの権限設定

まず、Cloud FunctionがGCPの各種サービスにアクセスできるよう、サービスアカウントに権限を付与します。特定のシークレットだけでなく、プロジェクト全体のシークレットにアクセスできる権限を与えるのが確実です。

# Cloud Shellで実行
PROJECT_ID="$(gcloud config get-value project)"
SERVICE_ACCOUNT_EMAIL="vertex-ai-action@${PROJECT_ID}.iam.gserviceaccount.com"

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \
  --role="roles/secretmanager.secretAccessor"

【修正2】ソースコードの全面改修

APIを直接呼び出すために、3つのファイル(requirements.txt, utils.py, gemini_api.py)を修正しました。

1. 必要なライブラリを追加 (requirements.txt)

Secret ManagerとSlackを扱うためのライブラリを追加します。

# requirements.txt
google-cloud-secret-manager
slack_sdk
2. 認証処理をAPI呼び出しに変更 (utils.py)

認証トークンをファイルからではなく、APIで取得するようにauthenticate関数を全面的に書き換えます。

# utils.py
from google.cloud import secretmanager
import hmac
from flask import Response
import os

def authenticate(request):
    """Secret Manager APIを直接呼び出して認証トークンを検証する"""
    if request.method != 'POST' or 'authorization' not in request.headers:
        # ... (エラー処理) ...
    
    try:
        client = secretmanager.SecretManagerServiceClient()
        project_id = os.environ.get("PROJECT")
        secret_name = f"projects/{project_id}/secrets/LOOKER_AUTH_TOKEN/versions/latest"
        response = client.access_secret_version(request={"name": secret_name})
        token = response.payload.data.decode("UTF-8").strip()

        expected_auth_header = f'Token token="{token}"'
        submitted_auth = request.headers['authorization']
        
        if hmac.compare_digest(expected_auth_header, submitted_auth):
            return Response(status=200)
        else:
            return handle_error('Incorrect token', 403)
    except Exception as e:
        return handle_error(f"Authentication Error: {e}", 500)

# ... (他のヘルパー関数) ...
3. AIモデル変更とエラーハンドリングを強化 (gemini_api.py)

デフォルトのAIモデル「gemini-1.5-flash」は現在利用できないため、新しい「gemini-2.5-flash」に変更しました。

# gemini_api.py
MODEL_VARIANT = 'gemini-2.5-flash'

AIモデル(Gemini)が何らかの理由で応答を返さない(例:コンテンツフィルタリング)場合でも、空の結果ではなくエラー内容を返すように修正します。

# gemini_api.py の model_with_limit_and_backoff 関数内
# ...
for i in range(0, list_size, row_chunks):
    # ...
    try:
        summary = model_prediction(
            model, content, temperature, max_output_tokens, top_k, top_p).text
    except Exception as e:
        print(f"Error during model prediction: {e}")
        # エラーが発生した場合、その内容を結果として設定
        summary = f"AI Model Error: {str(e)}"
    initial_summary.append(summary)
# ...

【修正3】デプロイコマンドの変更

シークレットをファイルとしてマウントする--set-secretsフラグは問題の原因となったため、すべてのデプロイコマンドから削除します。

# 正しいデプロイコマンド (例: vertex-ai-execute)
gcloud functions deploy vertex-ai-execute \
 --entry-point action_execute \
 --env-vars-file .env.yaml \
 --trigger-http \
 --runtime=python311 \
 --allow-unauthenticated \
 --no-gen2 \
 --memory=1024MB \
 --timeout=540s \
 --region=us-central1 \
 --project=${PROJECT_ID} \
 --service-account ${SERVICE_ACCOUNT_EMAIL}

3.Slack通知へのカスタマイズ

ここまでの修正でアクションは正常に動作しますが、今回はさらにSlackへ通知するようにカスタマイズしました。というのも、標準ではSendGridを利用したメール配信ですが、検証を理由にSendgridの無償版を使おうとした結果メールは送信不可という罠に陥り。。。それなら使い慣れたSlackで通知できる仕組みづくりを作ろう!ということで以下試してみた結果です◎

【カスタマイズ1】Slack APIの準備

  • Slack API(https://api.slack.com/apps)のページから[Create New App]→From scratchをクリックし、新規アプリを作成します。

※初めてSlackアプリを作成される方は、リンク先から[Create an App]→From scratchをクリックしてください。

  • アプリ名(例:Looker Vertex AI Action)を入力し、利用したいワークスペースを選択します。
  • 左メニューの「OAuth & Permissions」を開き、「Scopes」セクションでchat:writeスコープを追加します。
  • 「OAuth Tokens」セクションにて[Install to “ワークスペース名”]をクリックします。
  • 内容に問題なければ[許可する]をクリックし、ワークスペースへインストールします。
  • インストール完了後、Bot User OAuth Tokenに表示されているトークンをコピーします。
  • 通知したいSlackのチャンネルを開き、チャンネル情報からチャンネルID (C...) を取得します。
  • 1つ前の手順と同様、インテグレーション設定から作成したアプリを追加します。
  • 取得したBotトークンを、SLACK_BOT_TOKENという名前でGCPのSecret Managerに登録します。
echo -n "取得したBotトークン" > new_slack_token.txt
gcloud secrets create SLACK_BOT_TOKEN --replication-policy="automatic" --project=${PROJECT_ID}
gcloud secrets versions add SLACK_BOT_TOKEN --data-file="new_slack_token.txt" --project=${PROJECT_ID}
rm new_slack_token.txt

【カスタマイズ2】Slack通知ロジックの実装 (main.py)

action_execute関数内の、メール送信(SendGrid)部分をSlack通知ロジックに丸ごと置き換えます。

# main.py の action_execute 関数内
# ... (AIからの応答をbody変数に格納する処理) ...

try:
    # 1. Slack BotトークンをSecret Managerから取得
    client = secretmanager.SecretManagerServiceClient()
    project_id = os.environ.get("PROJECT")
    secret_name = f"projects/{project_id}/secrets/SLACK_BOT_TOKEN/versions/latest"
    response = client.access_secret_version(request={"name": secret_name})
    slack_bot_token = response.payload.data.decode("UTF-8").strip()

    # 2. Slackクライアントを初期化
    from slack_sdk import WebClient
    slack_client = WebClient(token=slack_bot_token)
    
    # 3. チャンネルIDを設定
    channel_id = "xxxxx" # 自身のチャンネルIDに書き換える
    
    # 4. Slackにメッセージを投稿 (シンプルなテキスト形式)
    slack_response = slack_client.chat_postMessage(
        channel=channel_id,
        text=body.replace('<br>', '\n').replace('<strong>', '').replace('</strong>', '')
    )
    return Response(status=200)
    
except Exception as e:
    # ... (エラー処理) ...

【カスタマイズ3】Looker画面の日本語化

gemini_api.py内のinitial_prompt_templatemain.py内のaction_listaction_formにあるlabeldescriptionの値を日本語に書き換えることで、Lookerのアクション実行画面も分かりやすくなります。

# gemini_api.py
initial_prompt_template = '''
    あなたは、ビジネスインテリジェンスツールを使う優秀なデータアナリストです。
    これからJSON形式でデータをお渡ししますので、データから読み取れる特徴やインサイトを分析してください。
    単なるデータの羅列ではなく、データやグラフをぱっと見ただけではわからないような、深い洞察を含めてください。
    以下の三重バッククォートで区切られたデータに基づき、下記の質問に日本語で回答してください。
    
    質問:

    ```{question}```
    
    JSONデータ: 
    
    ```{data}```

    回答:
'''

4.実行結果

では実装したアクションを実際に利用してみたいと思います!

今回はとある飲料メーカーの開発担当者が、新商品開発に向けて売上の結果をもとにアイデアを得ることを目標にしています。まずExploreの画面右上、歯車マークから>Exploreのアクション > 送信で[Vertex AI]を選択します。

「AIへの指示(プロンプト)を入力」から新商品のアイデアを得られるようなプロンプトを追加し、画面右下の[送信]をクリックします。

データ量にもよりますが、大体30秒程度でSlackへ回答が通知されます。

回答内容は3つにカテゴライズされ、①データの概要と主要な傾向 ②データから読み取れる深い洞察 ③新商品の開発アイデア と少々ボリューミーです(笑) 重要な新商品の開発アイデアについては以下のように回答があり、なかなかいいアイデアを提案してくれてるな..といった印象です◎

上記の洞察に基づき、現代の消費者のニーズに応える新商品の開発アイデアを3つ提案します。
---
#### アイデア1: 「機能性ボタニカルウォーター」
*   **コンセプト**: 売上トップの「無糖・健康・自然派」の強みをさらに深化させ、特定の機能性成分を配合した天然水ベースの飲料。天然水に、ハーブやフルーツの微かな香りを加え、無糖・無着色にこだわります。
    *   **例1: 「リラックスウォーター」**: GABAやテアニンを配合し、ストレス軽減やリラックス効果を訴求。カモミールやラベンダーの香りで、就寝前や仕事の合間のリフレッシュに。
    *   **例2: 「ビューティーウォーター」**: コラーゲンやビタミンC、セラミドなどを配合し、美容効果を訴求。ローズヒップやザクロの香りで、内側からの輝きをサポート。
    *   **例3: 「腸活ウォーター」**: 食物繊維や乳酸菌発酵エキスを配合し、腸内環境の改善を訴求。レモンやジンジャーの香りで、すっきりとした飲み心地。
*   **ターゲット**: 健康意識が高く、日常的に手軽に健康ケアや美容ケアを取り入れたい20〜40代の女性。
*   **訴求ポイント**: 「飲むだけで手軽に健康・美容ケア」「自然な風味で心身を癒す」「罪悪感なく毎日飲める」
---
#### アイデア2: 「プレミアム・クラフト炭酸」
*   **コンセプト**: 炭酸飲料の安定した人気と「ウィルキンソン炭酸」の成功(無糖)に着目し、大人向けの洗練された味わいを追求した炭酸飲料。国産の果物やハーブのエキスを使用し、人工甘味料・着色料不使用にこだわります。
    *   **例1: 「和柑橘スパークリング」**: 柚子やすだち、かぼすなど、日本の四季を感じさせる和柑橘の香りを活かした無糖炭酸。食事とのペアリングも意識した、繊細な味わい。
    *   **例2: 「ボタニカルハーブソーダ」**: 紫蘇、山椒、生姜など、和のハーブやスパイスをアクセントにした微糖または無糖炭酸。リフレッシュ効果と、奥深い香りの両方を楽しむ。
*   **ターゲット**: 健康志向でありながら、リフレッシュや嗜好品としての満足感を求める30〜50代の男女。既存の炭酸飲料では物足りなさを感じている層。
*   **訴求ポイント**: 「大人のための上質なリフレッシュ」「日本の素材を活かした繊細な味わい」「食事にも合う、洗練された一本」
---
#### アイデア3: 「リカバリー&リラックスティー」
*   **コンセプト**: 「生茶」の売上低迷から、既存のお茶カテゴリに新たな価値を付加。スポーツドリンクが特定のシーンに限定されがちな中で、日常的なリカバリーやリラックスニーズに応えるノンカフェイン飲料。
    *   **例1: 「ナイトリラックスティー」**: カモミール、レモングラス、ルイボスなどをベースに、GABAやL-テアニンを配合。就寝前のリラックスタイムに特化し、安眠をサポート。
    *   **例2: 「デイリーリカバリーティー」**: 運動後だけでなく、仕事の合間のリフレッシュや、日中の水分補給にも適した、和漢素材(例:クコの実、ナツメ)やハーブをブレンドした無糖飲料。疲労回復や集中力向上をサポート。
*   **ターゲット**: ストレス社会で心身のバランスを整えたいビジネスパーソンや、健康的なライフスタイルを送る人々。カフェイン摂取を控えたい層。
*   **訴求ポイント**: 「心と体を癒す一杯」「ノンカフェインでいつでも安心」「自然な素材でリカバリー&リラックス」
---
これらの新商品は、単に売上上位の製品を模倣するのではなく、その成功要因の背景にある消費者の深層心理や、売上低迷製品の課題を分析することで導き出されました。現代の消費者が求める「健康」「自然」「機能性」「パーソナライズ」「上質さ」といったトレンドを捉え、既存のカテゴリの枠を超えた価値を提供することで、新たな市場を創造できると考えます。

5.さいごに

公式の手順でうまくいかない場合、シークレットの受け渡し方法を見直すのが解決の鍵でした。APIを直接呼び出す方式に変更したことで、動作が安定し、Slack通知への柔軟なカスタマイズも可能になりました。長い道のりでしたが、皆さんの設定の参考になれば幸いです!

Looker×生成AI / Looker Extension Framework関連のブログはこちら↓
Looker×生成AIについてまとめてみた
【Google Cloud Next Tokyo ’24 振り返り】Looker Extension Frameworkを使用した案件管理 デモ動画
Looker Extension Framework -Explore Assistant-のデプロイ方法~ユースケースを一挙公開!
Looker Extension Framework -Dashboard Summarization-のデプロイ方法~ユースケースを一挙公開!

ページのトップへ