
はじめに
こんにちは、データ・AIシステム本部データシステム部データ基盤ブロックの栁澤(@i_125)です。私はデータ基盤の安定化・効率化を目指しつつ、Analytics Engineerとしてデータ利活用領域にも踏み込み、データマート整備やLooker周辺の整備・サポートに取り組んでいます。本記事では、Lookerダッシュボードに生成AIによる要約機能を組み込んだ「Gemini in Looker」の活用事例をご紹介します。
目次
背景
弊社ではZOZOTOWNに出店いただいているテナント企業様向けに、Lookerを用いた売上分析ダッシュボードを提供しています。社内ではLookerのUI上で直接ダッシュボードを閲覧する一方、テナント企業様にはLooker Embed(Lookerのダッシュボードを外部アプリケーションに埋め込む機能)を通じて提供しています。このように、同じLookerインスタンスを社内外で共有しています。別のテナント企業様の売上情報が閲覧できてしまうと重大な問題となるため、厳格なアクセス制御が必須です。
このようなセキュアな環境でダッシュボードを運用し、データ利活用が進むにつれてダッシュボードの情報量も増えていきました。そんな中、ビジネス側から「ダッシュボード全体を要約して理解しやすくできないか」という提案がありました。これを受けて、生成AIを活用したダッシュボード要約機能を検討することになりました。
Gemini in Lookerとは
概要
ご存じの方も多いと思いますが、改めてGemini in Lookerについて説明します。Gemini in Lookerは、Looker上で利用可能な「生成AIによる支援機能群」の総称です。複数の機能が存在し、それぞれ異なる目的で利用できます。
| 機能名 | 提供形態 | 概要 |
|---|---|---|
| Conversational Analytics | 標準機能 | チャットUIで自然言語の質問に応答し、グラフやテキストで分析結果を返す機能 |
| LookML Assistance | 標準機能 | 自然言語で LookML のコードやパラメータを生成・補助する機能 |
| Visualization Assistant | 標準機能 | 自然言語から可視化の JSON 設定を生成する機能 |
| Dashboard Summarization | Looker Extension | ダッシュボードの各タイル(グラフ・表)を要約し、次のアクションを提案する機能 |
| Explore Assistant | Looker Extension | 自然言語での会話形式でデータ探索やグラフ生成を支援する機能 |
| Query Insights | Looker Extension | Explore 結果を要約する機能 |
※2025年11月時点では、標準機能はプレビュー版として提供されています。最新のステータスは公式ドキュメントをご確認ください。
この辺りは日々目まぐるしく新たな機能追加や名称の変更があり得るため、最新情報は公式ドキュメントを参照ください。今回はDashboard Summarization機能をZOZOTOWNテナントユーザー向けダッシュボードに埋め込む形で実装しました。
標準機能とLooker Extension
上記の機能は、提供形態によって大きく2つに分類されます。
標準機能
- Lookerの管理画面から設定を有効化するだけで利用可能
- 開発作業は不要で、すぐに利用を開始できる
- カスタマイズの自由度は限定的
Looker Extension
- Looker Extension Frameworkを使って開発するカスタムアプリケーション
- OSSとしてGitHubで公開されており、自社の要件に合わせてカスタマイズ可能
- 導入には開発とデプロイの作業が必要
- プロンプトの調整やUI変更など柔軟な対応が可能
今回はLooker ExtensionとしてOSSで公開されているdashboard-summarizationをカスタマイズして実装しました。マルチテナント環境でのセキュリティ要件やビジネス要件に合わせて独自に拡張しています。
アーキテクチャ全体像とデータフロー
システム構成
公式で提供されているアーキテクチャ図をベースに、ZOZOの環境に合わせた構成を実装しました。前提として、弊社ではLooker(core)を利用しています。

赤枠部分がデフォルトのOSS実装で提供されている部分から追加・変更した部分です。Cloud Load BalancingとCloud Armorを追加しました。Cloud RunへのアクセスはCloud Load Balancingを経由し、Cloud ArmorによってLookerインスタンスからのIPアドレスのみを許可しています。APIトークンによる認証で一定の安全性は確保されていますが、念のためIPアドレス制限も追加することで二重の防御を実現しています。
LookerインスタンスのIPアドレスについては公式ドキュメントを参照してください。
セキュリティ上の重要な設計判断
公式で提供されているアーキテクチャ図を見ると、Cloud RunがLooker API経由でBigQueryやData Sourcesへアクセスするように見えます。しかし実装を確認したところ、実際には異なる設計でした。フロントエンド(Looker Extension)がユーザー権限でLooker Query APIを呼び出し、取得したデータをCloud Run側へ送信する設計になっています。
マルチテナント環境では、この設計が重要なポイントです。今回参照されるダッシュボードは全テナント共通です。もしCloud Run側からLooker Query APIを呼び出す設計にすると、エンドユーザーから提供されたパラメータをそのまま利用する可能性があります。これによりパラメーター改ざん攻撃のリスクが生じ、他テナントのデータを閲覧できてしまう恐れがありました。
実際の実装では、権限制御およびクエリ実行はLooker内で完結しています。
実際のデータフロー(安全な設計)

