見出し画像

BI as Code × Gemini でダッシュボード更新ログを自動生成する


はじめに

BIダッシュボードの更新履歴管理は、多くのデータチームにとって手作業の負担が大きい課題です。本ドキュメントでは、BI as Code のアプローチと Gemini APIGitHub Actions を組み合わせて、ダッシュボードの変更ログを自動生成する仕組みについて紹介します。

背景と課題

従来の課題

  • ダッシュボードの変更内容を手動で記録する運用コスト

  • 「いつ、何が変わったのか」を後から追跡できない

  • チームメンバーや利用者への変更通知が漏れる

  • ダッシュボードが増えるほど管理が破綻する

BI as Code とは

BI as Code は、ダッシュボードの定義をコード(YAML / JSON 等)として Git リポジトリで管理するアプローチです。これにより:

  • バージョン管理: Git タグやコミット履歴で変更を追跡

  • 差分検出: 任意の2時点間の構造差分をプログラムで抽出可能

  • レビュープロセス: Pull Request ベースで変更をレビュー

多くの BI ツール(ThoughtSpot TML、Looker LookML、Tableau Pulseなど)が、ダッシュボード定義のコード化をサポートしています。

  • Communeの新分析画面(ThoughtSpot TML)に関する技術については、以前共有されています。

アーキテクチャ概要

アーキテクチャ概要

処理フロー

Step 1: ダッシュボード定義の Git 管理

BI ツールのダッシュボード定義(YAML/JSON)を Git リポジトリで管理し、定期的にエクスポートしてタグを付けます。

dashboard-definitions/
├── liveboard_A.yaml
├── liveboard_B.yaml
└── ...

各タグ(例: rev.20250101, rev.20250201)がある時点のダッシュボード状態のスナップショットとなります。

Step 2: 構造化された差分データの抽出

ダッシュボードの YAML 定義を、以下のようなメタデータテーブルに変換し、2つのバージョン間の差分を検出します。

差分データ

差分検出では、dbt audit スタイルの比較ロジックを使います:

  • ここでは、dbt auditの出力形式を参考にしています。追加、削除、変更の3つのパターンを追跡し、後続のGemini処理にデータの根拠を提供しています。

def audit_compare(current_df, latest_df, primary_keys, compare_columns):
    """
    2つの DataFrame を主キーでマージし、
    各行を added / removed / modified / identical に分類する
    """
    merged = current_df.merge(latest_df, on=primary_keys, how='outer', indicator=True)

    # left_only  → removed(旧バージョンにのみ存在)
    # right_only → added(新バージョンにのみ存在)
    # both       → compare_columns の値を比較して modified / identical を判定

    return diff_df
差分検出

出力は CSV 形式で、各テーブルの差分データを保存します:

target/diff_data/
├── tab_master_diff.csv
├── visualization_master_diff.csv
├── filter_master_diff.csv
├── formula_master_diff.csv
├── column_master_diff.csv
└── parameter_master_diff.csv

Step 3: Gemini API による更新ログの生成

差分 CSV を Gemini API に渡し、人間が読みやすい更新ログを自動生成します。

プロンプト設計のポイント

  1. テーブルスキーマの説明を埋め込む: 各カラムの意味をプロンプトに含め、Gemini が差分の意味を正確に理解できるようにする

  2. テーブルごとに専用テンプレートを用意: 例えば column_master の変更は table_name でグループ化して表示するなど、テーブルの性質に応じた出力を指定

  3. 低い temperature(0.3)を設定: レポートの一貫性と正確性を重視

  4. CSV の切り詰め処理: 大量の差分データがある場合、トークン制限に収まるよう自動で切り詰め(ヘッダー行は保持)

# プロンプトテンプレートの概念例
PROMPT_TEMPLATE = """
以下は {table_name} テーブルの差分データです。

## テーブルスキーマ
{schema_description}

## 差分データ (CSV)
{diff_csv_content}

上記データを分析し、以下の3つのセクションに分けて更新ログを作成してください:
1. 追加された項目
2. 削除された項目
3. 変更された項目(変更前後の値を含む)
"""
  • 後ほどプロンプト設計のノウハウ(Prompt Design 深掘り)について詳しくご紹介しますので、皆さんのお役に立てればと思います。

