LLMエージェントによるブラウザ自動化の Self-Healing 実験

スパイスコードの民谷です.

CTO櫻木の以前の検証記事では, LLM エージェントによるブラウザ操作自動化において, 操作ログから Playwright コードを生成して「チェーン」として機械的に再生する方式が効率的であることを示しました. チェーン方式は毎回UIを探索する方式に比べて 3.4 倍高速・費用が 1/5 と大きな効率差がありますが, 一方で, チェーンは Web サイトの DOM セレクタに依存しており, サイト更新でセレクタが変わるとチェーン全体が壊れるという課題があります.

本記事では, その解決策として, LLM エージェントによるチェーンの自動修復, すなわち Self-Healing の実現可能性を検証しました. ここでの Self-Healing とは, 実行失敗したチェーンのセレクタを LLM エージェントが事後的に修正・再登録するプロセスを指します.

検証は3段階で行い, 合計260回の実験を実施しました. 結論として, シンプルアプリでは99%の修復成功率を達成し, 本番の React アプリケーションでも条件次第で80〜100%の成功率を確認しています.

  • 実験1(200回): シンプルな検証用アプリで, 5種類のDOM変異 × 2モデル(Sonnet / Haiku)× 各20回
  • 実験2(20回): 本番の React 管理画面で, 4種類のDOM変異 × 各5回
  • 実験3(40回): プロンプト設計の改善で成功率が上がるかを検証する要因実験

本記事の Self-Healing は, LLM エージェントが環境変化に適応する「Self-Evolving Agents」[*1] の GUI ドメインにおける実践的な取り組みと考えています.

1. はじめに

1.1 手続き記憶としてのチェーンとその課題

graph TD
    LTM["長期記憶<br/>(Long-term Memory)"]
    DM["陳述記憶 / 宣言的記憶<br/>(Declarative Memory)"]
    NDM["非陳述記憶 / 非宣言的記憶<br/>(Non-declarative Memory)"]
    EM["エピソード記憶<br/>(Episodic Memory)<br/><i>個人的な体験の記憶</i>"]
    SM["意味記憶<br/>(Semantic Memory)<br/><i>一般的な知識・事実</i>"]
    PM["手続き記憶<br/>(Procedural Memory)<br/><i>スキル・手順の記憶</i>"]
    OTHER["プライミング, 条件づけ 等"]

    LTM --> DM
    LTM --> NDM
    DM --> EM
    DM --> SM
    NDM --> PM
    NDM --> OTHER

    style PM fill:#f3e5f5,stroke:#7b1fa2,stroke-width:3px

Chain機構と手続き記憶の記事で扱った手続き記憶は「無意識な手続き」に関する記憶であり, LLM の推論をほぼ介さずに高速・低コストで再実行できる知識です. チェーン(2.1節)はこの手続き記憶の一形態です.

通常時はチェーンが無意識的に再生されますが, Web サイトの DOM 構造が更新されてセレクタが変わると, 実行中に予期しないエラー(サプライズ)が発生します. 同記事では, このサプライズを検知した際に, 無意識的な手続き実行から意識的な推論(宣言的記憶)へフォールバックする二重過程の設計を示しました. 本記事では, この意識的フローの実体, すなわち Healer による壊れたチェーンの自動修復 — Self-Healing — の実現可能性を検証します.

1.2 検証ポイント

本記事で検証する問いは以下の3つです.

  1. シンプルアプリと本番Reactの両方で修復は成功するか? — 検証用の小さなアプリで得た結果は, 本番環境でも再現するのか
  2. モデル(Sonnet / Haiku)で実用的な差はあるか? — 低コストモデルと高性能モデルで成功率・費用にどの程度の差があるか
  3. プロンプト設計の改善で成功率はさらに上がるのか? — Healerの基本的な修復力はどの程度か. プロンプトを工夫すれば改善するのか

結論を先に述べると, シンプルアプリでは Sonnet が 99%の修復成功率 を達成し, 本番の React アプリでも変異の種類次第で 80〜100% の成功率を確認しました. モデル間では Sonnet 99% に対して Haiku 75% と明確な差があり, 実ページを確認する Sonnet のアプローチが優位でした. さらにプロンプトの構成要素を変えた要因実験では, 全4パターンが 90〜100% の成功率(各 n=10)を達成し, デバッグ手順や推論プロセスの追加は成功率を改善せず費用のみ最大+55%増加しました. 全実験を通じて修復の成否を最も強く分けたのは, エージェントが実ページの DOM 構造を確認するか否かでした.

2. システム概要

