GEMINI LABEN
OUTAGE — Geminiが過去最大級の障害(error 1076/1099)から回復へ。エンジニアリングチームの緩和策で影響は縮小中DAILY-BRIEF — 新エージェント「Daily Brief」が登場。夜間にinbox・カレンダー・タスクを分析し、朝のパーソナルダイジェストを生成GEMINI-OMNI — Geminiと生成メディアモデルを統合した動画AI「Gemini Omni」。プロンプトから一貫性のある高品質動画を生成ENTERPRISE — Gemini Enterpriseで3.5 Flashが6/8からデフォルト固定に。機能管理トグルは廃止され全ユーザーで有効DEPRECATION — 画像previewモデル(3.1-flash-image/3-pro-image)は6/25に停止。GA版への移行はお早めにFILE-SEARCH — File Searchがマルチモーダル検索に対応。gemini-embedding-2で画像をネイティブに埋め込み・検索OUTAGE — Geminiが過去最大級の障害(error 1076/1099)から回復へ。エンジニアリングチームの緩和策で影響は縮小中DAILY-BRIEF — 新エージェント「Daily Brief」が登場。夜間にinbox・カレンダー・タスクを分析し、朝のパーソナルダイジェストを生成GEMINI-OMNI — Geminiと生成メディアモデルを統合した動画AI「Gemini Omni」。プロンプトから一貫性のある高品質動画を生成ENTERPRISE — Gemini Enterpriseで3.5 Flashが6/8からデフォルト固定に。機能管理トグルは廃止され全ユーザーで有効DEPRECATION — 画像previewモデル(3.1-flash-image/3-pro-image)は6/25に停止。GA版への移行はお早めにFILE-SEARCH — File Searchがマルチモーダル検索に対応。gemini-embedding-2で画像をネイティブに埋め込み・検索
記事一覧/API / SDK
API / SDK/2026-06-12中級

App Store 審査リジェクト対応に Gemini API を組み込む — 通知の構造化から Resolution Center 返信までの運用記録

App Storeの審査リジェクト通知をGemini APIで3層のJSONに構造化し、ガイドライン照合・Resolution Center返信ドラフト・提出前セルフチェックへつなげた個人開発の運用記録です。

Gemini API131App Store審査構造化出力7個人開発59アプリ運用

プレミアム記事

6本のアプリを並行してアップデートしていた今年の春のことです。朝いちばんに App Store Connect を開くと、Resolution Center に未読の通知が2件並んでいました。1件はスクリーンショットに関する Guideline 2.3.3、もう1件はプライバシー表記に関する 5.1.1 の指摘でした。アップデートの待ち行列が詰まっているときのリジェクトは、単純に手戻りが増えるだけではありません。「どのアプリの、どの指摘を、どの順番で処理するか」という交通整理そのものが負担になります。この時期は StoreKit 2 への移行や新しい端末解像度への対応をまとめて進めていたため、提出の頻度自体がふだんの数倍になっていて、審査まわりの事務作業が開発時間を目に見えて圧迫していました。

個人開発では、審査対応を代わってくれるチームメンバーはいません。私自身、長くアプリを運用してきた中でリジェクト対応そのものには慣れているつもりでしたが、複数アプリの並行アップデートが重なった時期に、この「読んで・調べて・返信して・直す」の一連の流れを Gemini API で部分的に自動化してみたところ、思っていた以上に効果がありました。ここでは、その仕組みと運用してみて分かった限界をまとめます。

リジェクト通知を「読む」作業がボトルネックだった

最初に、何が時間を食っていたのかを振り返っておきます。リジェクト対応の作業を分解すると、おおよそ次の4段階になります。

  1. 通知本文を読み、指摘事項を特定する
  2. 該当するガイドライン条文を確認し、何が求められているかを把握する
  3. 修正で対応するか、説明(返信)で対応するかを判断する
  4. Resolution Center への返信文を書く、または修正して再提出する

時間を計ってみると、私の場合は1件あたり平均90分ほどかかっていました。意外なことに、最も重いのは修正作業ではなく、1と2の「読む・調べる」の段階です。審査チームからの通知は定型文の中に固有の指摘が埋め込まれた構造をしていて、複数の指摘が1通にまとまっていることも珍しくありません。しかも指摘の粒度はまちまちで、「スクリーンショットを差し替えてください」のような明確なものから、「アプリの機能がガイドラインの要求を満たしているか確認してください」のような解釈の幅が広いものまで混在します。

通知が英語であることも、地味に認知負荷を上げます。読み飛ばしによる誤解は再リジェクトに直結するため、結局は一文ずつ精読することになり、その間ほかのアプリの作業は止まります。段階公開の進行管理や AdMob のレポート確認といった日々の運用タスクと並行していると、リジェクト1件で半日のリズムが崩れることもありました。この「読む・調べる」を機械に任せ、人間は「判断する・直す」に集中する、というのが今回の設計方針です。

通知本文を3層のJSONに構造化する

中核になるのは、リジェクト通知の本文を構造化データへ変換する処理です。Gemini API の構造化出力(response_schema)を使い、通知1通を「指摘単位」に分解します。設計したスキーマは次の3層です。

  • 通知レベル: アプリ名・サブミッションID・返信のみで解決し得るかのフラグ
  • 指摘レベル: ガイドライン番号・審査側の要求・対象(バイナリ/メタデータ/スクリーンショット)
  • 根拠レベル: 通知本文からの原文引用