上図は実際に採用した安全な設計のデータフローです。Looker Extensionがユーザー権限でLooker Query APIを呼び出すことで、アクセス制御が確実に適用されます。
もしCloud Run側から呼び出していたら(危険な設計との比較)

対照的に、もしCloud Run側からLooker Query APIを呼び出す設計にしていた場合、ユーザーパラメータがそのまま利用されます。これによりパラメーター改ざん攻撃リスクや他ショップデータ漏洩の危険性がありました。
User AttributeとAccess Filterによる権限制御
今回のダッシュボードで扱うデータは、テナント企業様が運営するショップ(販売元)単位で管理されています。1つのテナント企業様が複数のショップを運営しているケースもあるため、アクセス制御もショップ単位で実装する必要があります。
この制御を実現するために、LookerのUser AttributeとAccess Filterを組み合わせています。各ユーザーのUser Attributeには閲覧可能なショップIDのリストが設定されており、このリストに基づいてアクセス可能なデータが自動的に制限されます。
具体的には以下の仕組みでセキュリティを担保しています。

上図のように、以下の3つのステップでアクセス制御が実現されています。
- Looker Embedでダッシュボードを表示しているアプリケーション側で一意のユーザーIDをユーザー属性(User Attribute)として保持した形で、Lookerユーザーを発行
- Lookerモデル上でaccess_filterを設定することで、ユーザーのUser Attributeに紐づくショップIDの条件をクエリ発行時に自動付与。例:
WHERE shop_id IN (123, 456, 789) - この条件はフロントエンドから悪意のあるリクエストを送っても変更できない
図中の黄色でハイライトされている「クエリ生成」と「SQL実行」の部分で、User Attributeに基づくアクセス制御が自動的に適用されます。このフローにより、クエリ実行はLooker内で完結しており、パラメーター改ざん攻撃等のリスクを回避できています。
機能面で工夫した3つのポイント
1. プロンプトの一部を外部ファイル化
デフォルトのプロンプトでは汎用的な要約しか生成されません。そこでZOZOTOWNのビジネスコンテキストを含むガイドラインファイル(guideline.txt)を作成しました。サーバー起動時に読み込んでプロンプトに含めることで、サイトの特徴を踏まえた現実的なNext Actionを提示できるようになりました。たとえばガイドラインファイルには、以下のような情報を含めています。
- サイトの特性や傾向に関するデータ(ユーザー属性、購買傾向、サイト構成など)
- ビジネス上の制約や推奨される運用方法
- 状況に応じた具体的な施策の選択肢
また、外部ファイル化することで、コードを変更せずにビジネス情報を更新できるメリットも得られました。
2. 複数クエリをまとめて要約
デフォルトの実装では、個別クエリ要約エンドポイント(/generateQuerySummary)を使ってタイルごとに要約を生成し、それを箇条書きで並べるスタイルでした。しかし、ダッシュボード全体の情報を統合した上での要約が欲しいという要望がありました。
OSSにはダッシュボード統合要約エンドポイント(/generateSummary)も用意されていますが、デフォルトでは使用されていません。そこでフロントエンド側で両方のエンドポイントを組み合わせるよう実装を変更しました。まずcollateSummaries関数で各クエリの個別要約を生成します(内部で/generateQuerySummaryを使用)。その結果をgenerateFinalSummary関数で/generateSummaryエンドポイントに渡します。これによりダッシュボード全体を俯瞰した統合要約を生成できるようになりました。
// 各クエリの個別要約を生成 const querySummaries = await collateSummaries( queryResults, nextStepsInstructions, restfulService, extensionSDK, dashboardMetadata, setQuerySummaries, setLoadingStates ); // 個別要約を統合してダッシュボード全体の要約を生成 await generateFinalSummary( querySummaries, restfulService, extensionSDK, setFormattedData, nextStepsInstructions );
3. 事前プロンプトの固定化
デフォルト実装ではユーザーが自由に質問を入力できるUIになっていますが、社外テナントユーザーが利用するケースではセキュリティリスクが懸念されました。そこで、ユーザー入力欄を廃止し、フロントエンド・バックエンド両方で固定プロンプトのみを使用するように変更しました。今回は特殊ケースなのでこのような実装に変更しましたが、こういった柔軟な変更ができるところもOSSの利点と言えます。
本番適用時の技術的課題と解決策
次に、本番適用時に直面した技術的課題とその解決策について説明します。
フィルタ状態の同期
本番適用時、ダッシュボードのフィルタ機能と組み合わせた際に技術的な課題が発覚しました。
課題:初期状態に依存した要約の不整合
デフォルトの実装では、ダッシュボード要約は初期ロード時のフィルタ状態を基に生成されていました。そのため、以下のような問題が発生しました。
- ユーザーがフィルタを変更
- 画面上のグラフ・表は新しいフィルタで更新される
- しかし要約生成時は初期フィルタ状態のデータを参照
- 結果として、画面表示と要約内容が乖離
この問題は、Looker Extension SDKにおけるダッシュボードメタデータの取得タイミングに起因していました。
解決策: 動的なフィルタ状態の取得
要約生成時にtileHostData.dashboardFiltersから現在のフィルタ状態を取得し、それをfetchDashboardDetailsに渡すように変更しました。これにより常にユーザーが見ている状態と一致した要約が生成されます。フィルタ変更後も画面表示と要約内容の整合性が保たれるようになりました。
// 最新のフィルタ状態でダッシュボードメタデータを再取得 const latestMetadata = await fetchDashboardDetails( dashboardId, core40SDK, extensionSDK, tileHostData.dashboardFilters || {} ); // メタデータを更新して一貫性を保つ setDashboardMetadata(latestMetadata); // 最新のクエリ結果を取得 const latestResults = await fetchQueryData(latestMetadata.queries, core40SDK);
Looker ExtensionでのSecret Key利用
実装時に個人的に混乱したポイントとして、Looker Extension FrameworkではUser Attributeに名前空間が自動的に付与される仕組みがあります。公式ドキュメントによると、拡張機能ID my-extension::my-extension でUser Attribute my_value を使用する場合の例を示します。Looker側で実際に設定する属性名は my_extension_my_extension_my_value になります。
extensionSDK.createSecretKeyTag()でSecret Keyを参照する際は、フロントエンド側ではmy_valueのように短い名前を指定するだけです。Frameworkが自動的に名前空間を付与してくれます。以下は実際の使用例です。
// バックエンドAPIへのリクエスト時にシークレットキーを使用 const response = await extensionSDK.serverProxy(`${restfulService}/generateSummary`, { method: 'POST', headers: { "Content-Type": "application/json" }, body: JSON.stringify({ querySummaries, nextStepsInstructions, // フロントエンド側では短い名前を指定 client_secret: extensionSDK.createSecretKeyTag("my_value") // Looker側では my_extension_my_extension_my_value として設定 }) });
PDFダウンロード対応
Extension Tileを含むダッシュボードをPDFダウンロードする場合、extensionSDK.rendered()を呼び出してレンダリング完了を通知する必要があります。この実装がないと、Lookerのレンダラが応答を停止してしまいます。
詳細は公式ドキュメントを参照してください。
なお、サマリ内容自体はPDFに含まれません。サマリは非同期で生成されるため、標準のダウンロード機能では取得できない仕様となっています。
実際の動作例
以下は、BigQueryの公開データセット(bigquery-public-data.thelook_ecommerce)を使用したデモダッシュボードでの動作例です。ダッシュボードには複数のタイル(グラフや表)が表示されており、右サイドに「サマリを生成」ボタンがあります。
なお、このダッシュボードの数字はZOZOTOWNの実際の売上データとは一切関係ありません。