2.1 チェーンとは

本記事で扱う「チェーン」とは, ブラウザ操作ログから自動生成された Playwright コードの JSON 定義です. 各ステップにセレクタと操作内容が記録されており, エージェントを介さず機械的に再生できます. 詳細は以前の検証記事を参照してください.

2.2 アーキテクチャ

アーキテクチャ

壊れたチェーンを修復する専用エージェント BrowserChainHealerAgent(以下, Healer)を構築しました. Healer は LangChain の create_react_agent で構築した ReAct パターン(Thought → Action → Observation を繰り返す)のエージェントであり, ツールの呼び出し順序は LLM が自律的に決定します.

2.3 使用ツール一覧

Healer は以下のツールを組み合わせて修復を行います.

カスタムツール(Healer専用)

ツール 役割
load_browser_source_profile ブラウザプロファイル(Cookie, 認証状態等)の復元
get_chain_blueprint 壊れたチェーン定義(JSON)の取得
submit_fixed_chain 修復チェーンの保存・登録

Playwright MCP ツール(ブラウザ操作) — Playwright MCP を介することで, LLM エージェントがブラウザ操作をツールとして呼び出せます.

ツール 役割
browser_navigate 指定URLへの遷移
browser_snapshot ページのアクセシビリティスナップショットを取得
browser_run_code Playwrightコードの直接実行(セレクタの個別検証に使用)

※ Playwright MCP では30種類以上のツールが利用可能ですが, 今回の修復タスクでは上記3つが主に使用されました.

2.4 想定する修復フロー

プロンプトでは以下のフローを想定して指示を設計しました.

ステップ 内容 使用ツール
1 ブラウザ状態の準備 load_browser_source_profile
2 壊れたチェーン定義を取得 get_chain_blueprint
3 実ページでDOM構造を確認し, セレクタを個別に検証 browser_navigate + browser_snapshot / browser_run_code
4 セレクタを修正 LLMが修正済みJSONを生成
5 修正チェーンを保存 submit_fixed_chain

2.5 プロンプト設計の概要

プロンプトの主な指示は以下の通りです.

  • 修復プロセス: プロファイル読み込み → チェーン取得 → ページ構造を確認 → セレクタ修正 → テスト → 提出
  • セレクタの優先順位: テキストマッチ → ID → data属性 → class
  • 効率性: 修正コードは1回で完成させ, browser_run_code を試行錯誤のために何度も呼び出さないこと(ただし, 実験3で追加する F2: デバッグ手順ではステップバイステップの検証を優先するため, この制約は緩和されます)
  • 提出: 修正が完了したら submit_fixed_chain で保存する

実験1・2では同一のプロンプト(上記の基本構成にセレクタ優先順位とデバッグ手順を含む)を使用しました. 実験3ではプロンプトの構成要素を要因として分解し, 各要素の効果を検証しています(詳細は5章で説明します).

3. 実験1: シンプルアプリでの基礎検証

まず, Self-Healing が原理的に機能するかを確認するため, シンプルな検証用アプリを対象に200回の実験を行いました.

3.1 対象アプリ

検証用に3ページ構成のシンプルなWebアプリケーションを用意しました.

  • ページ構成: login → search → detail
  • 操作ステップ数: 10ステップ
  • フォーム要素数: 5〜10

3.2 DOM変異パターン(5種類)

実際のWebサイト更新で起こりうる5種類のDOM変異パターンを用意しました.

Variant 変異内容
Variant 1: ID変更 #username#user-input
Variant 2: 要素階層移動 ラッパー <div> の追加
Variant 3: テキスト変更 「ログイン」→「サインイン」等
Variant 4: 属性変更 ID削除, data-testiddata-qa
Variant 5: 複合変異 ID/data属性すべて削除, テキスト・階層も変更

Before/After例(Variant 1: ID変更):

<!-- Before -->
<input type="text" id="username" placeholder="ユーザー名">
<button id="login-btn">ログイン</button>

<!-- After -->
<input type="text" id="user-input" placeholder="ユーザー名">
<button id="signin-button">ログイン</button>

各 Variant の DOM 変更が実際にチェーンを壊すことは, 変更前の正常なチェーン定義を各 Variant の DOM に対して直接実行し, セレクタが見つからず失敗することで事前に確認しています.

3.3 実験条件

  • 試行数: 5パターン × 2モデル × 各20回 = 合計200回
  • 実行環境: AWS Bedrock(ap-northeast-1)
  • トレース: 各試行を Langfuse でトレースし, トークン数・費用を記録
  • 成功判定: エージェントが提出した修復済みチェーンを新規ブラウザセッションで再実行し, 全ステップが正常に動作した場合を成功と判定

