AIエージェントに広告レポートを書かせるとは
広告レポートの自動化には段階があります。Looker Studioやスプレッドシートで数値を自動集計する段階までは、多くの現場ですでに実現しています。この記事で扱うのは、その先です。数値の集計だけでなく、所感や考察までをAIエージェントに文章で書かせる自動化を解説します。
定型ツールとAIレポートは、得意な領域が異なります。ダッシュボードは決めた指標を決めたレイアウトで常時表示するのが得意です。一方でAIエージェントは、数値を取得したうえで「なぜその数字になったのか」を文章にできます。両者は競合せず、役割が違うと捉えるのが実務的です。
わかりやすい違いは、出力の粒度です。定型レポートは「先週のCPAは3,200円でした」で止まります。AIレポートは「先週のCPAは3,200円で、前週比18%の悪化でした。主因はA商品のキャンペーンで、CVが横ばいのまま費用が増えたためです」まで書けます。この考察部分こそ、これまで人が毎朝手で書いていた部分です。
筆者も日次の広告レポートをこの仕組みで運用しています。毎朝の数値確認と一次コメントをAIに任せ、人間は違和感のある箇所の深掘りとクライアントへの最終報告に集中する、という分担にしました。所感の下書きが自動で上がってくるだけで、朝の立ち上がりがかなり軽くなります。
ただし、この自動化には正直な限界もあります。AIが書く考察は、あくまで下書きです。数値の取り違えや、相関を因果と誤認した説明が混じることがあります。だからこそ後半で、数値ミスを前提にした品質管理をていねいに扱います。
全体アーキテクチャ
この仕組みは4つの部品でできています。データの置き場、数値を取って文章を書くエージェント、通知先、そして全体を定時に動かすスケジューラです。順番に一方向で流れるだけの、シンプルな構成です。
中心にいるのはClaude Codeです。エージェントがBigQueryへSQLを投げて数値を取り、その数値をもとに考察を書き、結果をレポートの文章として出力します。BigQueryへの接続は、後述するとおりターミナルからの bq CLIでも、MCP経由でも構いません。
スケジューラの役割は、この一連を毎朝や毎週の月曜に自動起動することです。人がターミナルを開かなくても、決めた時刻にレポートがSlackへ届く状態にします。各部品は疎結合なので、通知先をSlackからメールに変えるといった差し替えも容易です。
| 部品 | 役割 | この記事での実現手段 |
|---|---|---|
| BigQuery | 広告データの集約先 | 前提(別記事で構築) |
| Claude Code | 数値取得と考察の執筆 | claude -p のヘッドレス実行 |
| レポート生成 | 文章としての整形 | プロンプトでフォーマット固定 |
| Slack | 通知先 | Incoming Webhookへ投稿 |
| スケジューラ | 定時起動 | cron / タスクスケジューラ |
前提として必要なもの
この仕組みを組む前に、2つの土台が必要です。1つは広告データがBigQueryに集約されていること、もう1つはClaude Codeが導入済みであることです。どちらも本記事の対象範囲の外なので、まだの場合は先に整えてください。
広告データのBigQuery集約がまだの場合は、BigQueryで広告データを集約する方法をご覧ください。Google広告、Meta広告、LINEヤフー広告などのデータを1か所にまとめる手順を解説しています。集約が済んでいることが、この自動化の出発点です。
Claude CodeとBigQueryの接続方法は、BigQueryをAIエージェントに接続するにまとめています。MCP経由でBigQueryをエージェントに公開する手順や、読み取り専用にする安全設定を扱っています。本記事はこの接続ができている前提で進めます。
Claude Code自体のインストールは、次のコマンドで行います。利用にはClaude Pro(月20ドル)以上のプラン、またはAPIの従量課金が必要です。
npm install -g @anthropic-ai/claude-code
導入後、次のコマンドでBigQueryに接続できているかを、対話モードで一度試しておくと安心です。「テーブルの一覧を出して」といった簡単な質問で、エージェントが数値を取れる状態かを確かめます。
claude
ヘッドレスモードでレポートを生成する
自動化の鍵は、Claude Codeのヘッドレスモードです。claude -p "プロンプト" の形で実行すると、対話画面を開かずに、渡したプロンプトを1回実行して結果を標準出力へ返します。この非対話実行を使えば、スクリプトやスケジューラからレポート生成を呼び出せます。
claude -p "先週のGoogle広告のCPAをBigQueryから取得して要約して"
このコマンドは、エージェントがBigQueryへSQLを投げ、結果を要約し、その文章を標準出力へ返します。出力をファイルに保存すれば、あとはSlackへ送るだけです。自動化の骨格は、このヘッドレス実行の1行です。
レポートの品質は、プロンプトの設計でほぼ決まります。数値の羅列で終わらせず、考察まで書かせつつ、事実と意見が混ざらないようにする必要があります。筆者が実務で使っているプロンプトは、次の4つの原則で組み立てています。
第一に、期間と比較軸を明示します。「最近の調子」ではなく「直近7日と前週の同曜日7日を比較」と書くと、意図どおりのSQLになります。第二に、数値は必ずSQLで取得させ、推測での数字を禁止します。第三に、出力フォーマットを固定し、毎回同じ見出しと並び順にします。第四に、事実(数値)と考察(筆者の解釈にあたる部分)を別の見出しに分けさせます。
これらを反映したプロンプトの例が次です。実際の運用では、この本文を別ファイルに置き、テーブル名やプロジェクトIDは環境に合わせて差し替えます。
#!/usr/bin/env bash
set -euo pipefail
# 環境に合わせて設定(ハードコードしない)
: "${BQ_PROJECT:?プロジェクトIDを環境変数で指定してください}"
: "${BQ_DATASET:?データセット名を環境変数で指定してください}"
REPORT_FILE="$(mktemp)"
PROMPT=$(cat <<EOF
あなたは広告運用の日次レポートを書くアシスタントです。
以下の手順とルールに厳密に従ってください。
# 対象データ
- BigQueryのプロジェクト: ${BQ_PROJECT}
- データセット: ${BQ_DATASET}
- 広告実績のテーブルからSQLで数値を取得すること
# 集計条件
- 期間: 直近7日(昨日を含む)
- 比較軸: その前の7日(前週同期間)との対比
- 指標: 費用 / コンバージョン数 / CPA / CV率 を媒体別に
# 出力ルール
1. 数値は必ずSQLを実行して取得する。記憶や推測で数字を書かない。
2. 実行したSQLと、取得した集計結果の表を本文に併記する。
3. 出力は必ず次の見出し構成にする。
## サマリー(3行以内)
## 数値(実行SQLと結果の表)
## 考察(前週比の変化とその推定要因)
## 確認が必要な点
4. 「考察」は推定であることを明記し、事実(数値)と混ぜない。
5. 断定を避け、根拠のない原因特定はしない。わからない点は
「確認が必要な点」に回す。
EOF
)
claude -p "$PROMPT" > "$REPORT_FILE"
echo "$REPORT_FILE"
ポイントは、SQLと集計結果を本文に併記させている点です。これは後述する品質管理の要でもあります。数値の根拠が同じレポート内にあれば、人間があとから検算できます。考察はあくまで「推定」と明記させ、断定させないことも、そのまま信頼性につながります。
Slackへ自動投稿する
生成したレポートは、Slackへ投稿すると運用に乗せやすくなります。Slackには「Incoming Webhook」という仕組みがあり、決められたURLへJSONをPOSTするだけでメッセージを送れます。Botの常駐やトークン管理が不要で、通知用途には手軽です。
まずSlackの管理画面でIncoming WebhookのURLを発行します。このURLは、それを知る人なら誰でも投稿できる秘密情報です。コードに直書きせず、環境変数やシークレット管理の仕組みで扱ってください。次のコードは、前段で生成したレポートファイルを読み込み、SlackへPOSTするPythonの完全な例です。
#!/usr/bin/env python3
"""生成済みレポートをSlackのIncoming Webhookへ投稿する。"""
import os
import sys
import json
import urllib.request
import urllib.error
def post_to_slack(webhook_url: str, text: str) -> None:
"""テキストをIncoming WebhookへPOSTする。"""
payload = json.dumps({"text": text}).encode("utf-8")
req = urllib.request.Request(
webhook_url,
data=payload,
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=15) as res:
body = res.read().decode("utf-8")
if body != "ok":
raise RuntimeError(f"想定外の応答: {body}")
except urllib.error.HTTPError as e:
detail = e.read().decode("utf-8", errors="replace")
raise RuntimeError(f"Slack投稿に失敗(HTTP {e.code}): {detail}") from e
def main() -> int:
# URLはコードに直書きせず環境変数から取得する
webhook_url = os.environ.get("SLACK_WEBHOOK_URL")
if not webhook_url:
print("環境変数 SLACK_WEBHOOK_URL が未設定です", file=sys.stderr)
return 1
# 第1引数でレポートファイルのパスを受け取る
if len(sys.argv) < 2:
print("使い方: post_report.py <レポートファイルのパス>", file=sys.stderr)
return 1
report_path = sys.argv[1]
with open(report_path, encoding="utf-8") as f:
report_text = f.read().strip()
if not report_text:
print("レポートが空のため投稿を中止します", file=sys.stderr)
return 1
header = "*広告日次レポート*\n"
post_to_slack(webhook_url, header + report_text)
print("Slackへ投稿しました")
return 0
if __name__ == "__main__":
sys.exit(main())
このスクリプトは、レポートが空なら投稿を中止し、Slackからの応答が想定外なら例外を投げます。自動実行では失敗が気づかれにくいため、こうした異常時の停止を入れておくと安全です。Slack側は正常時に ok という短い応答を返すので、それ以外は失敗として扱います。
なお、Slackのメッセージには文字数の上限があります。長いレポートを送る場合は、サマリーだけを投稿して詳細はファイル添付やスレッドに回すなど、分割を検討してください。まずはサマリー中心の短いレポートから始めるのが扱いやすいです。
スケジュールで定時実行する
ここまでのレポート生成とSlack投稿を1本のスクリプトにまとめ、スケジューラに登録すれば自動化が完成します。日次なら毎朝、週次なら月曜の朝など、レポートを見たいタイミングの少し前に実行するのが基本です。
実行時刻でいちばん大事なのは、広告データのBigQueryへの反映が終わったあとに動かすことです。各媒体の連携が朝に走る構成であれば、その完了より後の時刻を選びます。反映前に実行すると、前日のデータが欠けたレポートが届いてしまいます。
登録の詳細な手順はOSごとに異なるため深追いしませんが、考え方だけ整理します。macOSやLinuxならcron、Windowsならタスクスケジューラが標準です。次はcronで毎朝8時に実行する場合のイメージです。
# 毎朝8:00に日次レポートを生成してSlackへ投稿
0 8 * * * /path/to/run_daily_report.sh >> /path/to/report.log 2>&1
| 環境 | 仕組み | 補足 |
|---|---|---|
| macOS / Linux | cron | crontab -e で登録。実行ログを残す |
| Windows | タスクスケジューラ | 日本語パスや相対パスの指定に注意 |
| 共通 | ログ出力 | 失敗の検知のため標準出力・エラーを記録する |
自動実行では、失敗しても人が気づきにくい点が最大の弱点です。実行ログを必ずファイルに残し、できれば失敗時にもSlackへ通知が飛ぶようにしておくと、静かに止まっていた事態を避けられます。
品質管理:AIの数値ミスを前提に設計する
ここが実運用でいちばん重要な部分です。AIエージェントは、それらしい文章を書く一方で、数値を誤ることがあります。SQLのミス、集計単位の取り違え、相関を因果と誤認した説明などです。だからこそ、AIの数値ミスを「起こりうる前提」として設計する必要があります。
第一の守りは、SQLと結果の併記です。プロンプトの原則2で「実行したSQLと集計結果の表を本文に載せる」と指示しました。これがあれば、考察の数字が怪しいと感じたとき、同じレポート内のSQLと表で検算できます。数値の根拠がブラックボックスにならないことが、信頼の土台です。
第二の守りは、判定ロジックの分離です。「CPAが前週比20%以上悪化したら警告」のような閾値アラートは、AIの考察に任せず、決定的なSQLやスクリプトで判定します。アラートの発火は、毎回同じ入力なら同じ結果になる必要があります。ここをAIに委ねると、日によって判定が揺れる恐れがあります。AIは考察の言語化に使い、白黒の判定は決めうちのコードに持たせる、という分担です。
第三の守りは、人間のレビューです。社内での定点観測ならAIの下書きをそのまま流しても実害は小さいですが、クライアントへ送る報告は必ず人が目を通してから送ります。とくに原因を断定している箇所と、大きく動いた数値は重点的に確認します。この一手間が、AIの誤りが外部に出ていくのを防ぐ最後の関門です。
運用に乗せる前に、次のチェックリストで自分の構成を点検してください。
- レポートに実行SQLと集計結果の表が併記されているか
- 数値の推測を禁止するプロンプトになっているか
- 閾値アラートはAIでなく決定的なスクリプトで判定しているか
- 考察が「推定」であると明示され、事実と分離されているか
- クライアント向けの出力に人のレビュー工程が入っているか
- 実行ログが残り、失敗を検知できる仕組みがあるか
- Webhook URLやプロジェクトIDを環境変数で扱っているか
筆者の実感として、この仕組みの価値は「人を置き換えること」ではありません。毎朝の数値確認と一次コメントという定型部分をAIに任せ、人は違和感の深掘りと最終判断に集中する、という分担にこそ価値があります。AIの考察を鵜呑みにせず、良い下書きとして扱う姿勢が、破綻なく続けるコツでした。
まとめ
BigQueryとClaude Codeを組み合わせ、考察まで書く広告レポートを自動化する方法を整理しました。
| ステップ | 内容 |
|---|---|
| 前提 | 広告データのBigQuery集約、Claude Code導入と接続 |
| 生成 | claude -p のヘッドレス実行、4原則のプロンプト設計 |
| 通知 | Incoming WebhookへJSONをPOST |
| 実行 | cron / タスクスケジューラでデータ反映後に起動 |
| 品質 | SQL併記・判定の分離・人のレビューの三段で守る |
この自動化の本質は、レポートの「数値の集計」から「所感の言語化」までを前に進めることです。一方で、AIの考察は下書きであり、数値ミスは起こりうるという前提は崩せません。まずは社内向けの日次レポートから小さく始め、SQL併記と人のレビューで精度を確かめながら、任せる範囲を広げていくことをおすすめします。
Looker StudioやGASを軸にした自動化の全体像は広告レポート自動化ガイドで扱っています。定型ダッシュボードとAIレポートは競合せず、常時参照はダッシュボード、所感つきの定点報告はAIレポート、という住み分けで併用するのが実務的です。