5月の連休明け、Mac の前に座ると Antigravity のウィンドウが静かに開いたままになっていました。前夜に仕込んだ Background Agent が、壁紙アプリ用の新しいアセット 12 点をフォルダにきれいに並べ、A/B 用のサムネイル、メタデータの下書き、ストア説明文の差分まで一つずつコミット待ちの状態にしていたのです。手を動かして「整えて」くれている感覚があって、その朝は珍しく、コーヒーを淹れるよりも先に画面を眺めていました。
私は個人開発で iOS / Android アプリを長く運営していて、ジャンルの中心は壁紙・癒し・引き寄せ系です。運営を続ける上で一番の負荷はコードよりも「毎日のアセット更新と申請周りの細かい作業」でした。この記事は、その夜間ルーチンを Antigravity の Background Agent に任せた3週間を、後から自分でも再現できる手順とコードに落とし込んだものです。所感だけでなく、タスク定義・重複検出・リサイズ・完了ゲートまで、実際に動かしている形をそのまま残します。
なぜ「夜間アセット更新」が最初の自動化対象だったのか
iOS と Android で並走している壁紙アプリの更新リズムは、私の場合だいたい次のような形に落ち着いていました。日中に新しいモチーフのスケッチや色味の検討を進め、夜に画像書き出しと命名規則のチェックを行い、翌朝にメタデータを整えてストアに反映する、というサイクルです。問題は、夜の書き出しと命名規則のチェックがほぼ毎日発生し、しかも自由度がほとんどない「単純だが間違えると怖い作業」だった点です。
「単純だが間違えると怖い」という性質は、エージェントとの相性が良いと以前から感じていました。判断要素が少ない一方で、人がやると注意力の摩耗で必ずどこかでミスが入る作業です。AdMob の収益が安定している間は、命名規則のずれや解像度違いを見逃した日が一日あるだけで、その夜の配信スロットを一日落とすことになります。深夜に手元でやり直す気力もなく、いつかは手放したいと考えていました。
自動化の対象を選ぶとき、私は「失敗しても致命傷にならず、かつ毎日発生し、判断の幅が狭い」という3条件で測ります。コードのリファクタリングは判断の幅が広すぎ、ストア説明文の最終調整は失敗が致命傷になりやすい。残ったのが、画像・テキスト・ストア用ファイルという「アセットの宿直」でした。最初の一歩としては、ちょうど良い手触りの作業だったと思います。
3週間で落ち着いたパイプラインの全体像
3週間で落ち着いた構成は、最初の設計よりかなり素朴になりました。Background Agent に時間枠を与え、その中で次の4つだけを順番に処理してもらっています。
- 前日に書き出した新規アセット 10〜20 点の解像度・色空間・ファイル名規則の検証
- iPhone / iPad / Android タブレットの解像度ごとへの一括リサイズと再書き出し(12 プロファイル)
- App Store / Google Play 用メタデータ草稿の生成(ja, en, zh-Hant, ko, de, fr, es, pt-BR, it の 9 言語)
- 朝 5 時時点の差分レポートを Markdown で 1 ファイルだけ生成し、リポジトリのトップに置く
最初は「失敗したらロールバックも自動で」と欲張りに設計しましたが、3日目には外しました。任せる仕事の線が複雑になるほど朝の確認に時間がかかり、結果として人間の負荷が逆に増えてしまったからです。今は「失敗していたら作業を止めて、朝の私に判断を委ねる」という、職人的にいえば「迷ったら手を止める」方針で安定しています。
道具を増やすより、任せる範囲を削るほうが効きました。自動化の初期は、足し算ではなく引き算で設計したほうが安定します。
タスク定義に「時間予算」を組み込む
Background Agent を放置運用するとき、最初に効いたのが「時間予算」という単一の概念でタスクを縛ることでした。タイムアウト・対象範囲・完了条件をばらばらに書くのではなく、1 つの枠として与えます。私が実際に使っている定義はこの形です。
# .antigravity/tasks/nightly-asset-update.yaml
task: nightly-asset-update
schedule: "0 22 * * *" # JST 22:00 起動
budget:
wall_clock: "7h" # 翌朝 5:00 までに必ず終える
hard_stop: "04:50" # これを過ぎたら処理中でも中断しレポート
scope:
source: "assets/wallpapers/incoming"
lookback_days: 30 # 重複検出は直近30日分のみを対象に
steps:
- validate_assets
- resize_profiles
- draft_metadata
- write_report
on_uncertain: halt # 迷ったら止める(自動ロールバックはしない)
report: "_logs/nightly/{{date}}.md"
wall_clock と hard_stop を別々に持たせているのは、起動時刻が多少ずれても「朝5時には必ず私が見られる状態になっている」という不変条件を守るためです。タスクが伸びても、04:50 を過ぎたら処理中の段階でレポートを書いて止まります。朝に未完成のレポートが置いてあるほうが、レポートが存在しないより圧倒的に扱いやすいのです。
on_uncertain: halt は、Background Agent のシステムプロンプト側でも繰り返し補強しています。「自信のない処理は実行せず、Needs Your Review に積んで次へ進む」と書いておくと、夜のうちに事故を広げず、朝の私が一括で判断できます。
知覚ハッシュによる重複検出
壁紙アプリでいちばん怖い事故は、ファイル名だけ違って中身が同じ画像を二重に配信してしまうことです。連番やカテゴリプレフィックスを手で付け替えていると、これは想像以上に起きます。Background Agent には、知覚ハッシュ(pHash)でこれを検出させています。
# scripts/detect_duplicates.py
from pathlib import Path
from datetime import datetime, timedelta
from PIL import Image
import imagehash
LOOKBACK_DAYS = 30
THRESHOLD = 5 # ハミング距離。5以下は「ほぼ同一」とみなす
def recent_assets(root: Path, days: int):
cutoff = datetime.now() - timedelta(days=days)
for p in root.rglob("*.png"):
if datetime.fromtimestamp(p.stat().st_mtime) >= cutoff:
yield p
def scan(root: Path):
hashes = {}
flagged = []
for path in recent_assets(root, LOOKBACK_DAYS):
h = imagehash.phash(Image.open(path))
for known_path, known_h in hashes.items():
distance = h - known_h
if distance <= THRESHOLD:
flagged.append((path, known_path, distance))
hashes[path] = h
return flagged
if __name__ == "__main__":
for dup, original, dist in scan(Path("assets/wallpapers")):
print(f"NEAR-DUP: {dup.name} ~ {original.name} (distance={dist})")
ここで LOOKBACK_DAYS = 30 にしているのには理由があります。最初の1週間、私は全期間の生成物をスキャンさせていました。すると Background Agent のタスク実行時間が日に日に伸び、3日目で4時間、5日目で6時間、7日目には8時間を超えて朝の処理が間に合わなくなりました。対象を直近30日に絞った瞬間、2時間程度に落ち着きました。長時間タスクは、対象を件数ではなく時間軸で区切る癖をつけると安定します。
閾値を 5 にしているのも実地調整の結果です。0 にすると完全一致しか拾わず、書き出し設定がわずかに違うだけの実質同一画像を取りこぼします。逆に 10 まで上げると、同じシリーズの色違いまで「重複候補」として上がってきて、朝の確認がノイズだらけになりました。自分のアセットの傾向に合わせて、この一行を調整するのが運用の勘所です。
12 デバイスプロファイルへの一括リサイズ
リサイズは、誰がやっても同じ結果になるべき作業の典型で、Background Agent の几帳面さがいちばん美しく効く部分です。デバイスごとの目標解像度を 1 つの辞書にまとめ、色空間を sRGB に正規化してから書き出します。
# scripts/resize_profiles.py
from pathlib import Path
from PIL import Image, ImageCms
PROFILES = {
"iphone-17-pro-max": (1320, 2868),
"iphone-17": (1206, 2622),
"iphone-se": (750, 1334),
"ipad-pro-13": (2064, 2752),
"android-1080p": (1080, 1920),
"android-1440p": (1440, 3120),
# ... 全12プロファイル
}
def export_all(src: Path, out_root: Path):
img = Image.open(src).convert("RGB")
for name, (w, h) in PROFILES.items():
resized = img.resize((w, h), Image.LANCZOS)
out_dir = out_root / name
out_dir.mkdir(parents=True, exist_ok=True)
resized.save(out_dir / src.name, "PNG", optimize=True)
return len(PROFILES)
このスクリプト自体は素朴ですが、Background Agent に任せる価値があるのは「12 プロファイル × 毎日 10〜20 点 = 200 前後の書き出しを、命名規則を一切ずらさずに毎晩続ける」という反復の部分です。人間がやると 200 回のうち必ずどこかで連番を飛ばすか、プロファイル名を取り違えます。ここは迷わず任せて良い領域だと、3週間で確信しました。
任せられる作業と任せられない作業の線引き
3週間運用して、自分の中ではっきり線が引けました。判断の軸はシンプルで、「ユーザーが受け取る体験のうち、人の手触りが残っていてほしい部分かどうか」です。配信する作品の選定や説明文の語尾には、私自身の体温が乗っていてほしい。逆に、ファイル名や解像度の整合は、誰がやっても同じ結果になるべき部分なので、Background Agent に任せた方が美しく仕上がります。
| 作業 | 判断 | 理由 |
| 12 プロファイル一括リサイズ | 任せる | 結果が一意。反復で人がミスする領域 |
| ファイル名のサニタイズ・連番整合 | 任せる | 規則が明確。几帳面さが価値になる |
| 知覚ハッシュによる重複検出 | 任せる | 機械のほうが網羅的かつ正確 |
| 色空間チェック(P3 / sRGB 混在防止) | 任せる | 目視より検査コードのほうが確実 |
| メタデータ草稿の多言語生成 | 草稿だけ任せる | 最終の語尾チェックは必ず人間 |
| 今日の配信スロットに入れる作品の選定 | 握る | 作品の体温が乗る一次判断 |
| ストア説明文の最終的な言い回し | 握る | 日本語の語尾の温度感は手放せない |
| レビュー返信文の送信 | 握る | 草稿は AI・送信前の最終確認は人間 |
この表は、3週間のあいだに何度か行き来しながら固まりました。最初は重複検出すら自分で見たくなりましたが、機械のほうが正確だと分かってからは手放せました。逆に、メタデータの語尾だけは、何度任せても最後に自分で触りたくなる。その感覚自体が、作品の輪郭を保つための大事な信号だと考えています。
朝の確認を 3〜5 分に収める Nightly レポート
夜間自動化を続ける上で、もっとも重要なのは「朝、自分が確認するときの負荷を増やさないこと」です。最初の1週間は、毎朝30分以上かかっていました。Background Agent の出力ログが長く、何を確認すべきかが分散していたためです。3週間目には、確認フローは次の Markdown レポート 1 枚にまとまりました。
# Nightly Report 2026-05-22
## Status
- Total assets processed: 18
- Resize variants generated: 216 (18 × 12 device profiles)
- Metadata drafts: 9 languages × 18 = 162 files
- Duplicates flagged: 1 (needs human review)
- Color space anomalies: 0
## Needs Your Review (今朝の判断が必要なもの)
1. assets/wallpapers/2026-05/aurora-014.png
- Detected as near-duplicate of aurora-009 (perceptual hash distance: 4)
- Action requested: Keep both? Replace? Skip?
## All Clear (確認不要)
- aurora-001 ... aurora-013, aurora-015 ... aurora-018: 規定通り処理完了
- Metadata drafts: 全件文体チェック通過(最終承認は人)
## Time
- Started: 22:03
- Completed: 04:48
- Total: 6h 45m
ポイントは「人の判断が必要なものだけを先頭にまとめる」ことです。すべてを並列に並べると、目が滑って結局全部を見直すことになります。Background Agent には、自信のある処理結果と迷った判断を分けて報告するよう、システムプロンプトでしつこく書き留めてあります。このレポート 1 枚で、朝の確認は 3〜5 分以内に収まるようになりました。旅先でも、iPad で開いて「あの画像はスキップで」と一言返すだけで翌日の配信が回っていく感覚は、運営を続けてきた中でも特別な手応えがあります。
3週間で遭遇した3つの落とし穴
実運用は美しい話ばかりではなく、当然うまくいかない夜もありました。同じ規模のアプリ運営をされている方の参考になれば嬉しいです。
1. タスクの実行時間が伸び続ける。 前述のとおり、全期間スキャンが原因で実行時間が 8 時間を超えました。対象を直近30日に絞り、lookback_days をタスク定義に明記することで解決しました。長時間タスクは時間軸で区切るのが基本です。
2. メタデータ草稿の文体ぶれ。 多言語草稿を任せた当初、日によって日本語の語尾の温度感がぶれていました。「ぜひお試しください」と「お試しいただけたら嬉しいです」が混在し、朝の修正時間が地味に長くなっていました。プロンプトに「過去30件のリリースノートを参照し、語尾の温度感を一定に保つ」と書き加えてから、ほぼ揃うようになりました。文体の基準を毎回プロンプトに具体的な参照元として持たせるのが効きます。
3. Mac のスリープと Background Agent の関係。 Mac が完全スリープに入ると、Background Agent のセッションも一緒に止まることがありました。私の運用では夜間だけ caffeinate -i -t 28800 を動かし、ディスプレイは消えるが CPU は眠らせない設定にしています。Apple Silicon の効率コアで動かす分には、電力消費もほぼ気にならない範囲でした。
# 夜間だけスリープを抑止(22:00 から 8 時間)
caffeinate -i -t 28800 &
完了ゲート — 「終わりました」を信じる前に
最後に、3週間でいちばん効いた仕組みを共有します。Background Agent が「処理が完了しました」と報告しても、その報告だけを信じないことです。完了の直前に、生成物そのものを検査するステップを必ず一つ挟みます。私はレポート生成の前段で、書き出されたファイル数とプロファイル数の整合を機械的に確認させています。
# scripts/completion_gate.py
from pathlib import Path
import sys
EXPECTED_PROFILES = 12
def verify(out_root: Path, source_count: int) -> bool:
expected = source_count * EXPECTED_PROFILES
actual = sum(1 for _ in out_root.rglob("*.png"))
if actual != expected:
print(f"GATE FAILED: expected {expected} files, found {actual}")
return False
print(f"GATE OK: {actual} files")
return True
if __name__ == "__main__":
ok = verify(Path("assets/output"), int(sys.argv[1]))
sys.exit(0 if ok else 1)
このゲートが落ちたときは、Background Agent はレポートを「未完成」として書き、私の判断を待ちます。「成功したと報告されたが実際には欠けている」という最悪のパターンを、この一段で防げます。放置運用で安心して眠るためには、エージェントの自己申告ではなく、生成物を数える検査を信じるのが結論でした。
次の1ヶ月では、レビュー返信草稿の品質向上と、AdMob の配信スロット最適化提案を、別の Background Agent タスクとして並走させる予定です。任せられる手仕事を増やすことで、私自身は新しい作品の構想に、もう少し集中できるようになりつつあります。同じように個人でアプリを長く運営している方が、夜の繰り返し作業を軽くする一助になれば幸いです。お読みいただきありがとうございました。