3.4 使用モデル

ブログ表記 モデルID 世代
Haiku claude-3-haiku-20240307 Claude 3(2024年3月)
Sonnet claude-sonnet-4-20250514 Claude 4(2025年5月)

なお, 両モデルは世代が異なるため(Claude 3 vs Claude 4, 約1年の差), 純粋なモデルサイズの比較ではなく, 現時点で利用可能な低コストモデルと高性能モデルの実用比較として位置づけています.

3.5 結果: 成功率

Model Variant 1 (ID変更) Variant 2 (階層移動) Variant 3 (テキスト) Variant 4 (属性) Variant 5 (複合) 全体
Haiku 90% 100% 100% 85% 0% 75%
Sonnet 100% 95% 100% 100% 100% 99%

Sonnet 99%(100回中99回), Haiku 75%(100回中75回)で, モデルによって大きな差が出ました. 特に Variant 5(複合変異)では Haiku が 20回中0回成功と完全に失敗しています.

Sonnet の唯一の失敗は Variant 2(階層移動)で発生しました. 他の19回の成功試行が browser_run_code 内に Playwright コードをまとめた2ステップのチェーンを生成したのに対し, この1回では Playwright MCP の高レベルツール(browser_type / browser_click)を個別に呼び出す12ステップのチェーンを生成していました. これらのツールは操作対象を ref(アクセシビリティスナップショットの動的ID)で指定しますが, エージェントはロケータ式を ref に記述しており, 実行時にエラーとなりました. セレクタの修正自体は正しかったものの, チェーンの出力形式を誤ったケースです.

※ 各条件は20回の試行であり, Variant単位の成功率は±10〜15ポイント程度の変動がありうる点にご留意ください.

3.6 ツール使用パターンの差

成功率の差は, 修復アプローチの違いから生じています.

指標 Haiku Sonnet
browser_run_code 使用回数 100試行中99試行で0回 平均7.6回
browser_navigate 使用回数 全試行で0回 全試行で1回
平均所要時間 ~39s ~91s
  • Sonnet: 「実ページを見て直す」アプローチ. 実際のページにブラウザで遷移し, browser_run_code でセレクタを個別に試して動作を検証(平均7.6回). 確認済みのセレクタで修正チェーンを組み立ててからテスト.
  • Haiku: 「チェーン定義だけで直す」アプローチ. 壊れたチェーン定義を読み, セレクタの修正案を一度に生成. 実際のページを開かずに, 修正済みチェーンをそのまま提出.

Sonnet は実ページを確認することで修復精度が高まっています. 一方, Haiku はプロンプトで指示したブラウザ確認ステップを省略し, チェーン定義の文字列情報のみから修正を試みる傾向がありました. Variant 5(複合変異)では ID・data属性・テキストがすべて変更されるため, 実ページを確認しない Haiku は有効なセレクタを特定できず全件が失敗しました.

所要時間の分布

モデル 平均 標準偏差 最小 最大 成功件数
Haiku 39.5s 3.6s 33.3s 48.7s 75/100
Sonnet 91.1s 5.3s 74.4s 108.0s 99/100

※ 所要時間は実行検証で成功した試行のみの統計値

3.7 費用比較

モデル 1回あたり費用 成功率 成功1回あたり費用 所要時間
Haiku $0.016 75% $0.022 39s
Sonnet $0.54 99% $0.55 91s

※「1回あたり費用」は全試行(成功・失敗含む)の平均値. 「成功1回あたり費用」は「1回あたり費用 ÷ 成功率」で算出

Haiku は費用面で約25倍安価ですが, 複合変異(Variant 5)では成功率0%であり, DOM変異の種類を事前に予測できない実運用では信頼性に欠けます. Sonnet は1回あたりの費用は高いものの, 99%の成功率で安定して修復できるため, 実運用では Sonnet が現実的な選択肢です.

3.8 実験1の結論

  • Sonnet は5種類のDOM変異パターンに対して 99%の修復成功率 を達成. Self-Healing は原理的に機能する
  • Haiku は75%にとどまり, 特に複合変異では全件失敗. 実ページを確認しないアプローチの限界
  • 以降の実験はすべて Sonnet で実施する

ただし, この実験はシンプルな3ページ・10ステップのアプリを対象としています. 本番の React アプリケーションでも同様に修復できるのかが次の問いです.

4. 実験2: 本番Reactアプリでの予備検証

