大量のデータを蓄積していても、それを活かしきれていないと感じたことはありませんか。SQL の知識がないメンバーがデータにアクセスできない、分析結果を読み解くのに時間がかかる——こうした課題は、多くのチームが抱えている共通の悩みです。
Gemini API と Google BigQuery を組み合わせることで、自然言語による問い合わせからSQL自動生成、結果の要約・可視化までを一気通貫で実現できます。ここではPython を使った実装例を交えながら、AI 駆動のデータ分析パイプラインを構築する方法を丁寧に解説します。
この記事で学べること:
- Gemini API と BigQuery の連携アーキテクチャ
- 自然言語から SQL を自動生成する仕組み
- クエリ結果を Gemini で要約・インサイト化する方法
- 実運用に向けたセキュリティとコスト管理のポイント
対象読者: BigQuery の基本操作を理解しており、Gemini API で分析ワークフローを強化したい開発者・データアナリスト
前提知識と環境準備
必要なもの
- Google Cloud プロジェクト — BigQuery API と Vertex AI API(または Google AI Studio の API キー)が有効化されていること
- Python 3.10 以上 —
google-cloud-bigqueryとgoogle-genaiパッケージを使用 - BigQuery データセット — 分析対象のテーブルが存在すること(ここでは公開データセット
bigquery-public-data.google_analytics_sampleを例に使用)
パッケージのインストール
pip install google-cloud-bigquery google-genai db-dtypes pandasAPI キーの設定
import os
# Google AI Studio の API キーを使用する場合
os.environ["GOOGLE_API_KEY"] = "your-api-key-here"
# Vertex AI を使用する場合は、サービスアカウント認証を設定
# gcloud auth application-default loginアーキテクチャの全体像
Gemini × BigQuery データ分析パイプラインの基本フローは以下の通りです。
ユーザーの質問(自然言語)
↓
[Gemini API] スキーマ情報を参照して SQL を生成
↓
[BigQuery] SQL を実行し、結果を取得
↓
[Gemini API] 結果を要約し、インサイトを提示
↓
レポートまたはダッシュボードに出力
重要なのは、Gemini にテーブルスキーマの情報を渡すことです。スキーマを理解した上で SQL を生成するため、カラム名や型の誤りが大幅に減少します。
ステップ 1:BigQuery のスキーマ情報を取得する
まず、分析対象テーブルのスキーマを取得し、Gemini に渡すコンテキストを構築します。
from google.cloud import bigquery
client = bigquery.Client()
def get_table_schema(dataset_id: str, table_id: str) -> str:
"""テーブルスキーマを人間が読める形式で返す"""
table_ref = f"{client.project}.{dataset_id}.{table_id}"
table = client.get_table(table_ref)
schema_lines = [f"テーブル: {table_ref}"]
schema_lines.append(f"行数: 約{table.num_rows:,}行")
schema_lines.append(f"カラム:")
for field in table.schema:
nullable = "NULL許可" if field.mode == "NULLABLE" else "必須"
schema_lines.append(
f" - {field.name} ({field.field_type}, {nullable}): {field.description or '説明なし'}"
)
return "\n".join(schema_lines)
# 使用例
schema_info = get_table_schema(
"google_analytics_sample",
"ga_sessions_20170801"
)
print(schema_info)出力例:
テーブル: your-project.google_analytics_sample.ga_sessions_20170801
行数: 約170,366行
カラム:
- visitorId (INTEGER, NULL許可): 説明なし
- visitNumber (INTEGER, NULL許可): 説明なし
- visitId (INTEGER, NULL許可): 説明なし
- visitStartTime (INTEGER, NULL許可): 説明なし
...
ステップ 2:Gemini で自然言語から SQL を生成する
取得したスキーマをシステムプロンプトに組み込み、ユーザーの質問を SQL に変換します。
from google import genai
# Gemini クライアントの初期化
genai_client = genai.Client()
def generate_sql(question: str, schema: str) -> str:
"""自然言語の質問から BigQuery SQL を生成する"""
system_prompt = f"""あなたは BigQuery SQL のエキスパートです。
以下のテーブルスキーマに基づいて、ユーザーの質問に答える SQL クエリを生成してください。
{schema}
ルール:
- 有効な BigQuery Standard SQL のみを出力すること
- SQL クエリのみを返し、説明文は含めないこと
- LIMIT 句を適切に使い、大量のデータを返さないこと
- テーブル名にはプロジェクト名を含めること
- コメントは含めないこと
"""
response = genai_client.models.generate_content(
model="gemini-2.5-flash",
contents=question,
config=genai.types.GenerateContentConfig(
system_instruction=system_prompt,
temperature=0.1, # SQL生成は低温で正確性を重視
),
)
# コードブロックのマークダウンを除去
sql = response.text.strip()
sql = sql.replace("```sql", "").replace("```", "").strip()
return sql
# 使用例
question = "チャネル別のセッション数トップ10を教えてください"
sql = generate_sql(question, schema_info)
print(sql)生成される SQL の例:
SELECT
channelGrouping,
COUNT(*) AS session_count
FROM
`bigquery-public-data.google_analytics_sample.ga_sessions_20170801`
GROUP BY
channelGrouping
ORDER BY
session_count DESC
LIMIT 10SQL の安全性を検証する
生成された SQL をそのまま実行するのはリスクがあります。実行前に必ずドライランで検証しましょう。
from google.cloud.bigquery import QueryJobConfig
def validate_sql(sql: str) -> dict:
"""SQL をドライランで検証し、処理バイト数を返す"""
job_config = QueryJobConfig(dry_run=True, use_query_cache=False)
try:
query_job = client.query(sql, job_config=job_config)
bytes_processed = query_job.total_bytes_processed
return {
"valid": True,
"estimated_bytes": bytes_processed,
"estimated_gb": round(bytes_processed / (1024**3), 4),
}
except Exception as e:
return {"valid": False, "error": str(e)}
# 検証実行
result = validate_sql(sql)
print(result)
# 出力: {'valid': True, 'estimated_bytes': 148567232, 'estimated_gb': 0.1384}ステップ 3:クエリを実行し、Gemini で結果を要約する
検証を通過した SQL を実行し、その結果を Gemini に渡して分析させます。
import pandas as pd
def execute_and_summarize(sql: str, question: str) -> str:
"""SQL を実行し、結果を Gemini で要約する"""
# SQL 実行
df = client.query(sql).to_dataframe()
# データフレームを文字列に変換
result_text = df.to_string(index=False, max_rows=50)
# Gemini による要約
summary_prompt = f"""以下のデータ分析結果を、ビジネスパーソンにも理解できる形で要約してください。
## 元の質問
{question}
## 実行した SQL
{sql}
## 結果データ
{result_text}
以下の観点で要約してください:
1. 主要な発見(最も重要なデータポイント)
2. 注目すべきトレンドやパターン
3. 次のアクションとして考えられること
"""
response = genai_client.models.generate_content(
model="gemini-2.5-flash",
contents=summary_prompt,
)
return response.text
# 実行例
summary = execute_and_summarize(sql, question)
print(summary)出力例:
## 分析結果の要約
### 主要な発見
- Organic Search が最も多く、全セッションの約43%を占めています
- 次いで Social(約22%)、Direct(約18%)が続きます
### 注目すべきパターン
- 有料チャネル(Paid Search)のシェアは5%未満と低く、
オーガニック施策の効果が際立っています
- Referral トラフィックも10%程度あり、外部サイトからの
流入経路が確立されています
### 推奨アクション
- Organic Search の強みを維持しつつ、Paid Search の
ROI を検証する価値があります
- Social チャネルの詳細(プラットフォーム別内訳)を
深掘りすると、さらなる改善ポイントが見つかる可能性があります
ステップ 4:会話型のデータ分析エージェントを構築する
ここまでのコンポーネントを統合し、対話的にデータ分析を行えるエージェントを構築します。
class GeminiBigQueryAgent:
"""自然言語でBigQueryを操作する分析エージェント"""
def __init__(self, dataset_id: str, table_ids: list[str]):
self.bq_client = bigquery.Client()
self.genai_client = genai.Client()
self.schemas = self._load_schemas(dataset_id, table_ids)
self.history = []
def _load_schemas(self, dataset_id, table_ids) -> str:
"""複数テーブルのスキーマを一括取得"""
all_schemas = []
for tid in table_ids:
schema = get_table_schema(dataset_id, tid)
all_schemas.append(schema)
return "\n\n".join(all_schemas)
def ask(self, question: str) -> dict:
"""質問を受け取り、分析結果を返す"""
# 1. SQL 生成
sql = generate_sql(question, self.schemas)
# 2. 検証
validation = validate_sql(sql)
if not validation["valid"]:
return {
"error": f"SQL検証エラー: {validation['error']}",
"sql": sql,
}
# 3. コスト上限チェック(1GB以上は確認を求める)
if validation["estimated_gb"] > 1.0:
return {
"warning": f"推定処理量が {validation['estimated_gb']}GB です。実行しますか?",
"sql": sql,
}
# 4. 実行と要約
summary = execute_and_summarize(sql, question)
# 5. 履歴に追加
self.history.append({
"question": question,
"sql": sql,
"bytes": validation["estimated_bytes"],
})
return {
"question": question,
"sql": sql,
"summary": summary,
"estimated_cost_gb": validation["estimated_gb"],
}
# 使用例
agent = GeminiBigQueryAgent(
dataset_id="google_analytics_sample",
table_ids=["ga_sessions_20170801"]
)
result = agent.ask("デバイス別のコンバージョン率を教えてください")
print(result["summary"])実運用に向けたセキュリティとコスト管理
SQL インジェクション対策
Gemini が生成した SQL をそのまま実行するため、以下のガードレールを設けることを推奨します。
import re
FORBIDDEN_PATTERNS = [
r"\bDROP\b",
r"\bDELETE\b",
r"\bUPDATE\b",
r"\bINSERT\b",
r"\bCREATE\b",
r"\bALTER\b",
r"\bTRUNCATE\b",
]
def is_safe_sql(sql: str) -> bool:
"""読み取り専用の SQL のみ許可する"""
upper_sql = sql.upper()
for pattern in FORBIDDEN_PATTERNS:
if re.search(pattern, upper_sql):
return False
return upper_sql.strip().startswith("SELECT") or upper_sql.strip().startswith("WITH")コスト管理のベストプラクティス
BigQuery はスキャンしたデータ量に応じて課金されるため、コスト管理が重要です。
- ドライラン必須: 前述の
validate_sqlで推定コストを事前確認 - クエリ上限の設定:
QueryJobConfig(maximum_bytes_billed=1_000_000_000)で1GBの上限を設定 - パーティションの活用: 日付パーティションテーブルを使い、スキャン範囲を限定
- キャッシュの活用: 同一クエリは BigQuery の自動キャッシュから返されるため課金なし
# コスト制限付きクエリ実行
job_config = QueryJobConfig(
maximum_bytes_billed=1_000_000_000 # 1GB上限
)
df = client.query(sql, job_config=job_config).to_dataframe()まとめ
Gemini API と BigQuery の連携により、自然言語でデータ分析を行う仕組みを構築できます。SQL の専門知識がなくても、チーム全員がデータにアクセスし、インサイトを得られるようになるのは大きなメリットです。
本記事で紹介したアーキテクチャは、社内の分析ダッシュボードや Slack Bot への組み込みなど、さまざまな形で応用できます。まずは公開データセットで試し、自社のデータに展開していく進め方がおすすめです。
より高度なデータ分析エージェントの構築に興味がある方は、Gemini API × Python で AI データ分析エージェントを構築する でエージェント設計のパターンを詳しく解説しています。また、SQL 生成の精度をさらに高めたい方は Gemini で始める自然言語 SQL 変換ガイド も参考になるでしょう。