ブラウザ操作を AI に任せるとき、どの層に投資すると速いか
まとめ
- 決まった手順を繰り返すなら、Playwright のスクリプトが最も速く安い。実測で 1 タスク 0.16〜0.87 秒、LLM の呼び出しは 0 回だった。
- AI にブラウザを操作させるツールの差は、ドライバの性能ではなくモデルの往復回数で決まる。Playwright 系の browse は、生の CDP を使う browser-use より 2 倍遅かった。
- 探索は browser-use、固まった手順はスクリプト、夜間の反復はそのスクリプト。この 3 層に分けると、どの層も無駄にならない。
測ったこと
自宅の Ubuntu で、ブラウザ操作を AI に任せる手段を 3 つ用意した。
1 つは Playwright のスクリプト。モデルを介さず、決め打ちのセレクタで操作する。 2 つめが browser-use 1.0.2。モデルが Python を書き、生の CDP で操作する。MCP 経由なら追加の LLM は要らない。 3 つめが browse 0.0.16。こちらもモデルがコードを書くが、実行は Playwright 系のドライバ(Patchright)で、専用の拡張機能を入れたブラウザに繋ぐ。
テスト用に、ローカルで 5 ページのアプリを用意した。 JS で 800ms 後に描画される一覧、JS で動くフォーム、3 ページの遷移、cookie で守られたログイン、3 秒かかる応答。
AI に任せる 2 つは、同じモデル(DeepSeek V4.1 Flash)、同じプロンプト、同じデータで、5 ケースを 3 回ずつ回した。 合計 30 回。ブラウザはどちらも専用プロファイルの headless Chrome で、常用のプロファイルには触れていない。
同じタスクを 3 つの手段で測った結果
表1: 同一タスクの所要時間
| ケース | Playwright スクリプト | browser-use | browse | 呼び出し回数(browser-use / browse) |
|---|---|---|---|---|
| JS 描画後の値を取る | 0.87 秒 | 27.3 秒 | 125.4 秒(3 回中 1 回失敗) | 2.7 / 15.0 |
| JS のフォームを送信する | 0.16 秒 | 33.3 秒 | 75.1 秒 | 6.3 / 6.7 |
| 3 ページ遷移とスクリーンショット 3 枚 | 0.26 秒 | 67.8 秒 | 104.6 秒 | 9.3 / 13.3 |
Playwright の列は、私が書いたスクリプト 1 本を 1 回実行した時間で、モデルは介していない。3 ケース合わせて 1.3 秒。
5 ケース 15 回の通しでは、browser-use が 15 回すべて成功し、平均 38.9 秒。browse は 14 回成功で平均 78.4 秒だった。 1 回あたりの文脈消費は、browser-use が約 45 万トークン、browse が約 80 万トークン。
速さを決める往復回数と、スクリプトに替える時期
ブラウザ操作の速度を決めているのは、ドライバの速さではない。
browse は Playwright 系のドライバを使っているのに、生の CDP を叩く browser-use より遅い。 理由は往復の数にある。15 回の実行で、browser-use は 75 回、browse は 138 回モデルとツールが往復した。
モデルは 1 回ツールを呼ぶたびに、会話の文脈を丸ごと送り直す。 だから往復が 2 倍になれば、時間もトークンもほぼ 2 倍になる。ドライバを速くしても、この構造は変わらない。
browse が往復を重ねたのは、セッション名の解決と、スナップショットの読み直しだった。 加えて、OpenCode 側のコード実行ツールと browse の execute が同名で、モデルが入口を選び間違える場面があった。失敗した 1 回は、MCP ではなくシェルから CLI を叩き始め、引数を間違えて 31 回往復している。
スクリプトを書く手間と、AI に毎回任せる時間を比べる。
この記事の 3 ケースを書くのに、私は 3 分から 4 分かけた。 browser-use の 1 回が 27 秒から 68 秒なので、同じ操作を 3 回から 5 回やれば、書く手間は回収できる計算になる。
これは概算で、ケースの複雑さで動く。分岐や待ち条件が多いフローだと、スクリプトの執筆は 10 分を超える。その場合は 10 回前後が分岐点になる。 逆に、一度書いてしまえば実行の限界費用はゼロに近い。夜間に毎日回すなら、初日に書く方が安い。
ただし速さだけでは決められない。画面が変わり続けている開発中は、セレクタの保守が発生する。スクリプトは壊れるが、AI はその場で画面を見て操作を選び直せる。 壊れるコストが高い段階では AI、固まってからの反復はスクリプト、という順番になる。
見落としやすい非対称、権限
速さの比較だけを見ていると、権限の差を見落とす。
表2: 実行環境と権限
| 項目 | browser-use | browse |
|---|---|---|
| 実行されるコード | ユーザー権限の Python | 制限付きの JavaScript |
| 環境変数 | 読める | 読めない |
| ファイル | すべて読める | 作業ディレクトリと一時領域だけ |
| cookie | 生の CDP で全件 | セッションのページのホストだけ |
| 遷移先の制限 | MCP 経由では無い | セッションごとに指定できる |
browser-use のモデルは Python をそのまま実行するので、os.environ から API キーを読める。実際に読めた。 MCP サーバは OpenCode の子プロセスなので、OpenCode が持つ秘密をそのまま引き継ぐ。
この構成にしたとき、systemd のユーザー環境にすでに 3 つの API キーが入っていた。 そのため常駐ブラウザの unit は env -i で環境を作り直し、Chrome には秘密を渡さないようにした。
夜間のジョブも同じで、鍵を落とした環境から起動しないと、モデルが書いたコードに鍵が渡る。 ここは速度と関係なく、運用で塞ぐしかない部分になる。
運用に落とす
OpenCode の MCP に browser-use を登録し、専用プロファイルの headless Chrome を systemd で常駐させた。
"mcp": {
"servers": {
"bu": {
"type": "local",
"command": ["/home/takuya/.local/bin/browser-use", "--cli-mcp"],
"environment": { "BU_NAME": "opencode", "BU_CDP_URL": "http://127.0.0.1:9334" }
}
}
}ブラウザ側は、落ちても 3 秒で戻るようにしてある。 起動時に browser-use-smoke でバージョンと疎通を確かめ、失敗したらジョブをブラウザ起因の失敗として扱う。再試行してよい失敗と、内容の問題を分けるためだ。
browse は採用しなかったが、捨ててはいない。常用 Chrome のログインをそのまま使いたい場面では、cookie の読み出しをホスト単位に絞れる browse の方が安全だからだ。
3 層に分けると、それぞれの層が短い時間で終わる。
新しい画面を触るときは browser-use に任せる。セレクタを書かずに済み、画面を見ながら操作を選んでくれる。 操作が固まったら、その手順を Playwright のスクリプトに写す。モデルが書いたコードをそのまま資産にしてもよい。 夜間はスクリプトを回す。1 秒未満で終わり、失敗したときに原因がログに残る。
AI に任せたままにするのは、手順がまだ無い作業だけになる。 逆に言えば、手順があるのに毎回 AI に頼むのは、同じ設計を毎日やり直しているのと同じだった。