ANTIGRAVITY LABEN
記事一覧/連携・プラグイン
連携・プラグイン/2026-05-23上級

Antigravity × UMP・ATT 同意率最適化エージェント — 地域別の同意 UX を週次で改善し AdMob 収益を底上げする自律ループ設計

iOS の ATT と Google UMP の同意率は AdMob の eCPM を 1.3〜2.0 倍動かす隠れた変数です。Antigravity のサブエージェントに地域別の同意 UX 実験を週次で回させ、6 アプリの ARPDAU を持ち上げた実装メモを書きます。

antigravity435admob16umpatt2consentmonetization15agents90

プレミアム記事

直近の 2 ヶ月ほど、6 つのアプリで運用している ATT(App Tracking Transparency)と Google UMP(User Messaging Platform)の同意 UX を改めて全部見直していました。理由はとてもシンプルで、AdMob の月次レポートを国別に並べた時に、同意率が 10 ポイント違うだけで eCPM が 1.3〜2.0 倍動いていることに、改めて目の前で観察できたからです。

私が個人開発を始めたのが 2014 年、累計ダウンロード数は 5,000 万を超え、現在も月収ベースで AdMob が事業の柱になっています。1997 年に 16 歳でインターネットに触れた時、海外のフォーラムの誰かが書いてくれたコードを読んで「国境を越えて誰かと一緒に作れる」と感動した記憶があるのですが、いま思えばその感覚は、地域ごとに異なる同意 UX を設計するときにも静かに効いています。同意は単なる法的なチェックではなく、その地域のユーザーとの関係の入り口なので、雑に作るとそのあとの収益・継続率の全てを引きずります。

この記事は、同意率を「観測 → 仮説 → 実装 → 検証」のループで毎週回すために、Antigravity のサブエージェントをどう責務分割し、どこまで自律で動かし、どこから人間に判断を渡したのか、という運用の足場をまとめたメモです。広告 SDK の組み込み手順や法務文言そのものは書きません。あくまで「すでに UMP・ATT を入れている個人開発者が、同意率を改善し続けるための機械学習的でないループを Antigravity に任せる」ための実装記録です。

同意率を AdMob 収益の主要 KPI として扱う理由

ATT で「許可しない」を選んだユーザーには IDFA が渡らず、AdMob は NPA(Non-Personalized Ads)配信になります。UMP で「同意しない」を選んだ EU 圏ユーザーも同様に NPA です。NPA の eCPM は、現場の感覚と公開ベンチマークを突き合わせると、パーソナライズ広告に対しておおむね 0.5〜0.7 倍に収束します。つまり同意率を 60% から 80% に持ち上げられれば、その国の広告収益はかなり乱暴に均すと 10〜15% 上振れする計算です。私の運用では、もっとも改善幅が出た地域で月次の AdMob 収益が約 12% 持ち上がりました。

同意率は技術的には測りやすい数字です。ATT は ATTrackingManager.trackingAuthorizationStatus、UMP は ConsentInformationconsentStatus をそのままイベントに送ればよく、難しいのは「何を比較対象にするか」と「比較を続けるか」のほうです。1 回の改善で 5 ポイント上げて満足してしまい、半年経って気付くと OS バージョンの変化や地域ロール変更で 8 ポイント下がっている、という展開を 2 度経験して以降は、同意率を季節変動のある KPI として扱い、Antigravity に週次で観測させる体制に切り替えました。

ステップ 1: イベント設計と BigQuery エクスポート

最初にやるのは、Antigravity が読める粒度でイベントを設計することです。ここで雑にすると、後段のサブエージェントがいくら賢くても判断材料が足りません。私のアプリでは下記の 6 つを Firebase Analytics に送り、BigQuery エクスポートに乗せています。

// Android (Kotlin) — UMP 同意状態の記録
private fun logUmpStatus(status: Int, geo: String) {
    val statusLabel = when (status) {
        ConsentInformation.ConsentStatus.OBTAINED -> "obtained"
        ConsentInformation.ConsentStatus.REQUIRED -> "required"
        ConsentInformation.ConsentStatus.NOT_REQUIRED -> "not_required"
        ConsentInformation.ConsentStatus.UNKNOWN -> "unknown"
        else -> "other"
    }
    firebaseAnalytics.logEvent("ump_status_settled") {
        param("status", statusLabel)
        param("geo", geo)                 // ISO-3166 alpha-2
        param("os_major", Build.VERSION.SDK_INT.toString())
        param("app_version", BuildConfig.VERSION_NAME)
        param("prompt_variant", currentVariantId)  // Remote Config から
    }
}
// iOS (Swift) — ATT 同意状態の記録
import AppTrackingTransparency
import FirebaseAnalytics
 