サマリー生成

  • 全テーブルの個別ログを生成した後、それらを統合して全体サマリーを生成します。

  • これにより、「今回のリリースで何が変わったか」を一目で把握できます。

Step 4: GitHub Actions による自動化

name: Dashboard Update Report

on:
  schedule:
    - cron: '0 17 * * 0'  # 毎週定期実行
  workflow_dispatch:       # 手動実行も可能

jobs:
  generate-report:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Python
        # uv / pip 等でセットアップ

      - name: Clone dashboard definitions
        # BI 定義リポジトリを取得

      - name: Get latest tag
        run: |
          LATEST_TAG=$(git tag --sort=-version:refname | head -1)

      - name: Extract diff and generate report
        env:
          GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
        run: |
          # 対象ダッシュボードごとにループ
          for DASHBOARD in $TARGET_DASHBOARDS; do
            # 1. 現在のドキュメントからバージョンタグを取得
            # 2. 差分データを抽出
            python extract_diff.py \\
              --dashboard "$DASHBOARD" \\
              --from "$CURRENT_TAG" \\
              --to "$LATEST_TAG"

            # 3. Gemini で更新ログを生成
            python generate_report.py \\
              --dashboard "$DASHBOARD" \\
              --diff-dir ./target/diff_data
          done

      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v6
        with:
          commit-message: "Update dashboard changelog to ${{ env.LATEST_TAG }}"
          branch: auto/update-dashboard-docs
          title: "Update dashboard changelog to ${{ env.LATEST_TAG }}"
GitHub Actions による自動化

Step 5: ドキュメント管理

生成されたログは Markdown ファイルとして管理し、追記式(最新の変更が先頭)で更新します。ファイル名にバージョンタグを含めることで、現在のバージョンが一目で分かります。

docs/dashboard_docs/
├── Dashboard_A rev.20250201.md
└── Dashboard_B rev.20250201.md

ドキュメントの構成:

# Dashboard_A rev.20250101 → rev.20250201

**生成日時**: 2025-02-01 10:00:00

## 更新サマリー
フィルターの追加と一部ビジュアライゼーションのチャートタイプが変更されました。

## 主な変更点
- タブ「概要」に新しいグラフが2つ追加
- 日付フィルターのデフォルト値が変更
- 計算式「成長率」のロジックが更新

## 詳細

<details>
<summary>visualization_master の変更</summary>
...
</details>

<details>
<summary>filter_master の変更</summary>
...
</details>

---
(以下、過去のバージョンの変更ログが続く)
ドキュメントの構成-1
ドキュメントの構成-2

導入のメリット

導入のメリット

設計上の工夫

1. メタデータテーブルへの正規化

BI 定義ファイル(YAML)をそのまま diff するのではなく、意味のあるメタデータテーブルに正規化してから比較します。これにより:

  • YAML の構造変更(キーの順序変更等)によるノイズを排除

  • 主キーベースの正確なマッチング

  • テーブルごとに適切な比較カラムを指定可能

2. dbt audit スタイルの差分検出

データエンジニアリングでよく使われる dbt audit パターンを採用。added / removed / modified / identical の4ステータスで分類し、変更の種類を明確に区別します。

3. テーブル特性に応じたプロンプト分岐

全テーブルに同じプロンプトを使うのではなく、テーブルの特性に合わせてプロンプトを変えます。例えば、カラム定義の変更はテーブル名でグループ化して表示する方が読みやすいため、専用のテンプレートを用意します。

4. CSV 切り詰めによるトークン管理

大量の差分データがある場合でも、ヘッダー行を保持しながら自動的に切り詰めることで、API のトークン制限内に収めます。

Prompt Design 深掘り