実験1でSonnetによるSelf-Healingの基本的な有効性を確認しました. 次に, 本番に近いReactアプリケーションでも修復が機能するかを予備検証します.

4.1 対象アプリ

Next.js + Material-UI(MUI) + ReactFlow で構築された管理画面を対象にしました. 具体的には, オートメーション作成フォーム(条件分岐の設定, タグ・オペレーションの選択を含む複合的なUI操作)で, 約35行のPlaywrightコードで構成されるチェーンです.

実験1のシンプルアプリ(10ステップ, HTMLフォーム)と比較して, 以下の点で複雑です.

  • MUI の Autocomplete/Combobox コンポーネント(内部で多層のDOMを生成)
  • ReactFlow のノード操作(ドラッグ&ドロップ, エッジ接続)
  • Reactの仮想DOM再レンダリングによるDOM構造の動的変化

Next.js + Material-UI(MUI) + ReactFlowの管理画面

4.2 変異方式

実験1・2ともに, サーバー側でDOM構造を切り替える方式 を採用しています. 実験1ではPython(Jinja2テンプレート)による検証用Webアプリの設定切り替え, 実験2ではReactコンポーネントのpropsを設定値に応じて切り替える仕組みです. いずれも, variant番号を変更してアプリを再起動するだけでDOM変異を安定して再現できます.

4.3 DOM変異4種

Variant 変異内容
Variant A placeholder属性の削除(タグ選択 + オペレーション選択の2ヶ所)
Variant B ラベルテキストの変更(「条件で振り分け」→「条件分岐」)
Variant C placeholder削除 + 3つ目のcombobox追加 + テキスト変更(累積的な複合変異)
Variant D 遅延ローディング(タグ選択のAutocompleteが3秒間loading状態)

各 Variant は Healer の修復能力の異なる側面を検証するものです. Variant A の placeholder 削除は, getByPlaceholder が使えなくなった場合に LLM がどの代替セレクタを選択するかを試すケースです. Variant B のラベルテキスト変更はセレクタの値が変わるパターン, Variant C は複数の変更が重なった累積変異, Variant D はバックエンドの応答遅延をシミュレートしたタイミング依存の変異です.

4.4 実験条件

  • モデル: Sonnet のみ(実験1の結論に基づく)
  • プロンプト: 実験1と同一(セレクタ優先順位 + デバッグ手順を含む構成)
  • 試行数: 4パターン × 各5回 = 合計20回
  • 目的: 本番Reactアプリでの修復可能性を確認する予備検証. 実験1とは異なり1回あたり$2〜$4の費用がかかるため, まず各5回で方向性を把握し, 深掘りすべきvariantを実験3で重点的に検証する方針としました.

4.5 結果: Variant別成功率

Variant 変異内容 成功率
Variant A placeholder削除(2ヶ所) 2/5 (40%)
Variant B ラベルテキスト変更 5/5 (100%)
Variant C 複合変異 2/5 (40%)
Variant D 遅延ローディング 4/5 (80%)

Variant B(ラベルテキスト変更)は全5回成功しました. テキストが変わっても, Healer は実ページのDOM構造から正しい代替セレクタを特定できています.

なお, Variant A・C では placeholder 削除に対する代替セレクタの選択で失敗するケースが目立ちました. 後述する実験3では, この placeholder 削除のケースに絞り, プロンプトの構成を変えることで修復品質がどう変わるかを検証しています.

失敗が発生した3つのVariantについて, 個別の要因を分析します.

Variant D(80%): 1件の失敗は遅延ローディングに起因するタイミングの問題です. タグ選択のAutocompleteが3秒間loading状態にあり, オプションが表示される前に getByRole('option', {name: '沖縄'}) をクリックしようとしてタイムアウトしました. セレクタの修正自体は正しいものの, 待機処理が不十分でした. なお, この種の遅延ローディングはバックエンドのレスポンス遅延やAPIの応答時間増加など, 普段の開発でも頻繁に遭遇する変異です. 5回中4回で修復に成功しており, タイミング依存の問題にもHealerが対応できることは実用上の価値が高いと考えています.

Variant A(40%): placeholder属性が削除された2ヶ所について, Healerがどのような代替セレクタを選択したかによって成否が分かれました. 詳細は4.7節で分析します.

Variant C(40%): 複数の変異が同時に適用された結果, placeholder参照のタイムアウト, 新たに追加されたcomboboxによるstrict mode違反, テキスト変更の未対応が重なり失敗しました.

4.6 費用と時間

Variant 平均費用 合計費用 平均時間
Variant A $3.35 $16.73 6.0min
Variant B $3.94 $19.70 6.4min
Variant C $4.30 $21.50 7.2min
Variant D $2.84 $14.22 5.2min
合計 $3.61 $72.15 6.2min

実験1のシンプルアプリ(Sonnet: $0.54/回, 91秒)と比べて, 本番Reactアプリでは1回あたり$2.84〜$4.30, 所要時間5.2〜7.2分と大幅に増加しています. 主な要因は, 操作ステップ数の増加(10ステップ → 約35行のPlaywrightコード)と, MUI Autocomplete/Combobox等のコンポーネントが生成する多層DOM構造です. 特にMUIのComboboxは内部的に複数のARIAロールを持つため, Healerが正しいセレクタを特定するのにより多くのDOM確認(browser_snapshotbrowser_run_code)を必要とし, これが費用と時間の増加に直結しています.

4.7 Variant Aの詳細分析(実験3への橋渡し)

Variant A(40%)の5回の試行を1件ずつ並べて比較したところ, 成功には 2つの条件が同時に満たされる必要がある ことが見えてきました.

試行 オペレーションplaceholder除去 タグのセレクタ戦略 結果
1 残した ✗ getByRole('combobox') NG
2 除去 ✓ getByRole('combobox').all() OK
3 除去 ✓ locator('[role="combobox"]') OK
4 除去 ✓ getByRole('button', {name:'開く'}) NG
5 残した ✗ getByRole('button', {name:'開く'}) NG
  • オペレーション側: placeholder行を除去するか残すかの二値的な問題. 試行1, 5は「存在しないplaceholderを参照するセレクタ」を残したまま提出し, タイムアウトで失敗
  • タグ側: 代替セレクタの選択品質が成否を分ける. combobox ロールで要素を特定したセレクタは成功するが, ボタン名「開く」で要素を特定したセレクタは不適切で失敗

Variant Aの失敗は, (1) 壊れたセレクタをそのまま残す, (2) 不適切な代替セレクタを選択する, という2種類に分類できます. 特にタグ側の代替セレクタの選択品質は, プロンプトの指示内容(例えば, セレクタ候補を複数検討させる, ブラウザで事前検証させる等)で改善できる可能性があります.

4.8 実験2の結論

  • 本番Reactアプリでも Healer は修復チェーンを生成でき, Variant Bでは100%, Variant Dでは80%の成功率を達成
  • 失敗の要因はVariantごとに異なる: タイミングの問題(Variant D), 代替セレクタの選択品質(Variant A), 複合変異の対応困難さ(Variant C)
  • Variant Aの分析から, 代替セレクタの選択品質がプロンプト設計で改善できる可能性がある
  • 実験3では, プロンプトの構成要素を要因として分解し, 修復品質への寄与を検証する

5. 実験3: プロンプト設計によるHealer修復力の探索

実験2では, placeholder 削除を含む Variant A(40%)と Variant C(40%)で修復の失敗が目立ちました. いずれも失敗の多くは placeholder が削除されたセレクタの修復に起因しています. 実験3では, この placeholder 削除の修復パターンを分離し, プロンプトの構成要素を変えることで修復力が改善するかを検証します.

5.1 実験の動機

Variant A・C ではいずれも placeholder が削除されたセレクタの修復失敗が多く(4.7節), getByPlaceholder が使えなくなった場合の代替セレクタ選択が Healer にとって難しい課題であることがわかりました.

そこで, この placeholder 削除の修復パターンを分離し, プロンプトの構成要素を変えることで代替セレクタの選択品質が改善するかを検証することにしました(実験2では1回あたり$3〜$4の費用がかかり, 40回分となるとお財布にやさしくないという事情もあります). 具体的には, タグ選択の placeholder 削除のみ に絞った変異を新設し, プロンプトの構成要素を要因として分解しています. タグ側を選んだ理由は, 代替セレクタに複数の候補(combobox role, button name, locator 等)があり, プロンプトの指示がセレクタ選択品質にどう影響するかを観察しやすいためです.

5.2 プロンプトの要因分解

実験1・2で使用したプロンプト(2.5節)を分析すると, 主に3つの構成要素に分解できます.

要因 内容 OFF ON
F1: セレクタ戦略 セレクタの優先順位と snapshot 確認 指示なし テキスト→ID→data→class の優先順位 + ページの snapshot を確認
F2: デバッグ手順 ステップバイステップの実行と問題箇所の特定 指示なし 1ステップずつ実行して問題箇所を特定する
F3: 推論プロセス 修正前の構造化された思考 指示なし 壊れたセレクタを特定→原因を分析→代替候補を3つ挙げる→最適解を選択

実験1・2のプロンプトは F1(セレクタ戦略)と F2(デバッグ手順)が ON の構成でした. F1 は Self-Healing の基本であるため全パターンで ON に固定し, F2(行動: 実行して確認する)× F3(思考: 考えてから直す)の2×2で検証しました.

パターン F1: セレクタ戦略 F2: デバッグ手順 F3: 推論プロセス 説明
プロンプトA セレクタ戦略のみ(最小構成)
プロンプトB + デバッグ手順(実験1・2と同等の構成)
プロンプトC + 推論プロセス
プロンプトD 全部入り

5.3 実験条件

  • 対象変異: Variant A からタグ側の placeholder 削除のみを抽出(壊れるセレクタ1ヶ所)
  • モデル: Sonnet
  • 試行数: 4パターン × 各10回 = 合計40回
  • 成功判定: 実験1・2と同一(修復済みチェーンを新規ブラウザセッションで再実行し, 全ステップが正常動作すれば成功)

5.4 結果: 成功率

パターン F2: デバッグ F3: 推論 成功率
プロンプトA 10/10 (100%)
プロンプトB 9/10 (90%)
プロンプトC 9/10 (90%)
プロンプトD 9/10 (90%)
合計 37/40 (92.5%)

全パターンで90%以上の成功率を達成しました. placeholder 削除に対する代替セレクタの選択という課題に対して, Healer は高い修復力を示しています.

最小構成の プロンプトA(セレクタ戦略のみ)が10回中10回成功と最も安定しており, デバッグ手順(F2)や推論プロセス(F3)を追加しても成功率は改善しませんでした.

失敗した3件の内訳は以下の通りです.

パターン 試行 失敗したセレクタ 原因
プロンプトC run_01 getByPlaceholder('タグを選択').first() 壊れたセレクタをそのまま使用(修復が行われなかった)
プロンプトB run_03 getByRole('option', { name: '商品金額計' }) タグ選択の修復自体は成功したが, 後続ステップで偶発的にタイムアウト
プロンプトD run_01 getByPlaceholder('タグを選択').first() 壊れたセレクタをそのまま使用(修復が行われなかった)

3件中2件は placeholder 削除を認識できずセレクタが修復されなかったケースであり, 1件は修復対象外のステップでの偶発的な失敗でした.

5.5 費用と時間

パターン F2: デバッグ F3: 推論 平均費用 合計費用 平均時間
プロンプトA $1.00 $10.01 2.8min
プロンプトB $1.73 $17.34 3.6min
プロンプトC $0.98 $9.78 2.8min
プロンプトD $1.33 $13.25 3.1min
合計 $1.26 $50.38 3.1min

費用差の要因を探るため, ツールの使用回数を比較しました.

パターン F2: デバッグ F3: 推論 browser_run_code 平均回数 平均費用
プロンプトA 0.9回 $1.00
プロンプトB 6.0回 $1.73
プロンプトC 0.7回 $0.98
プロンプトD 3.3回 $1.33

F2(デバッグ手順)を ON にしたパターンは browser_run_code の使用回数が大幅に増加しています. 「1ステップずつ実行して問題箇所を特定する」指示に従い, Playwright コードを繰り返し実行してセレクタを検証するため, ツール呼び出し回数とトークン消費が増え, 費用+55%の直接的な原因となっています.

一方, F3(推論プロセス)は「考えてから直す」指示であるため, ツール呼び出し回数はほとんど変わらず, 費用への影響は限定的でした(プロンプトC: $0.98 vs プロンプトA: $1.00, プロンプトD: $1.33 vs プロンプトB: $1.73).

5.6 考察

修復アプローチの違い: ツール使用ログを分析すると, Healer の修復アプローチは大きく3つに分類できました.

修復アプローチ browser_run_code browser_snapshot / browser_navigate 結果
コード実行型: セレクタを個別にコードで検証 1回以上 あり 成功
snapshot型: DOM構造を見て推測で修正 0回 あり 成功
チェーン定義のみ: ページを見ずに修正 0回 なし 失敗

プロンプトA では10回中6回が snapshot 型(browser_run_code = 0回)で修復に成功しています. ページの DOM 構造を browser_snapshot で確認するだけで, Playwright コードを直接実行しなくても適切な代替セレクタを選択できていました.

一方, 失敗した3件のうち2件(プロンプトC run_01, プロンプトD run_01)は browser_navigate すら呼び出さず, チェーン定義の文字列情報だけから修正を試みていました. これは実験1で Haiku が取った「チェーン定義だけで直す」アプローチ(3.6節)と同じパターンであり, 壊れたセレクタ getByPlaceholder('タグを選択') がそのまま残って失敗しています. Sonnet でもまれにこのアプローチを取ることがあり, その場合は修復に失敗します.

プロンプト構成と成功率: 4パターンとも90〜100%の成功率であり, 今回の条件(1ヶ所の placeholder 削除)ではプロンプトの構成による明確な成功率の差は観察されませんでした. ただし各パターン n=10 のため, 90%と100%の差が有意かは判断できません.

プロンプト追加の効果: 今回の条件では, デバッグ手順(F2)と推論プロセス(F3)のいずれを追加しても, 成功率に明確な改善は確認できませんでした. 今回の修復対象は placeholder 削除による1ヶ所のセレクタ破損であり, 「問題箇所の特定」は不要で, 「どの代替セレクタを選ぶか」が成否を分ける課題です. F2 は browser_run_code の使用回数を大幅に増やしますが, snapshot 型のアプローチでも修復に成功しているこの課題では, 追加のコード実行が費用に見合う効果をもたらさなかった可能性があります.

費用の差: 成功率に明確な差がない一方で, F2 ON のパターンは費用が平均+55%高くなっています. 今回の条件では, デバッグ手順の追加は費用の増加に見合う成功率の改善をもたらしませんでした.

5.7 実験3の結論と限界

  • 1ヶ所の placeholder 削除に対して, 全4パターンが90〜100%の成功率を達成. プロンプトの構成による明確な成功率の差は確認できなかった(各 n=10)
  • F2(デバッグ手順)を追加したパターンは費用が平均+55%増加したが, 成功率の改善は確認できなかった
  • 修復の成否を分けたのはプロンプトの構成ではなく, エージェントが実ページを確認したか否かだった(失敗3件中2件がページ未確認型)

ただし, 以下の限界があります.

  • サンプル数: 各パターン n=10 であり, 90%と100%の差が統計的に有意かは判断できない
  • 破損パターンの限定: 今回は placeholder 削除のみを対象とした. 他の種類の破損パターン(DOM構造の大幅な変更, コンポーネントの置換等)やステップ数の多いチェーンに対しても同じ結果が得られるかは未検証
  • Variant A との対比: placeholder 削除パターンを分離して検証した結果であり, 実験2の Variant A とは修復対象のスコープが異なるため, 厳密な対照実験ではない

6. まとめと実務への示唆

6.1 3段階の検証結果

本記事では, 3段階・合計260回の実験を通じて, LLM エージェントによる Self-Healing の実現可能性を検証しました. 冒頭で掲げた3つの検証ポイントに対する回答を整理します.

検証ポイント1: シンプルアプリと本番Reactの両方で修復は成功するか?

成功します. シンプルアプリでは Sonnet が99%の修復成功率を達成し(実験1), 本番の React 管理画面でも Variant B(ラベルテキスト変更)で100%, Variant D(遅延ローディング)で80%の成功率を確認しました(実験2). MUI の Autocomplete/Combobox 等の多層 DOM 構造に対しても, Healer は実ページの DOM を確認して適切な代替セレクタを選択できています. ただし, placeholder 削除のように元のセレクタが根本的に使えなくなり, どの代替セレクタを選ぶべきかをエージェント自身が判断する必要があるケースでは, Variant A(40%)や Variant C(40%)のように成功率が低下しました.

検証ポイント2: モデル(Sonnet / Haiku)で実用的な差はあるか?

明確な差があります. Sonnet 99% に対して Haiku 75%(実験1). 差の主因は修復アプローチの違いです. Sonnet は実ページをブラウザで確認してセレクタを個別に検証する一方, Haiku はチェーン定義の文字列情報のみから修正を試みる傾向がありました. 複合変異(Variant 5)では Haiku が全20回失敗しており, DOM 変異の種類を事前に予測できない実運用では Sonnet が現実的な選択肢です.

検証ポイント3: プロンプト設計の改善で成功率はさらに上がるのか?

1ヶ所の破損に対しては, プロンプトの構成による明確な成功率の差は確認できませんでした. 全4パターンが90〜100%の成功率を達成し(実験3, 各 n=10), デバッグ手順や推論プロセスの追加は費用を最大+55%増加させた一方, 成功率の改善にはつながりませんでした. ただし, この結果は1ヶ所の placeholder 削除に限定した条件であり, より難度の高い修復タスクでは異なる結果になる可能性があります.

6.2 費用の全体像

実験 試行数 合計費用 1回あたり平均費用
実験1: シンプルアプリ(Sonnet) 100 $54.34 $0.54
実験1: シンプルアプリ(Haiku) 100 $1.64 $0.016
実験2: 本番React 20 $72.15 $3.61
実験3: プロンプト要因実験 40 $50.38 $1.26
合計 260 $178.51

実験2・3の新規実験費用は合計 $122.53 でした. 本番 React アプリ(実験2)の費用がシンプルアプリ(実験1)の約7倍に増加しているのは, 操作ステップ数の増加と MUI コンポーネントの多層 DOM に起因する browser_snapshot / browser_run_code の呼び出し増によるものです. 実験3では placeholder 削除の修復パターンに絞ったことで, 本番 React アプリでありながら平均 $1.26/回まで費用が抑えられています.

6.3 実務への示唆

今回の実験から得られた, Self-Healing を実運用に導入する際の示唆を3点挙げます.

1. 修復の難度は変異の種類によって大きく異なる

実験2では, ラベルテキスト変更(Variant B: 100%)や遅延ローディング(Variant D: 80%)は高い成功率でしたが, placeholder 削除(Variant A: 40%)では成功率が低下しました. テキストやタイミングの変異ではセレクタの修正方針が比較的明確ですが, placeholder 削除のようにセレクタ戦略自体の変更が求められるケースでは, LLM がどの代替セレクタを選ぶかの判断品質が成否を左右します. 修復対象の変異タイプに応じてプロンプトや検証戦略を調整することが重要です.

2. プロンプトの追加指示は費用対効果を見て判断する

今回の条件では, 「ステップバイステップで確認せよ」「候補を3つ挙げて最適解を選べ」といった追加指示は, ツール呼び出し回数を増やして費用を押し上げた一方, 成功率の改善は確認できませんでした. プロンプトへの指示追加にはツール呼び出し増加による費用増が伴うため, 修復タスクの難度に応じて費用対効果を検証しながら判断する必要があります.

3. 「ページを見る」ことが修復の前提条件

全3実験を通じて, 修復の成否を最も強く分けたのは「実ページの DOM 構造を確認するか否か」でした. 実験1では Haiku が実ページを見ずに全件失敗(Variant 5), 実験3でも Sonnet がまれにページ未確認で失敗するケースがありました. 逆に, browser_snapshot で DOM を確認しさえすれば, browser_run_code でコードを実行しなくても修復に成功しています(snapshot 型, 5.6節). プロンプトの設計で最も重視すべきは, エージェントが必ず実ページを確認するフローを組み込むことです.

6.4 今後の課題

以下の課題が残っています.

  • セレクタ戦略の変更を伴う修復: placeholder 削除のように元のセレクタ戦略が根本的に使えなくなるケースでは, 実験2で成功率40%(Variant A)と課題が残りました. 代替セレクタの選択品質を向上させるプロンプト設計や検証戦略のさらなる探索が必要です
  • 大規模なチェーン: 今回の対象は最大35行の Playwright コードでした. ステップ数が100を超えるような大規模チェーンでの修復性能は未検証です
  • サンプル数: 実験3は各パターン n=10 であり, 90%と100%の差の統計的有意性は判断できません. より大きなサンプルでの検証が求められます
  • 修復経験の蓄積: 現在の Healer は修復を独立に実行しており, 過去の修復パターンを学習・再利用する仕組みはありません. 修復経験の蓄積により, 類似パターンの修復精度と速度が向上する可能性があります

6.5 おわりに

本記事では, LLM エージェントによるブラウザ自動化チェーンの Self-Healing を, シンプルアプリから本番 React アプリまで段階的に検証しました. 260回の実験を通じて, Healer はシンプルアプリでは99%, 本番 React アプリでも条件次第で80〜100%の修復成功率を達成し, Self-Healing が実用可能な水準にあることを確認できました. 今後はセレクタ戦略の変更を伴う修復の品質向上や修復経験の蓄積など, 実運用に向けた課題に取り組んでいきます.

スパイスコードでは, このような AI を使ったチャレンジングな機能開発に取り組んでいます. LLM エージェントの設計・検証を一緒に推進してくれるエンジニアを募集中です. 興味を持った方は, ぜひカジュアルにお話ししましょう!

採用情報はこちら


*1: Gao et al., "A Survey of Self-Evolving Agents", TMLR 2026. エージェントが経験を蓄積し環境変化に適応するフレームワークを体系化した研究.