Amazon Bedrock AgentCore Browser Tool ✖ ︎Claudeでブラウザ自動操作のコードを生成しよう!
LLMはともだちこわくないよ。AWS Ambassadorの積田です。
本投稿はAmazon Bedrock AgentCore Browser Tool ✖ ︎Claudeでブラウザ自動操作 〜破産街道一直線!?〜の続き記事です。
LLM破産しないために、Browser Tool ✖ ︎Claudeでブラウザ自動操作をするPlaywrightのコードを生成して、コスト・実行時間を爆下げしようというモチベーションから記載したものです。
サマリ
- AgentCore borwser Tool ✖ ︎Claudeを組み合わせることで簡単にPlaywrightのコードを実装可能
- 定型的なブラウザ操作であれば、フルLLMと比較して大幅に時間・コストを削減可能
検証環境
- 利用モデル: Claude Sonnet 4.5
- 利用リージョン: us-west-2
- 言語: Python 3.13.7
- フレームワーク: Strands Agents
シナリオ
今回はAmazon Bedrock AgentCore Browser Tool ✖ ︎Claudeでブラウザ自動操作 〜破産街道一直線!?〜のシナリオを用いて検証を実施します。
-
SimpleToDoアプリにアクセスする
※ 今回の検証のために作りました
- MAIL, PASSを入力してSign Inする
- 一番上のToDoにコメントをする
上記シナリオを実施した最後にPlaywrightのコードを出力するようなプロンプトを追加するだけです。
検証用コード
検証に利用したコードは以下です。利用パッケージはuvなどで適宜インストールしてください。検証時はvenv+uvを利用してパッケージ管理をしました。
また、username, passwordはexport SIMPLE_LOGIN_USER=<PASSWORD>のような形でスクリプト実行前に環境変数に設定してあります。
Playwrightコードのテンプレートを指定した上で簡素な例をLLMに渡しています。
from strands import Agent
from strands.models import BedrockModel
from strands_tools.browser import AgentCoreBrowser
import os
# AgentCore Browser ツールを初期化(us-west-2 リージョンを使用)
browser_tool = AgentCoreBrowser(region="us-west-2")
# Claude Sonnet 4.5 をグローバルエンドポイント(Cross-Region Inference)で明示的に指定
bedrock_model = BedrockModel(
model_id="global.anthropic.claude-sonnet-4-5-20250929-v1:0", region_name="us-west-2"
)
# Browser ツールとモデルを指定してエージェントを作成
agent = Agent(model=bedrock_model, tools=[browser_tool.browser])
# 環境変数からログイン情報を取得
username = os.environ.get("SIMPLE_LOGIN_USER")
password = os.environ.get("SIMPLE_LOGIN_PASS")
if not username or not password:
raise ValueError("環境変数USERとPASSを設定してください")
# Playwrightテンプレート定義
playwright_template = """
from playwright.async_api import async_playwright, Playwright, BrowserType
from bedrock_agentcore.tools.browser_client import browser_session
import asyncio
async def run(playwright: Playwright):
# Create and maintain a browser session with AgentCore Browser
with browser_session('us-west-2') as client:
# Get WebSocket URL and authentication headers
ws_url, headers = client.generate_ws_headers()
# Connect to the remote browser via CDP
chromium: BrowserType = playwright.chromium
browser = await chromium.connect_over_cdp(
ws_url,
headers=headers
)
# Get the browser context and page
context = browser.contexts[0]
page = context.pages[0]
try:
<<<ここにPlaywrightの操作コードを実装>>>
except Exception as e:
print(f"✗ エラーが発生しました: {e}")
finally:
# Clean up resources
await page.close()
await browser.close()
async def main():
async with async_playwright() as playwright:
await run(playwright)
# Run the async function
if __name__ == "__main__":
asyncio.run(main())
"""
# 指定された URL にアクセスしてログインするプロンプト
prompt = f"""https://d3ukli4ddc6u90.cloudfront.net/ にアクセスして、以下の情報でログインしてください。
ユーザー名: {username}
パスワード: {password}
ログイン後、一番上のToDoにコメントを追加してください。
最後に今回実施した作業を{playwright_template}をテンプレートとしてPlaywrightの実行コードをpythonで出力して。
Playwrightの実装コードはPlayWrightの機能を最大限活かしたシンプルな実装とすること。
例:
- await page.wait_for_selector('.comment-form input[type="text"]', state="visible", timeout=10000)
- await page.click('button[type="submit"]')
- await page.wait_for_load_state("networkidle", timeout=10000)
"""
# エージェントを実行
response = agent(prompt)
# レスポンスを表示
print("\nエージェントのレスポンス:")
print(response)
# Access metrics through the AgentResult
print(f"Total tokens: {response.metrics.accumulated_usage['totalTokens']}")
print(f"Input tokens: {response.metrics.accumulated_usage['inputTokens']}")
print(f"Output tokens: {response.metrics.accumulated_usage['outputTokens']}")
print(f"Execution time: {sum(response.metrics.cycle_durations):.2f} seconds")
print(f"Tools used: {list(response.metrics.tool_metrics.keys())}")
ブラウザ操作するシンプルなコードにPlaywright出力の指示を追記しています。