ボタンをクリックすると、各タイルの内容を分析し、ダッシュボード全体を俯瞰した要約とNext Actionが生成されます。なお、スクリーンショット内のNext Action部分はビジネス情報を含むためマスキングしています。

実装のポイント
本記事で紹介した実装における重要なポイントを以下にまとめます。
セキュリティ設計
- User AttributeとAccess Filterによるマルチテナントデータアクセス制御
- パラメーター改ざん攻撃等のリスクを回避
- 事前プロンプトの固定化によるセキュリティ担保
- ユーザー入力の制限によるセキュリティ強化
- Cloud Load BalancingとCloud Armorによる二重防御
- APIトークン認証に加えてIPアドレス制限を実施
プロンプト設計
- ZOZOTOWNのビジネスコンテキストを外部ファイル化(guideline.txt)
- 現実的なNext Actionの提示
要約処理の工夫
- 複数クエリの統合要約の実装
- ダッシュボード全体を俯瞰した要約生成
- フィルタ状態の動的取得による整合性の確保
- ユーザー操作に応じた要約生成
まとめ
本記事では、Gemini in LookerのDashboard Summarization機能をマルチテナント環境で安全に実装する方法を紹介しました。
現在はお試しリリースの段階ですが、マルチテナント環境での生成AI活用機能を、セキュリティを担保しながら安全に実装できました。今後はユーザーフィードバックを元にした継続的な改善を検討していきます。
Gemini in Lookerの導入を検討している方がいれば、ぜひ参考にしてみてください。
ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。