目次
Claudeに必要だったのは、iOSのタイムラインを300ポイントスクロールして止めることだった。シミュレーター上で指を動かし、終点で一時停止してから離した。
タイムラインは1,407ポイント動いた。
スクリーンショットも別の問題を引き起こしていた。個人用のMastodonクライアントを開発していた開発者は、実装を変更した後、Claudeにシミュレーターで確認するよう依頼していた。画面を次々に読み込むことで、コンテキストのかなりの部分が消費されているようだった。
99セッションを調査した結果、ワークフローを変えることになった。インターフェース要素とアプリケーションの状態はテキストで読み取り、画像は見た目の確認にだけ使う。そして、より挙動を予測しやすい画面操作・ドラッグツールをエージェントに提供する。
4つのベンチマークタスク全体で、画像の読み込み回数は27回から1回に減り、コンテキスト増加量は43%減少した。ただし、実行時間は34%長くなった。
この事例で参考になるのは、このトレードオフだ。スクリーンショットを減らすことで、コンテキストは大幅に節約できた。しかし、すべての操作が速くなったわけではない。
セッションログから分かったこと#
調査によると、99セッションで計2,125枚のシミュレーター画像がコンテキストに取り込まれていた。これにはスクリーンショットと、録画フレームから生成した画像が含まれる。
標準のスクリーンショットツールは、920 × 2,000ピクセルにリサイズした画像を返しており、1枚あたり約2,500トークンを消費していた。最も画像の多いセッションには176枚が含まれていた。そのセッションのコンテキストは約77万トークンに達し、半分以上を画像が占めていた。
ログからは、回避できる操作上の無駄も見つかった。
- タップ直後のBash呼び出しのうち900回以上は、1〜5秒の
sleepだけだった。 - スワイプとドラッグ892回のうち165回、割合にして18%は、待機せず直後にスクリーンショットを撮っていた。
直後に撮ったスクリーンショットには、画面遷移の途中が写っていることがあった。Claudeはその画面から座標を選ぶが、実際にタップする頃には画面が変わっていた。
今回の改善では、録画フレームの分析は対象外とした。画面遷移を調べるうえで、画像は今も有用だ。対象にしたのは、ボタンを探す、設定を開く、スクロールがどこで止まったか確認するといった日常的な画面操作だった。
座標と状態をテキストで読む#
主な追加機能は、画面のダンプをファイルに書き出すようアプリに指示する、デバッグ専用URLだった。
ダンプには、アクセシビリティ要素の種類、ラベル、中心座標に加え、タイムラインのcontentOffset.yなどのアプリケーション状態を含めた。Claudeは画像からタブを探す代わりに、次のような記述を読めるようになった。
画面: 静止済み(0.3秒)
ボタン "ホーム" 選択中 中心=(158,822)
ボタン "通知" 中心=(244,822)
状態: タイムライン
contentOffset.y: -116.0これにより、「どこをタップするか」と「操作後に何が起きたか」という2つの問いに直接答えられるようになった。
タップ座標には、各要素の表示されている部分の中心を使った。これは、一部だけが見えている行で重要になる。行全体のフレームの中心は、画面外やバーの下に位置することがあるからだ。
この実装では、アクセシビリティ対応を有効にしないと、UIKitが必要なラベルを設定しなかった。シミュレーターのcom.apple.Accessibility環境設定でApplicationAccessibilityEnabledを設定し、アプリを再起動すると、ボタン名とタブ名を取得できるようになった。
アクセシビリティ要素の生成には追加の処理負荷がかかる可能性があるため、開発者はアニメーションの引っかかりを計測するときには、この設定を削除している。
AXeやidbなどの外部ツールでも、インターフェース要素を読み取れる。今回は、依存関係を増やさずに内部状態も取得したかったため、ダンプ機能をアプリに組み込む方法が適していた。
動きが止まってから確認する#
テキストにするだけでは、古くなった座標の問題は解決しない。ダンプにも、インターフェースの動きが止まるまで待つ仕組みが必要だった。
最初の方法ではスクリーンショットを比較しており、ダンプ1回に約3秒かかっていた。代わりに、アプリ内部のアニメーションとスクロールの状態を調べ、対象となる動きが250ミリ秒間なくなるまで待つようにした。これにより、通常のダンプは約1秒で完了するようになった。
ただし、判定を複雑にする点が1つあった。iOS 26のタブバーにある_UILiquidLensViewが、位置に追従するアニメーションを保持していた。このアニメーションを判定に含めると、画面はいつまでも静止したと見なされなかった。
そこで、無限に繰り返すアニメーションと、計算上の継続時間が60秒を超えるアニメーションは判定から除外した。さらに、5秒のタイムアウトも設けた。
これは実用上の近似であり、画面の読み込み完了を保証するものではない。動きが止まった後で画像が届くこともある。エージェントは、期待する要素が存在するかを引き続き確認し、必要ならダンプを取り直さなければならない。
タイムラインのリアルタイム更新には、別の制御が必要だった。デバッグ用URLで新着投稿の反映を一時保留し、確認からタップまでの間に行がずれないようにした。保留を解除すると、待機していた新着投稿が挿入される。
見た目の確認にはスクリーンショットを残す#
テキストからアイコンの要素を取得することはできる。しかし、アイコンが正しく描画されたかどうかまでは分からない。
新しいワークフローでは、見た目を確認するときにxcrun simctl ioでスクリーンショットを撮り、デバイスのポイント単位の幅に合わせてリサイズし、JPEGで保存した。テストしたデバイスでは、その幅は402ピクセルだった。
標準のサイズでは、画像の1ピクセルがタップ座標の1ポイントに対応した。画面全体の画像に必要なトークン数は約470で、標準のスクリーンショットツールについて報告された消費量の約5分の1だった。小さなアイコンだけに切り抜けば、約50トークンまで減らせた。
切り抜いた画像の座標には注意が必要だ。座標の原点は切り抜き領域の左上になるため、タップする前に切り抜き位置のオフセットを加えなければならない。
実用上の使い分けは簡単だ。操作対象の位置と状態はテキストで確認する。見た目に関する問いには画像を使う。
精度を求める前にドラッグを直す#
1,407ポイントのスクロールを引き起こしたのは、毎秒約3,000ポイントの速さで指を300ポイント動かす操作だった。移動経路の末尾では同じ座標を繰り返し指定し、指を離す前の一時停止を表そうとしていた。
しかし、その一時停止では、慣性を確実に消すことができなかった。
追加の実験で、より明確な傾向が見えてきた。32ミリ秒間隔で20ポイントずつ、合計200ポイント動かすと、終点で300ミリ秒停止しても、リストは416ポイントスクロールした。
終点に微小な動きを加えると結果が変わった。最後の位置を16ミリ秒ごとに0.5ポイントずつ交互にずらすと、指を200ポイント動かした場合に、約180ポイントのスクロールで止まるようになった。
作業仮説は、touch_pathが連続する同一座標を移動イベントとして送っていない、というものだった。UIKitは、最後に実際に動いたときの速度を保持しているように見えた。終点で微小な動きを加えることで、はるかに低い速度の新しいイベントを送れると考えられた。
この仕組みは観測された挙動から推測したもので、ツールの実装を調べて確認したわけではない。
完成したドラッグ用ヘルパーは、32ミリ秒ごとに約20ポイントずつ移動し、その後、終点付近で16回の微小な動きを加える。一度の計測では、指を320ポイント動かすと、300ポイントスクロールした。
スクロールが始まるまでに吸収される初期移動量は、10〜80ポイントの間でばらついた。そのため、正確に位置を合わせるには、操作後にcontentOffset.yを読み取り、残りの距離を補正する必要があった。
ベンチマークで実際に測ったもの#
比較には、次の4つのタスクを使った。
- ダミーの画像投稿の3番目の添付画像を開き、ビューアー内で1回スワイプして閉じる。
- 時刻表示の設定を変更し、元に戻す。
- タイムラインを正確に300ポイントスクロールして止める。
- 一番上の通知のアイコンを目視確認し、スクリーンショットを保存する。
提供されたレポートでは、テストに使ったエージェントはOpus 5.5を使用するClaude Codeとされている。各条件は、1台のMacと1台のシミュレーターデバイスで、それぞれ3回実行する予定だった。計測時間にはサブエージェントの起動時間を含めていない。
| タスク | 従来の時間 | 新しい時間 | 従来の画像数 | 新しい画像数 | |---|---:|---:|---:|---:| | 画像ビューアー | 18秒 | 46秒 | 5 | 0 | | 設定 | 36秒 | 53秒 | 13 | 0 | | 正確なスクロール | 47秒 | 28秒 | 5 | 0 | | 通知の確認 | 18秒 | 32秒 | 4 | 1 |
主要な合計値は、各タスクの中央値を足したものであり、一連の操作を通して計測した値ではない。
| 指標 | 従来のワークフロー | 新しいワークフロー | |---|---:|---:| | 読み込んだ画像数 | 27 | 1 | | 画像の推定トークン数 | 約66,000 | 約50 | | コンテキスト増加量 | 約107,000トークン | 約61,000トークン | | 操作時間 | 119秒 | 159秒 |
重要な制約もある。従来のワークフローによる通知確認は、5回の試行中4回が、通知のスクリーンショットを読み込んだ直後にAPI側のエラーで停止した。原因は不明だったため、この条件には唯一成功した1回の結果を使った。
この結果は、このプロジェクトでの判断を裏付けるものだ。他のアプリやマシンにも当てはまる一般的な性能の見積もりではない。
コンテキストが減ったのに時間がかかった理由#
新しいワークフローは、画像ビューアー、設定、通知の各タスクで遅くなった。
ベンチマーク中のダンプ処理には、動きが止まるまで待つため、1.6〜5秒かかった。スクリーンショットは約0.4秒で返ってきた。また、観測されたモデルの判断時間も、約1〜2秒から4〜5秒へと増えた。理由は分からなかった。
従来のワークフローは、確認が早すぎることで、結果的に速くなる場合があった。別のスクリーンショットで古い画面情報をすぐに修正できれば、画像数や時折の再試行は増えるものの、全体としては早く終わることがあった。
正確なスクロールでは、このトレードオフが逆転した。新しいワークフローは、従来の47秒に対して28秒で完了し、3回とも1回のドラッグで済んだ。従来は2〜3回のドラッグが必要だった。
小さなモデルを選ぶ前に、インターフェースを改善する#
開発者は当初、Opusのコンテキストを節約するため、シミュレーターの操作をHaikuに委任することを検討していた。
従来のツールを使った初期テストでは、4タスク中3タスクで再試行が発生した。あるスクロールはわずか80ポイントで止まり、別の実行では違う設定を変更してしまった。新しいツールでは、Haikuは予備試行8回すべてに成功した。
サンプル数は少ない。それでも、モデルを選ぶ前にツール設計を見直す価値があることを示唆していた。
スクリーンショットをほぼ使わなくなると、Opusが直接操作しても、タスクごとの追加トークン数は約14,000〜16,000に収まった。委任すると、起動と指示の読み込みに3〜18秒かかり、さらに結果を報告する手順も増えた。開発者は直接操作を続け、ワークフローをAGENTS.mdに記録した。
自分のプロジェクトでも、繰り返し行うシミュレーター上のタスクを1つ選ぶところから始めてみよう。表示中の操作要素と、測定できる状態値を1つ含むテキストダンプを追加し、スクリーンショットを使うワークフローと、コンテキスト増加量、完了時間、正確さを比較する。スクリーンショットは、実際に目で見る必要のある確認のために残しておこう。
出典: https://zenn.dev/eai/articles/dont-let-agents-see-the-ios-sim-screen
目次