設計原則:「LLM にデータの意味を理解させる」

  • CSV の差分データだけを渡しても、LLM は chart_type: TREEMAP → COLUMN が何を意味するのか理解できません。

  • ドメイン知識をプロンプトに埋め込むことが、品質の高いレポートを生成する鍵です。

心得 1: スキーマ説明の埋め込み — コンテキストウィンドウを「辞書」として使う

各テーブルのカラム定義をプロンプトに含めることで、LLM が差分データの各フィールドの意味を正確に理解できます。

TABLE_SCHEMA_DESCRIPTIONS = {
    "visualization_master": """
    ## ビジュアライゼーション情報
    - tab_name: 所属タブ名
    - visualization_name: ビジュアライゼーション名
    - display_mode: 表示モード(CHART_MODE/TABLE_MODE)
    - chart_type: チャートタイプ(TREEMAP/COLUMN/PIVOTなど)
    - ordered_column: 使用カラムのリスト
    - custom_name: カラムのカスタム表示名
    - visualization_description: ビジュアライゼーションの説明文
    """,
    "filter_master": """
    ## フィルター情報
    - filter_id: フィルターID
    - filter_column: フィルター対象カラム
    - default_values: デフォルト値のリスト
    - included_visualization_names: 適用先ビジュアライゼーションのリスト
    - filter_description: フィルターの説明文
    """,
    # ... 他テーブルも同様
}

なぜ効果的か:LLM は「display_modeが CHART_MODEから TABLE_MODEに変わった」→「グラフ表示からテーブル表示に切り替わった」と、ユーザー視点での意味を推論できるようになります。

心得 2: 差分データのセマンティクスを明示する — 「audit カラム」の意味を教える

  • 差分 CSV には dbt_audit_row_status や dbt_audit_in_a / dbt_audit_in_b といった監査用カラムが含まれます。

  • これらの意味をプロンプト内で明示的に説明します。

# 差分データの共通カラム説明
- dbt_audit_in_a: True の場合、旧バージョンに存在するレコード
- dbt_audit_in_b: True の場合、新バージョンに存在するレコード
- dbt_audit_row_status: 変更ステータス
  - added: 新規追加されたレコード(in_a=False, in_b=True)
  - removed: 削除されたレコード(in_a=True, in_b=False)
  - modified: 変更されたレコード(in_a と in_b の両方に存在するが内容が異なる)

心得: LLM は CSV の値だけ見ても added/ removed/ modifiedの意味を推測できますが、in_a/ in_bがどちらのバージョンを指すかは曖昧です。明示することで誤解を防ぎます。

心得 3: テーブル特性に応じたプロンプト分岐 — 「One Size Fits All」の罠を避ける

  • 全テーブルに同一のプロンプトを使うと、出力の読みやすさが大きく低下します。

  • テーブルの性質に応じてプロンプトを分岐させます。

デフォルトテンプレート(tab_master, visualization_master, filter_master など):

# 出力形式
以下の形式で更新ログを作成してください:

#### 追加された項目
- (追加された内容を箇条書きで記載)

#### 削除された項目
- (削除された内容を箇条書きで記載)

#### 変更された項目
- (変更前→変更後の形式で記載)

column_master 専用テンプレート(カラム変更は table_nameでグループ化が必須):

# 出力形式
**重要**: 変更内容は `table_name`(データソーステーブル名)でグループ化して記載してください。

#### テーブル別の変更一覧

##### [table_name_1]
- 追加されたカラム: column_name1, column_name2, ...
- 削除されたカラム: column_name3, ...
- 変更されたカラム: column_name4(変更内容の説明)

##### [table_name_2]
- ...

なぜ分岐が必要か

  • column_masterでは 1 つのダッシュボードに数十〜数百のカラムが存在し、それぞれが異なるテーブルに属します。

  • フラットな箇条書きでは可読性が著しく低下するため、テーブル名でのグループ化が不可欠です。

def get_prompt_template_for_table(table_name: str) -> str:
    """テーブルの特性に応じたプロンプトを選択"""
    if table_name == "column_master":
        return get_column_master_prompt_template()
    return get_default_prompt_template()

心得 4: ロールの設定 — 「視点」を指定する

