ログオフ
開発2026-08-11

AIエージェントに管理画面を操作させる:任せる範囲の決め方と、実務で踏んだ4箇所

スクリーンショットを人が撮って見せる往復をやめ、ブラウザを直接読ませる。ただし任せる範囲を決めないと、見ていない画面を「確認しました」と報告してくる。線引きと、実際に詰まった4箇所を書く。

外部サービスの管理画面での設定を、人がスクリーンショットを撮ってAIに見せ、返ってきた指示を人が実行する形でやると、異常に遅い。ブラウザを直接読ませれば往復は消える。ただし任せる範囲を決めておかないと、今度は見ていない画面を「確認しました」と報告してくる。

1. 線引きは「不可逆かどうか」で切る

事象

どこまでAIにやらせるかを決めずに始めると、毎回その場で迷う。全部止めれば往復が残って遅いままだし、全部任せると取り返しのつかない操作まで走る。

原因

「危なそうかどうか」で切ろうとするのが原因だ。危なさは主観なので毎回ぶれるし、判断のたびに手が止まる。

解決策

人間に残すのは、ログインと、取り消せない操作の承認だけにする。 それ以外(画面を読む・値を入力する・保存する)はAIがやる。「不可逆かどうか」で切ると迷わない。

  • 止める:IDや識別子の確定、公開、送信、削除、課金の実行
  • 進める:あとから変えられる設定の保存、表示の確認、値の読み取り

認証情報そのものをAIに触らせないのは別枠で固定する。ログインは人が済ませた状態から始める。

2. 「見ていないのに見た」と報告してくる

事象

スクリーンショットを取得するツールが、途中で切れたデータや壊れた画像を返す環境があった。その状態でエージェントに操作を任せると、画面の中身・コマンドの結果・タップの結果を実際には確認しないまま、それらしい報告を書いてくる。

実害として出たのは入力値の取り違えだった。別の項目を選んでいたのに「正しく入りました」と報告された。

原因

ツールが黙って壊れると、成功の「形」だけが返る。中身が空でも、呼び出し自体はエラーにならないので先へ進める。人が横で画面を見ていないと気づけない。

解決策

見る手段を、壊れないものに固定して閉ループにする。

まず、画面のキャプチャはアプリ側のUIツールではなくOS標準のコマンドでファイルに保存し、画像として読み直す。ツール内での表示に依存させない。

xcrun simctl io booted screenshot /tmp/shot.png   # 保存してから読む

次に、画面の状態判定は見た目ではなく公式APIで照会する。「登録できたか」を管理画面のスクリーンショットで判断せず、APIでレコードの一覧を引いて確認する。

シェルの出力は区切りの目印(sentinel)で挟み、その内側だけを事実として扱う。前のコマンドの出力が混ざったまま読まれる事故がこれで消える。

echo "S_START"; <実行したいコマンド>; echo "S_END:$?"

そのうえで、状態が確実に読めないうちは不可逆な操作をエージェントにやらせない。1の線引きと同じ結論に着く。

3. 押しても何も起きないリンクの正体は window.open

事象

管理画面のメニュー項目をクリックしても何も起きない。hrefjavascript:void(0) かハッシュだけで、遷移先が読めない。クリック自体は成功している。

原因

正体は window.open によるポップアップだった。別ウィンドウとして開くので、自動化ツールのタブ一覧に現れないことがある。画面上も何も変わらないので、「クリックが効いていない」ようにしか見えない。

解決策

window.open を一時的に差し替えて、渡されるURLを捕まえる。関数の中身を読むより速い(そもそも読めないこともある)。

let captured = null
const orig = window.open
window.open = (u) => { captured = u; return { focus(){}, close(){} } }
// ここで対象をクリック
window.open = orig
console.log(captured)

捕まえたURLは、クエリを落とさず丸ごと location.assign で開く。OAuth の state がクエリに入っていることがあり、削ると認証に失敗する。

遷移が数秒かけて別ホストへリダイレクトすることもあるので、直後のキャプチャでは「変わっていない」ように見える。タブのURLを少し後で見直すと着地している。着地したら本体のURLを控えて、次回からは直接そこへ行く。

4. どのウィンドウを操作しているか確定させる

事象

ブラウザやプロファイルを複数つないでいると、接続名が Browser 1 Browser 2 のように汎用的で、どれがどれか判別できない。用途を分けている場合、取り違えると別のアカウントで操作してしまう。

原因

自動化ツール側の接続名は、ブラウザの表示名やプロファイル名と一致しない。

解決策

各ブラウザで myaccount.google.com を開き、ログイン中のアカウントを読めば特定できる。接続時に名前を付けられる仕組みなら、その場で付けておくと次回から一発で選べる。

macOS なら、そもそも狙ったプロファイルだけを開くランチャーを作っておくほうが早い。osacompile.app にすれば1クリックで直接開く。

osacompile -o ~/Applications/ChromeWork.app \
  -e 'do shell script "open -na \"Google Chrome\" --args --profile-directory=\"Profile 3\""'

--profile-directory に渡すのはディレクトリ名(Default / Profile 2 …)で、画面に出ている表示名ではない。対応は Local Stateprofile.info_cache を読めばわかる。

jq -r '.profile.info_cache | to_entries[] | "\(.key)\t\(.value.name)"' \
  ~/Library/Application\ Support/Google/Chrome/Local\ State

細かい詰まりどころ

画面を実際に運転させると、記事にしにくい細かさで詰まる。繰り返し出たものだけ挙げる。

  • フォームの値を直接注入しても反映されないことがある(React 等では成功したように見えて空のまま)。打鍵に切り替える
  • 入力のたびにレイアウトがずれる。エラー文の出現と消滅で数十px動くので、座標は毎回取り直す
  • APIキーのような文字列はカーソルが文字に被る。拡大して読む。1文字違えば動かない
  • タブを増やさなければ後片付けは最後に1枚閉じるだけで済む
  • エラーメッセージの言い分を鵜呑みにしない。 「ファイルの中身が違う」と言われて中身を疑ったら、実際はそのファイルが404だったことがある。画面を読む前に curl や公開APIで実測するほうが速い

線引きを「不可逆かどうか」に置いてから、この種の設定作業の往復はほぼ消えた。人の判断が要るのはログインと最後の一押しだけで、残りは読んで入れて保存するだけの単純作業になる。単純作業だからこそ、見たかどうかを本人の自己申告に任せない仕組みのほうが要る。

関連記事

開発 一覧へ