3層目の「原文引用」を必須にしているのが、運用上いちばん効いているポイントです。要約だけを出力させると、モデルが通知に書かれていない要求を補ってしまったときに気づけません。原文引用を並記させておけば、引用元が本文に存在するかを機械的に検証でき、幻覚の混入を提出前に検出する仕組みになります。

次のコードは実際に使っている処理を整理したものです。何をするコードかを先に言うと、通知本文のテキストを受け取り、指摘単位のリストを含む Pydantic モデルとして返します。

from google import genai
from pydantic import BaseModel
 
class RejectionItem(BaseModel):
    guideline: str      # 例: "2.3.3"
    requirement: str    # 審査側が求めていることの要約
    evidence: str       # 通知本文からの原文引用(必須)
    target: str         # "binary" / "metadata" / "screenshot" のいずれか
    action: str         # こちらが取るべき具体的アクション
 
class RejectionReport(BaseModel):
    app_name: str
    submission_id: str
    items: list[RejectionItem]
    reply_only_candidate: bool  # 修正なしの返信だけで解決し得るか
 
client = genai.Client()
 
def parse_rejection(notice_text: str) -> RejectionReport:
    prompt = f"""以下は App Store の審査リジェクト通知です。
指摘を1件ずつ分解し、スキーマに従って整理してください。
制約:
- 通知に書かれていない事実を補わないこと
- evidence には必ず通知本文の原文をそのまま引用すること
- 判断に迷う項目は action に「要人間判断」と書くこと
 
---
{notice_text}
"""
    res = client.models.generate_content(
        model="gemini-3-flash",
        contents=prompt,
        config={
            "response_mime_type": "application/json",
            "response_schema": RejectionReport,
        },
    )
    return res.parsed
 
def verify_evidence(report: RejectionReport, notice_text: str) -> list[str]:
    """引用が原文に実在するかを検証し、不一致の指摘を返す"""
    broken = []
    normalized = " ".join(notice_text.split())
    for item in report.items:
        quoted = " ".join(item.evidence.split())
        if quoted[:80] not in normalized:
            broken.append(item.guideline)
    return broken

なぜこう書くのかについて、2点だけ補足します。まず、モデルは gemini-3-flash で十分でした。通知の分解は読解タスクであって推論タスクではないので、Pro 系を使う必然性がありません。月に数件の処理ならコストは誤差の範囲ですが、レイテンシが短いぶん Flash のほうが運用のテンポに合います。次に、verify_evidence を別関数として分けているのは、検証をモデルの自己申告に任せないためです。引用の実在チェックは Python の文字列照合で済む処理なので、確率的なモデルに頼らず決定的なコードで行います。生成と検証を分離する考え方は、このパイプライン全体を貫く原則になっています。

スキーマ設計で迷ったのは target と reply_only_candidate の2つのフィールドでした。target を「バイナリ/メタデータ/スクリーンショット」の3値に絞ったのは、後続の作業分岐がこの3つで決まるからです。バイナリ指摘なら再ビルドと再提出が必要で、メタデータ指摘なら App Store Connect 上の編集だけで済み、スクリーンショット指摘なら撮影環境の準備から始まります。分類の粒度を細かくしすぎると、モデルの判定ブレが増えるだけで作業分岐には寄与しませんでした。一方 reply_only_candidate は「修正なしの返信だけで解決し得るか」という楽観シナリオのフラグで、これが true の指摘から先に処理すると、待ち時間の長い再ビルドを挟まずに審査を前へ進められる場合があります。もっとも、このフラグはあくまで候補の提示として扱い、返信のみで行くかどうかの最終判断は条文を読んでから自分で下します。

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

この記事の続きを読む

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

この記事で得られること
リジェクト通知をguideline番号・根拠引用・対応アクションの3層JSONへ変換する gemini-3-flash の構造化出力スキーマと動くPythonコード
Resolution Center返信で押さえる3原則と、英語返信ドラフト生成プロンプトのBefore/After実例
過去リジェクト9件をNDJSONで蓄積し、提出前チェックで対応時間を1件あたり約90分→30分に短縮した運用手順
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

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

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

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

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

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

関連記事

API / SDK2026-05-25
Gemini API でアプリのローカリゼーションを自動QAする: 翻訳ドリフトを早期検出する構造化出力パイプライン
累計5,000万DLの個人開発アプリで多言語ローカリゼーションを運用するなかで見えた、翻訳ドリフトを早期検出する Gemini 2.5 Pro × 構造化出力の評価パイプラインを設計から実装まで整理します。
API / SDK2026-06-12
壁紙アセットの画像探しを File Search のマルチモーダル検索に任せてみた記録
数千枚の壁紙アセットから目当ての一枚を探す作業を、File Search のマルチモーダル検索(gemini-embedding-2)に置き換えられるか300枚で検証しました。カテゴリタグ運用との比較、つまずいた点、使い分けの結論を実装コードとともに残します。
API / SDK2026-06-04
AdMob レポートの判定は Gemini にやらせない — 構造化出力を「抽出」に限定する設計
AdMob レポートからフロア(eCPM 下限値)を決めるとき、Gemini に判定そのものをやらせるのは危険です。構造化出力は「乱れたレポートを型付きデータに抽出する」工程だけに限定し、しきい値判定は決定論的なコードに置く——その設計理由と実装を、実際の 42 グループ運用の判定ルールとともにまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →