GitHub ActionsからGemini File Search Toolを自動実行する

GitHub ActionsからGemini File Search Toolを自動実行する

2025.11.14

https://dev.classmethod.jp/articles/gemini-api-file-search-tool-rag/

Gemini APIのファイル検索ツールを使用して検索拡張生成(RAG)が低コストでできるようになりました。

今回はこのAPIの実行をGithub Actions経由で行わせてみます。

背景

BigQueryのテーブルのメタデータ(テーブル定義、カラム説明、ドメイン知識)をマークダウンにして参照できるようにしているのですが、このファイルをGithubで管理しています。

Github上のブランチにファイルがマージされたら自動的に最新の情報をGeminiのファイル検索に反映させれるのでは?と思ったのでやってみます。

社内データを扱うデータアナリストやバックオフィス業務を行うユーザーが「営業チーム別の月次売上」「部署別の予算」といったビジネス用語で検索する際、適切なBigQueryテーブルやカラムを見つけやすくなるのではないか、さらに将来的にはSQLの実行とそのデータの解析までも目論んでおります。

アーキテクチャ

GitHub Repository
  ↓ (git push)
GitHub Actions
  ↓ (検出: metadata/docs/* 変更)
gemini_file_search_tool.js
  ↓ (複数ファイル並列アップロード)
Gemini File Search Store
  ↓
ユーザークエリ(自然言語)
  ↓
Gemini + File Search Tool
  ↓
回答(該当テーブル・カラム情報など) + 引用元ファイル表示

ユーザーがメタデータから「適切なテーブル」「必要なカラム」を見つけるまでのフローを自動化を目標にしています。

実装のポイント

1. ディレクトリ別Store割り当て

複数のBigQueryデータセット(Salesforce、HubSpot、AWSなど)があり、それぞれ対応するGemini File Search Storeに同期する必要がありました。
検索する際、最大5つまでしかStoreを検索できなかったため、以下のようにBigQueryデータセットとStoreのマッピング情報を設定としてとりあえずおいておくことにしました

{
  "salesforce-store": {
    "datasets": ["データセット1", "データセット11"]
  },
  "hubspot-store": {
    "datasets": ["データセット2", "データセット22", "データセット2222"]
  },
  "aws-store": {
    "datasets": ["データセット3"]
  },
  "core-store": {
    "datasets": ["domain"]
  }
}

2. 再帰的ディレクトリスキャン

階層構造を持つディレクトリがあったため、再帰的にマークダウンファイルを取得する機能を追加しました。

function getMarkdownFiles(dir, recursive = false) {
  try {
    const files = [];
    const entries = fs.readdirSync(dir, { withFileTypes: true });

    for (const entry of entries) {
      if (entry.isFile() && entry.name.endsWith('.md')) {
        files.push(path.join(dir, entry.name));
      } else if (recursive && entry.isDirectory()) {
        // domain ディレクトリの配下を再帰的に取得
        files.push(...getMarkdownFiles(path.join(dir, entry.name), true));
      }
    }
    return files;
  } catch (error) {
    console.error(`Error reading directory ${dir}:`, error.message);
    return [];
  }
}

利用時:

const isRecursive = datasetName === 'domain';
const files = getMarkdownFiles(datasetPath, isRecursive);

3. 並列アップロード

複数ファイルを効率的にアップロードするため、Promise.all()で並列処理しました。

const uploadPromises = files.map(file =>
  (async () => {
    try {
      let operation = await ai.fileSearchStores.uploadToFileSearchStore({
        file: file.path,
        fileSearchStoreName: storeId,
        config: {
          displayName: file.name,
          mimeType: 'text/markdown'
        }
      });

      // Wait until import is complete
      while (!operation.done) {
        await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL_MS));
        operation = await ai.operations.get({ operation });
      }

      console.log(`${file.name}`);
      return { file: file, status: 'success' };
    } catch (error) {
      console.log(`${file.name}: ${error.message}`);
      return { file: file, status: 'error', error: error.message };
    }
  })()
);

const uploadResults = await Promise.all(uploadPromises);

GitHub Actions統合

変更されたファイルの検知方法

GitHub Actionsで変更ファイルを検知する方法ですが、以下のような方法に指定みました:

1. paths フィルター

特定ディレクトリへの変更でのみワークフローを実行します。

on:
  push:
    branches:
      - main
    paths:
      - 'metadata/docs/**'

メリット:

  • ✅ 不要なワークフロー実行を削減
  • ✅ GitHub Actions の実行時間と請求額を削減
  • ✅ 設定がシンプル

動作例:

✅ metadata/docs/domain/knowledge.md 更新        → ワークフロー実行
✅ metadata/docs/domain/sub/file.md 追加         → ワークフロー実行
❌ metadata/json/salesforce/table.json 更新        → ワークフロー実行しない
❌ README.md 更新                                  → ワークフロー実行しない

2. Workflow ステップ内で検知(より詳細な制御が必要な場合)

ワークフロー内で git diff を使用して、変更ファイルを動的に検知。
※ 変更されたファイルだけFile Search Toolを使ってStoreにインポートしたかった。

例:

- name: Get changed files
  id: changed-files
  run: |
    CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD)
    echo "files=$CHANGED_FILES" >> $GITHUB_OUTPUT

- name: Check domain files changed
  run: |
    if echo "${{ steps.changed-files.outputs.files }}" | grep -q "metadata/docs"; then
      echo "Domain metadata changed - syncing to Gemini"
    else
      echo "No domain metadata changes - skipping sync"
    fi

変更があったファイルリストを GITHUB_OUTPUT に保存し、Storeにインポートするためのスクリプトファイルに渡すようにしてみました。

ワークフロー実装

.github/workflows/sync-gemini.ymlの完全な設定:

name: Sync Metadata to Gemini File Search

on:
  push:
    paths:
      - 'google-bigquery/metadata/docs/**'
    branches:
      - main

jobs:
  sync:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0 

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm install

      - name: Get changed files
        id: changed_files
        run: |
          if [ "${{ github.event.before }}" = "0000000000000000000000000000000000000000" ]; then
            # Initial push (new branch)
            CHANGED=$(git diff --name-only HEAD~1..HEAD 2>/dev/null || echo "")
          else
            # Regular push
            CHANGED=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }})
          fi

          # メタデータディレクトリ配下のファイルのみフィルタ
          METADATA_FILES=$(echo "$CHANGED" | grep "^google-bigquery/metadata/docs/" || echo "")

          echo "files<<EOF" >> $GITHUB_OUTPUT
          echo "$METADATA_FILES" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

          if [ -z "$METADATA_FILES" ]; then
            echo "No metadata files changed"
          else
            echo "Changed files:"
            echo "$METADATA_FILES"
          fi

      - name: Sync to Gemini File Search
        if: steps.changed_files.outputs.files != ''
        env:
          GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
          GEMINI_FILE_SEARCH_STORES: ${{ secrets.GEMINI_FILE_SEARCH_STORES }}
          CHANGED_FILES: ${{ steps.changed_files.outputs.files }}
        run: |
          echo "🚀 Syncing files to Gemini..."
          node .github/workflows/gemini_file_search_tool.js

環境変数の設定

GitHub Secrets に以下を登録します:

  • GOOGLE_API_KEY: Gemini APIキー
  • GEMINI_FILE_SEARCH_STORES: 同期先のストア情報。JSON形式でStore Nameを保存していました。

本記事でのStoreは前もって作っておいたものを使っています。

例:

{
  "salesforce-store": "fileSearchStores/salesforce-store-****",
  "hubspot-store": "fileSearchStores/hubspot-store-****",
  "aws-store": "fileSearchStores/aws-store-****",
  "core-store": "fileSearchStores/core-store-****"
}

設定方法:

  1. GitHub リポジトリの Settings → Secrets and variables → Actions
  2. "New repository secret" をクリック
  3. Name: GOOGLE_API_KEY, Secret: [APIキー]
  4. Name: GEMINI_FILE_SEARCH_STORES, Secret: [JSON]

実行してみた結果

ワークフロー起動

実際にActionsが動いてStoreに保存できたときの画面です。

スクリーンショット 2025-11-14 9.24.24

2ファイル変更検知しているけど、同期のスクリプトの方でマークダウンファイルしか対象にしていなかったためインポートされたのは1つでした。

これはActionsのワークフロー定義でフィルタリングする方が良いかも。。。

実際の検索

ドキュメントのサンプルコードを参考にして試してみました。

例:

// Ask a question about the file
  const response = await ai.models.generateContent({
    model: "gemini-2.5-flash",
    contents: "AWSマーケットプレイスで販売している製品を調べるには?",
    config: {
      tools: [
        {
          fileSearch: {
            fileSearchStoreNames: [ストアの一覧]
          }
        }
      ]
    }
  });

  console.log(response.text);

引用元の表示

https://ai.google.dev/gemini-api/docs/file-search?hl=ja#citations

Gemini APIのレスポンスからgroundingChunksを抽出して、どのファイルから情報が取得されたかを表示します。

function extractCitations(response) {
  const citations = [];

  if (response.candidates && response.candidates.length > 0) {
    const candidate = response.candidates[0];

    if (candidate.groundingMetadata?.groundingChunks) {
      for (const chunk of candidate.groundingMetadata.groundingChunks) {
        if (chunk.retrievedContext) {
          const context = chunk.retrievedContext;
          citations.push({
            title: context.title || 'Unknown',
            fileSearchStore: context.fileSearchStore || ''
          });
        }
      }
    }
  }

  return citations;
}

スクリーンショット 2025-11-14 9.41.48


GitHub Actions + Gemini File Search Storeで、メタデータの自動同期し、メタデータの検索ができないか試してみました。

この仕組みにより、自然言語で検索し、Geminiが適切なテーブルやカラムの情報を見つけて提示できるようになりました。

今後の目標:

  1. メタデータ検索の精度向上 - ドメイン知識の充実化、RAG用チャンク構造の改善
  2. SQL生成機能 - Geminiが見つけたテーブル・カラム情報からSQLクエリを自動生成
  3. SQL実行・結果表示 - BigQueryへの自動クエリ実行と結果表示

GitHub Actions,あんまり触ったことなかったけど面白いですな。

参考資料

この記事をシェアする

FacebookHatena blogX

関連記事