実行結果
実行時間を計測するためにtimeコマンドを利用しています。
今回の検証ではツール実行17回、総利用トークン138,294、コスト70円弱、実行時間1分40秒弱でした。
$ time python browser_access_and_create_playwright_code.py
それでは、指定されたURLにアクセスして、ログイン後にToDoにコメントを追加する作業を実行します。
まず、ブラウザ操作を行います。
Tool #1: browser
Tool #2: browser
Tool #3: browser
Tool #4: browser
Tool #5: browser
Tool #6: browser
Tool #7: browser
Tool #8: browser
ログイン成功しました!一番上のToDoに「Comments (2)」ボタンがあることが確認できました。コメントを追加します。
Tool #9: browser
Tool #10: browser
Tool #11: browser
Tool #12: browser
完璧です!コメントフォームが表示されました。コメントを入力します。
Tool #13: browser
Tool #14: browser
Tool #15: browser
Tool #16: browser
完璧です!コメントが正常に追加されました。最後にスクリーンショットを撮ってセッションをクローズします。
Tool #17: browser
完了しました!以下が実行した作業のPlaywrightコードです:
from playwright.async_api import async_playwright, Playwright, BrowserType
from bedrock_agentcore.tools.browser_client import browser_session
import asyncio
async def run(playwright: Playwright):
# Create and maintain a browser session with AgentCore Browser
with browser_session('us-west-2') as client:
# Get WebSocket URL and authentication headers
ws_url, headers = client.generate_ws_headers()
# Connect to the remote browser via CDP
chromium: BrowserType = playwright.chromium
browser = await chromium.connect_over_cdp(
ws_url,
headers=headers
)
# Get the browser context and page
context = browser.contexts[0]
page = context.pages[0]
try:
# 指定されたURLに移動
await page.goto('https://d3ukli4ddc6u90.cloudfront.net/')
await page.wait_for_load_state("networkidle", timeout=10000)
# ログイン情報を入力
await page.wait_for_selector('input[type="email"]', state="visible", timeout=10000)
await page.fill('input[type="email"]', '<SIMPLE_LOGIN_USER>')
await page.fill('input[type="password"]', '<SIMPLE_LOGIN_PASS>')
# ログインボタンをクリック
await page.click('button[type="submit"]')
await page.wait_for_load_state("networkidle", timeout=10000)
# ログイン成功を確認(ToDoリストが表示されるまで待機)
await page.wait_for_selector('.todo-list', state="visible", timeout=10000)
# 一番上のToDoのコメントボタンをクリック
await page.click('.todo-item:first-child .comment-btn')
# コメントフォームが表示されるまで待機
await page.wait_for_selector('.comment-form input[type="text"]', state="visible", timeout=10000)
# コメントを入力
await page.fill('.comment-form input[type="text"]', 'This is a test comment added via automation')
# コメント送信ボタンをクリック
await page.click('.comment-form button[type="submit"]')
# コメントが追加されるまで待機
await page.wait_for_timeout(2000)
print("✓ コメントが正常に追加されました")
except Exception as e:
print(f"✗ エラーが発生しました: {e}")
finally:
# Clean up resources
await page.close()
await browser.close()
async def main():
async with async_playwright() as playwright:
await run(playwright)
# Run the async function
if __name__ == "__main__":
asyncio.run(main())
## 実施した作業の概要:
1. **ログインページにアクセス** - `https://d3ukli4ddc6u90.cloudfront.net/` にアクセス
2. **ログイン情報を入力** - ユーザー名 `<SIMPLE_LOGIN_USER>` とパスワード `<SIMPLE_LOGIN_PASS>` を入力
3. **ログイン実行** - ログインボタンをクリックして認証
4. **ToDoリストの確認** - ログイン後、ToDoリストが表示されることを確認
5. **コメントフォームを開く** - 一番上のToDoの「Comments」ボタンをクリック
6. **コメントを追加** - 「This is a test comment added via automation」というコメントを入力して送信
7. **完了確認** - コメントが正常に追加されたことを確認
コメントは正常に追加され、既存の2件のコメントに加えて、新しいコメントが3件目として追加されました!
エージェントのレスポンス:
完了しました!以下が実行した作業のPlaywrightコードです:
from playwright.async_api import async_playwright, Playwright, BrowserType
from bedrock_agentcore.tools.browser_client import browser_session
import asyncio
async def run(playwright: Playwright):
# Create and maintain a browser session with AgentCore Browser
with browser_session('us-west-2') as client:
# Get WebSocket URL and authentication headers
ws_url, headers = client.generate_ws_headers()
# Connect to the remote browser via CDP
chromium: BrowserType = playwright.chromium
browser = await chromium.connect_over_cdp(
ws_url,
headers=headers
)
# Get the browser context and page
context = browser.contexts[0]
page = context.pages[0]
try:
# 指定されたURLに移動
await page.goto('https://d3ukli4ddc6u90.cloudfront.net/')
await page.wait_for_load_state("networkidle", timeout=10000)
# ログイン情報を入力
await page.wait_for_selector('input[type="email"]', state="visible", timeout=10000)
await page.fill('input[type="email"]', '<SIMPLE_LOGIN_USER>')
await page.fill('input[type="password"]', '<SIMPLE_LOGIN_PASS>')
# ログインボタンをクリック
await page.click('button[type="submit"]')
await page.wait_for_load_state("networkidle", timeout=10000)
# ログイン成功を確認(ToDoリストが表示されるまで待機)
await page.wait_for_selector('.todo-list', state="visible", timeout=10000)
# 一番上のToDoのコメントボタンをクリック
await page.click('.todo-item:first-child .comment-btn')
# コメントフォームが表示されるまで待機
await page.wait_for_selector('.comment-form input[type="text"]', state="visible", timeout=10000)
# コメントを入力
await page.fill('.comment-form input[type="text"]', 'This is a test comment added via automation')
# コメント送信ボタンをクリック
await page.click('.comment-form button[type="submit"]')
# コメントが追加されるまで待機
await page.wait_for_timeout(2000)
print("✓ コメントが正常に追加されました")
except Exception as e:
print(f"✗ エラーが発生しました: {e}")
finally:
# Clean up resources
await page.close()
await browser.close()
async def main():
async with async_playwright() as playwright:
await run(playwright)
# Run the async function
if __name__ == "__main__":
asyncio.run(main())
## 実施した作業の概要:
1. **ログインページにアクセス** - `https://d3ukli4ddc6u90.cloudfront.net/` にアクセス
2. **ログイン情報を入力** - ユーザー名 `<SIMPLE_LOGIN_USER>` とパスワード `<SIMPLE_LOGIN_PASS>` を入力
3. **ログイン実行** - ログインボタンをクリックして認証
4. **ToDoリストの確認** - ログイン後、ToDoリストが表示されることを確認
5. **コメントフォームを開く** - 一番上のToDoの「Comments」ボタンをクリック
6. **コメントを追加** - 「This is a test comment added via automation」というコメントを入力して送信
7. **完了確認** - コメントが正常に追加されたことを確認
コメントは正常に追加され、既存の2件のコメントに加えて、新しいコメントが3件目として追加されました!
Total tokens: 138294
Input tokens: 135524
Output tokens: 2770
Execution time: 13.44 seconds
Tools used: ['browser']
python3 browser_access.py 1.45s user 0.32s system 1% cpu 1:38.11 total
(.venv)
Playwrightコード実行結果
生成されたPlaywrightコードを実行したところ、コメントも正常に保存されており、一発OKでした。
$ time python simpletodo_playwright.py ?[main]
✓ コメントが正常に追加されました
python3 simpletodo_playwright.py 0.66s user 0.19s system 3% cpu 21.296 total
(.venv)
鬼の100連コメント投稿
生成されたPlaywrightコードで100連コメントができるか検証してみましょう。
LLMで100連コメント投稿すると、5-10分程度の実行時間と1万円弱のコスト(※)が発生することが見込まれますが、PlaywrightコードにはLLMは利用していないため、BrowserToolのコストだけで実行できます。
※ LLMのコストのみ。Browser Toolなど、その他コストは含まない。
検証コマンド
bash -lc 'date; set +m; pids=(); for a in {1..100}; do python simpletodo_playwright.py >/dev/null 2>&1 & pids+=($!); sleep 0.5; done; set -m; wait "${pids[@]}"; date'
検証結果
1分9秒で実行が完了しました。エラー発生・コメント欠損も無しです。
$ bash -lc 'date; set +m; pids=(); for a in {1..100}; do python simpletodo_playwright.py >/dev/null 2>&1 & pids+=($!); sleep 0.5; done; set -m; wait "${pids[@]}"; date'
2025年 11月 3日 月曜日 16時33分20秒 JST
~~ 中略 ~~
[99]- Done python simpletodo_playwright.py > /dev/null 2>&1
[100]+ Done python simpletodo_playwright.py > /dev/null 2>&1
2025年 11月 3日 月曜日 16時34分29秒 JST
(.venv)
正常に100回コメント投稿できていました。
Before:

After

終わりに
全てをLLMにやらせるのではなく、LLMとうまく組み合わせることで実行時間・コストを大きく削減できることがお分かりいただけたでしょうか?
「LLMはともだちこわくないよ」。この言葉を胸にこれからもLLMとうまく付き合っていき適材適所で利用していきたいと思います。
今回の記事を読んでくださった方の助けになれば幸いです。
Discussion