func logAttStatus(geo: String) {
    let status = ATTrackingManager.trackingAuthorizationStatus
    let label: String
    switch status {
    case .authorized: label = "authorized"
    case .denied:     label = "denied"
    case .restricted: label = "restricted"
    case .notDetermined: label = "not_determined"
    @unknown default: label = "other"
    }
    Analytics.logEvent("att_status_settled", parameters: [
        "status": label,
        "geo": geo,
        "os_major": String(ProcessInfo.processInfo.operatingSystemVersion.majorVersion),
        "app_version": Bundle.main.infoDictionary?["CFBundleShortVersionString"] ?? "",
        "prompt_variant": currentVariantId
    ])
}

イベント名は _settled で終えるのが個人的な好みです。prompt_shown だけだと表示と結果の対応が崩れやすく、最終確定状態に絞ったほうが後段の集計が安定します。geo は端末の locale ではなく、SDK 側で取得した推定地域を入れます。後述しますが、locale をそのまま地域として扱うと EU 圏判定で何度か事故りました。

BigQuery 側では下記のような正規化ビューを作ります。Antigravity のサブエージェントは生イベントではなく、必ずこのビュー経由で参照させると、後でイベント仕様を変えても影響範囲が限定的になります。

-- BigQuery: 同意率の地域×OS×バリアント集計
CREATE OR REPLACE VIEW analytics.consent_rate_weekly AS
SELECT
  DATE_TRUNC(DATE(event_timestamp), WEEK(MONDAY)) AS week_start,
  app_id,
  platform,                                    -- ios / android
  geo,
  os_major,
  prompt_variant,
  COUNTIF(event_name IN ('att_status_settled','ump_status_settled')) AS settled,
  COUNTIF(event_name = 'att_status_settled' AND status = 'authorized') AS att_yes,
  COUNTIF(event_name = 'att_status_settled' AND status = 'denied')     AS att_no,
  COUNTIF(event_name = 'ump_status_settled' AND status = 'obtained')   AS ump_yes,
  COUNTIF(event_name = 'ump_status_settled' AND status IN ('required','unknown')) AS ump_no
FROM analytics.events
WHERE _PARTITIONDATE >= DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY)
GROUP BY 1,2,3,4,5,6;

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
UMP / ATT の同意率を地域 × OS で正規化して BigQuery に貯めるイベント設計と、Antigravity のサブエージェントが扱えるデータ形にする具体手順
pre-permission 画面のコピー差分・表示タイミング・リトライ可否を Remote Config で実験し、Antigravity に PR 化させる週次ループの責務分割
ATT 拒否ユーザーを再プロンプトしてしまった事故、IDFV と locale の取り違え、EU 圏で見落としていた『同意の撤回』導線など、実装で踏んだ落とし穴の対処
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

連携・プラグイン2026-05-19
AdMob と AppLovin MAX を並走させて、Antigravity サブエージェントで日次比較する仕組み
2014年から AdMob で運用してきた壁紙アプリに AppLovin MAX を並走させ、Antigravity のサブエージェント群に eCPM・Fill Rate・ARPDAU を日次で比較させた90日間の実装メモです。広告ネットワーク横断の正規化と異常検知の閾値を中心に書きます。
アプリ開発2026-06-19
ATT を取る前に広告SDKを初期化していた — 初回起動だけ eCPM が下がる順序の罠
iOS の AdMob メディエーションを4本のアプリへ広げたとき、初回セッションだけ広告収益が落ちる現象に気づきました。原因は ATT 許諾と MobileAds 初期化の順序です。順序が効く理由と、Antigravity にその順序を監査させた実装記録をまとめました。
連携・プラグイン2026-06-26
自作 MCP サーバの巨大なツール結果でエージェントのコンテキストを溢れさせない設計
自作 MCP サーバのツールが大きな結果を一度に返すと、Antigravity のエージェントは静かに劣化します。フィールド射影・ページング・resource_link・出力予算で、コンテキストを溢れさせない実装を TypeScript の具体コードと実測値で示します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →