Python + Cursor CLI + Playwright CLIで、ブラウザ操作の一部をAIに任せる
X-tech推進本部 小林ブラウザ操作を自動化するプログラムを開発しても、ページの更新で対象のセレクタが変わってしまいメンテナンスが必要になることがあります。たまに変更があるだけならその都度修正対応しても問題ありませんが、更新が頻繁にある場合、メンテナンスだけで多くの工数が取られてしまいます。
最近ではAIによるブラウザ操作技術も発展してきており、AIエージェントに画面状態を解釈させながら操作する方法も登場していますが、全てをAI操作に置き換えれば良いわけではありません。全シナリオをAI操作にすると、定型処理に比べてコストや実行時間が多くかかったり、操作ミスがあった際の原因究明がしづらいというデメリットがあります。
AI利用のメリットを享受しながらデメリットを最小化するには、自動化フローのうち、本当に必要な部分のみに限定してAIを利用するのが有効です。本記事では、変更がほぼないと言える部分は従来の定型処理で操作し、変更が多い部分のみAI操作をする形で自動化を実現する方法を考えます。
想定する処理
社内システムのフォームに情報を入力する処理を想定します。フォームのURLは固定ですが、各入力項目のセレクタの変更はあり得るものとします。このとき、次のような役割分担で定型処理とAI操作を使い分けると効果的です。
- 定型処理でできる部分
- ブラウザ起動
- フォームにアクセス
- AIで操作する部分
- フォームへの情報入力
今回はAIで操作する部分として、3つの項目(名前、メールアドレス、電話番号)を持つフォームに情報入力する処理を想定した実装を紹介します。
※ なお、本記事で紹介する実装は、動作検証のためのサンプルです。本番環境での動作は想定していません。
実行環境
全体の処理はPythonで実装し、その処理の中で、AIで操作したい部分ではCursor CLIを呼び出すようにしました。CursorにはPlaywright CLIを経由してブラウザ(Chrome)を操作させました。
Python
├─ ブラウザ(Chrome)起動
├─ ページ表示(フォームにアクセス)
└─ Cursor CLI
└─ Playwright CLI
└─ Chrome操作(フォームへの情報入力)
Cursor CLI
PythonからCursor AgentをCLI経由で呼び出すため、Cursor CLIをインストールします。インストールしたら、初回のみagent loginを実行してCursorアカウントで認証します。
Playwright CLI
Playwright CLIは、AIエージェント向けのブラウザ操作CLIです。次のコマンドでインストールできます。
npm install -g @playwright/cli@latest
インストール後、Cursor AgentにPlaywright CLIの使い方を理解させるために、プロジェクト内で次のコマンドを実行し、スキルを導入します。
playwright-cli install --skills=agents
今回は仕組み上、Cursor CLIからPlaywright CLIのコマンドを承認なしで実行できるようにする必要があります。%USERPROFILE%\.cursor\cli-config.jsonのpermissionsに、今回使用するサブコマンドだけを追加します。
{
"permissions": {
"allow": [
"Shell(playwright-cli:attach*)",
"Shell(playwright-cli:snapshot*)",
"Shell(playwright-cli:fill*)",
"Shell(playwright-cli:click*)"
]
}
}
※ この設定は、ほかのプロジェクトでCursor CLIを使う場合にも適用されますのでご注意ください。
実装内容
Chromeの起動
まずChromeを起動します。このとき、Playwright CLIなどの外部ツールから接続できるよう、Remote Debuggingを有効にします。今回はChrome DevTools Protocol(CDP)用のローカルポートを開いて起動します。--remote-debugging-port=9223を付けてChromeを起動すると、http://127.0.0.1:9223 がそのChromeへのデバッグ用接続先になります。以降、Playwright CLIはこの接続先を使って、同じChromeを操作できます。
ポート番号はこのプログラムでは固定にしていますが、同じポートを別のChromeが先に使っていると、この処理がそちらへ接続し、意図しないブラウザを操作することがあります。実際は空いているポートを自動で選ぶようにすることをおすすめします。
import subprocess
import time
from pathlib import Path
import requests
ROOT = Path(__file__).resolve().parent
CHROME_PATH = r"C:\Program Files\Google\Chrome\Application\chrome.exe"
CHROME_PROFILE = ROOT / ".chrome-debug-profile"
def launch_browser() -> str:
"""Remote Debuggingを有効にしたChromeを起動する。"""
CHROME_PROFILE.mkdir(exist_ok=True)
port = 9223
subprocess.Popen([
CHROME_PATH,
f"--remote-debugging-port={port}",
"--remote-debugging-address=127.0.0.1",
f"--user-data-dir={CHROME_PROFILE}",
"--no-first-run",
"--no-default-browser-check",
])
cdp_url = f"http://127.0.0.1:{port}" # このURLを使い、後から同じChromeを操作できる
wait_until_debugging_ready(cdp_url)
return cdp_url
def wait_until_debugging_ready(cdp_url: str) -> None:
"""Remote Debuggingが利用可能になるまで待つ。"""
for _ in range(50):
try:
response = requests.get(f"{cdp_url}/json/version", timeout=1)
if response.status_code == 200:
return
except requests.RequestException:
pass
time.sleep(0.2)
raise TimeoutError("ChromeのRemote Debuggingが開始されませんでした")
対象ページを開く
先ほど起動したChromeのデバッグ用接続先にHTTPリクエストを送り、指定したWebページを新しいタブで開きます。cdp_urlは「どのChromeを操作するか」、page_urlは「そのChromeで何を開くか」を表します。/json/newにPUTリクエストを送信することで、新しいタブを作成して対象ページを表示します。
def open_target_page(cdp_url: str, page_url: str) -> None:
"""起動したChromeで対象ページを開く。"""
endpoint = f"{cdp_url}/json/new?{page_url}"
response = requests.put(endpoint, timeout=5)
response.raise_for_status()
プロンプトを作る
業務指示taskと、Playwright CLIの接続先cdp_urlを組み合わせて、Agentへ渡す最終プロンプトを作ります。
def build_prompt(prompt_path: Path, cdp_url: str) -> str:
"""自然言語の操作指示に、Playwright CLIの接続先を追加する。"""
task = prompt_path.read_text(encoding="utf-8").strip()
return f"""
{task}
## 接続先
Playwright CLIでは、次のコマンドで現在起動しているChromeにattachしてください。
playwright-cli attach --cdp={cdp_url}
""".strip()
taskの中身は別ファイルに用意した次のようなプロンプトです。
現在表示されているフォームを確認し、以下を入力してください。
名前:MLC小林
メールアドレス:test@example.com
電話番号:12345678
入力対象は固定のCSS selectorや識別子で決め打ちせず、画面に表示されているラベル、role、accessible nameなどから判断してください。
最後に保存ボタンを押し、保存完了メッセージが表示されたことを確認してください。
操作終了後は、成功したかどうかと簡単な実行結果を回答してください。
今回の例だと、プロンプトの内容は全てCursorへ送信されますので、入力するデータは、必ずテスト用のダミーデータを使いましょう。
AIエージェントによるブラウザ操作
Pythonのsubprocess.run()からCursor Agentを起動し、自然言語プロンプトを標準入力として渡します。Agentはプロジェクト内のSkillを参照しながらPlaywright CLIを実行し、処理結果を標準出力として返します。
def run_cursor_agent(prompt: str) -> None:
"""Cursor Agentにプロンプトを渡してブラウザ操作を実行させる。"""
result = subprocess.run(
[
"cmd.exe",
"/c",
"agent",
"-p",
"--workspace",
str(ROOT),
],
input=prompt,
text=True,
capture_output=True,
encoding="utf-8",
)
print(result.stdout)
上記の関数を順に呼び出すことで、一連の処理を実行できます。検証では自作のダミーフォームを使用しましたが、以下では対象ページのURLをPAGE_URLとして抽象化しています。
PAGE_URL = "ここに操作対象フォームのURLを指定"
PROMPT_PATH = ROOT / "prompts" / "browser_task.md"
cdp_url = launch_browser()
open_target_page(cdp_url, PAGE_URL)
prompt = build_prompt(PROMPT_PATH, cdp_url)
run_cursor_agent(prompt)
まとめ
本記事では、通常のPython処理でブラウザを起動・対象ページを表示し、その後の一部操作だけをCursor Agent + Playwright CLIに引き継ぐ構成を試しました。
今回の構成のように、自動化フローから必要なタイミングだけAIエージェントへ処理を引き継げるようにしておけば、定型処理の安定性を活かしながら、AIの柔軟性も取り込める可能性があります。一方で、権限設定や安定性、実行時間、コストなど、実運用に向けた検討事項もあります。今回の構成はあくまで1つの試みとして、どの部分にAIを取り入れると効果的かを今後も検証していきたいと思います。