プロンプトの冒頭で LLM に役割を与えます:

あなたはダッシュボードの開発者です。ダッシュボードを更新した際に、
関連データに変化があることを発見しました。
以下の差分データを分析し、更新ログを作成してください。

「ダッシュボード開発者」という役割を与えることで:

  • 技術的な詳細ではなく、ユーザー影響を重視した記述になる

  • 変更の意図を推測して補足説明を追加してくれる

心得 5: 「注意事項」セクションで出力品質をコントロール

プロンプト末尾の注意事項で、出力の方向性を微調整します:

注意事項:
- 技術的な詳細よりも、ユーザーに影響のある変更を優先して記載してください
- 変更の意図が推測できる場合は、簡潔に補足してください
- 差分データが空の場合は「変更なし」と記載してください

心得: これがないと、LLM は CSV の全行を機械的に列挙しがちです。「ユーザー影響を優先」と明示することで、重要な変更をハイライトし、些末な変更は省略してくれます。

心得 6: CSV データのインジェクション対策 — <csv_data> タグで境界を明示

差分データは外部入力なので、プロンプトインジェクションのリスクがあります。XML タグで明確にデータ境界を示します:

CSV データのインジェクション対策

サマリー生成のプロンプト:

全テーブルの更新ログを総括し、ダッシュボード全体の更新サマリーを作成してください。

## 出力形式
## 更新サマリー
(全体的な変更内容を2-3文で簡潔にまとめる)

## 主な変更点
- (ユーザーに影響のある重要な変更を箇条書きで記載)

メリット:

  • 各テーブルに最適なプロンプトを適用できる

  • トークン制限に引っかかりにくい(1テーブルずつ処理するため)

  • サマリー生成は既に要約済みのテキストを入力とするので、高品質な要約が得られる

心得 8: パラメーターチューニング — 正確性と創造性のバランス

TEMPERATURE = 0.3      # 低め:事実の正確な記述を重視
MAX_TOKENS = 8192      # 十分な出力長を確保
MAX_CSV_CHARS = 60000  # 入力CSV の切り詰め上限
パラメーターチューニング

CSV 切り詰めの実装ポイント

  • 単純に先頭 N 文字で切るとヘッダーが欠損し、LLM がカラムの意味を理解できなくなります。

  • 必ずヘッダー行を保持した上で、データ行を行単位で切り詰めます。

def truncate_csv_content(content: str, max_chars: int) -> str:
    lines = content.split('\\n')
    header = lines[0]          # ヘッダー行は必ず保持
    truncated = header + '\\n'

    for line in lines[1:]:
        if len(truncated) + len(line) > max_chars - 100:  # 余裕を持たせる
            break
        truncated += line + '\\n'

    truncated += f"\\n... (truncated, original: {len(content)} chars)"
    return truncated

応用可能なシナリオ

この仕組みは、BI ツールに限らず以下のようなケースにも応用できます:

  • Terraform の plan 結果を AI で要約し、インフラ変更の影響を通知

  • データベースマイグレーションの変更内容を自然言語で説明

  • 設定ファイル(Feature Flags 等)の変更を追跡・通知

まとめ

BI as Code + Gemini + GitHub Actions の組み合わせにより、以下を実現しました:

  1. Git タグによるダッシュボードのバージョン管理

  2. 構造化された差分抽出によるノイズのない変更検出

  3. Gemini API による人間が読みやすい更新ログの自動生成

  4. GitHub Actions + PR による変更通知とレビューフローの統合

この仕組みにより、ダッシュボードの変更管理にかかる手作業をほぼゼロにしつつ、利用者への透明性を向上させることができます。

最後

現時点ではまだ多くの改善点があります。プロンプトの改善を中心に、さらに深く開発を進めていくことが可能です。

ご意見がございましたら、ぜひコメントをいただき、一緒に交流できればと思います。本noteが皆さんのお役に立てれば幸いです。

We are hiring!

コミューンデータチームでは一緒に働く仲間を探しています。


いいなと思ったら応援しよう!