Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

ブラウザーエージェント

最終更新 Markdown で表示Agent セットアップ

Browser Run ツールで、ウェブを閲覧し、ページを検査し、スクリーンショットを取得し、フロントエンドの問題をデバッグするエージェントを構築します。ベータ

クリック、スクリーンショット、遷移といった固定のブラウザー操作ではなく、LLM が JavaScript を書き、ライブのブラウザーセッションに対して CDP コマンドを実行します。プロトコル上のすべてのドメイン、コマンド、イベント、型にアクセスできます。

提供されるツールは次の 2 つです。

ツール 説明
browser_search CDP 仕様を照会し、コマンド、イベント、型を見つけます。仕様はブラウザーの CDP エンドポイントから動的に取得し、キャッシュします。
browser_execute cdp ヘルパー経由で、ライブのブラウザーに対して CDP コマンドを実行します。呼び出しごとに新しいセッションを開き、コードを実行し、閉じます。

ブラウザーツールを使う場面

次のような処理が必要なときに、ブラウザーツールが役立ちます。

  • ウェブページの検査 — DOM 構造、計算済みスタイル、アクセシビリティツリー
  • フロントエンドのデバッグ — ネットワークウォーターフォール、コンソールエラー、パフォーマンストレース
  • 構造化データのスクレイピング — 描画済みページからコンテンツを抽出
  • スクリーンショットや PDF の取得 — ウェブコンテンツの視覚的なスナップショット
  • パフォーマンスのプロファイリング — Core Web Vitals、JavaScript プロファイリング、メモリ分析

描画済み DOM が不要な単純なページ取得には、代わりに fetch() を使います。

インストール

ブラウザーツールには Agents SDK と @cloudflare/codemode が必要です。

npm install agents @cloudflare/codemode ai zod

クイックスタート

1. バインディングを設定する

wrangler 設定に、Browser Run(旧 Browser Rendering)と Worker Loader のバインディングを追加します。

{
	"compatibility_flags": ["nodejs_compat"],
	"browser": {
		"binding": "BROWSER",
	},
	"worker_loaders": [
		{
			"binding": "LOADER",
		},
	],
}
compatibility_flags = [ "nodejs_compat" ]

[browser]
binding = "BROWSER"

[[worker_loaders]]
binding = "LOADER"

2. ブラウザーツールを作成する

import { createBrowserTools } from "agents/browser/ai";

const browserTools = createBrowserTools({
	browser: env.BROWSER,
	loader: env.LOADER,
});
import { createBrowserTools } from "agents/browser/ai";

const browserTools = createBrowserTools({
	browser: env.BROWSER,
	loader: env.LOADER,
});

Browser Run バインディングではなく、独自の CDP エンドポイントに接続する場合は cdpUrl を渡します。

3. streamText と組み合わせる

ブラウザーツールを、ほかのツールと一緒に渡します。model には任意の AI SDK プロバイダーを使えます。ここでは Workers AI を使います。

import { streamText } from "ai";
import { createWorkersAI } from "workers-ai-provider";

const workersai = createWorkersAI({ binding: env.AI });

const result = streamText({
	model: workersai("@cf/zai-org/glm-4.7-flash"),
	system: "You are a helpful assistant that can inspect web pages.",
	messages,
	tools: {
		...browserTools,
		...otherTools,
	},
});
import { streamText } from "ai";
import { createWorkersAI } from "workers-ai-provider";

const workersai = createWorkersAI({ binding: env.AI });

const result = streamText({
	model: workersai("@cf/zai-org/glm-4.7-flash"),
	system: "You are a helpful assistant that can inspect web pages.",
	messages,
	tools: {
		...browserTools,
		...otherTools,
	},
});

どちらのツールも、JavaScript の async アロー関数を入れる code パラメーターを受け取ります。サンドボックスはツールに応じてグローバルを注入します。browser_search には specbrowser_execute には cdp です。

LLM が browser_search を使うとき、コードは注入された spec オブジェクト経由で CDP 仕様を照会します。

async () => {
	const s = await spec.get();
	return s.domains
		.find((d) => d.name === "Network")
		.commands.map((c) => ({ method: c.method, description: c.description }));
};

LLM が browser_execute を使うとき、コードは注入された cdp ヘルパー経由で CDP コマンドを実行します。

async () => {
	const { targetId } = await cdp.send("Target.createTarget", {
		url: "https://example.com",
	});
	const sessionId = await cdp.attachToTarget(targetId);
	const { root } = await cdp.send("DOM.getDocument", {}, { sessionId });
	const { outerHTML } = await cdp.send(
		"DOM.getOuterHTML",
		{ nodeId: root.nodeId },
		{ sessionId },
	);
	await cdp.send("Target.closeTarget", { targetId });
	return outerHTML;
};

Agent と組み合わせる

典型的なパターンは、AIChatAgent のメッセージハンドラー内でブラウザーツールを作ることです。メッセージの永続化とストリーミングが使えます。

import { AIChatAgent } from "@cloudflare/ai-chat";
import { createBrowserTools } from "agents/browser/ai";
import { createWorkersAI } from "workers-ai-provider";
import { streamText, convertToModelMessages, stepCountIs } from "ai";

export class MyAgent extends AIChatAgent {
	async onChatMessage() {
		const workersai = createWorkersAI({ binding: this.env.AI });
		const browserTools = createBrowserTools({
			browser: this.env.BROWSER,
			loader: this.env.LOADER,
		});

		const result = streamText({
			model: workersai("@cf/zai-org/glm-4.7-flash"),
			system: "You can browse the web and inspect pages.",
			messages: await convertToModelMessages(this.messages),
			tools: {
				...browserTools,
			},
			stopWhen: stepCountIs(10),
		});

		return result.toUIMessageStreamResponse();
	}
}
import { AIChatAgent } from "@cloudflare/ai-chat";
import { createBrowserTools } from "agents/browser/ai";
import { createWorkersAI } from "workers-ai-provider";
import { streamText, convertToModelMessages, stepCountIs } from "ai";

export class MyAgent extends AIChatAgent<Env> {
	async onChatMessage() {
		const workersai = createWorkersAI({ binding: this.env.AI });
		const browserTools = createBrowserTools({
			browser: this.env.BROWSER,
			loader: this.env.LOADER,
		});

		const result = streamText({
			model: workersai("@cf/zai-org/glm-4.7-flash"),
			system: "You can browse the web and inspect pages.",
			messages: await convertToModelMessages(this.messages),
			tools: {
				...browserTools,
			},
			stopWhen: stepCountIs(10),
		});

		return result.toUIMessageStreamResponse();
	}
}

TanStack AI

TanStack AI では、/tanstack-ai エクスポートを使います。

import { createBrowserTools } from "agents/browser/tanstack-ai";
import { chat, workersAIText } from "@tanstack/ai";

const browserTools = createBrowserTools({
	browser: env.BROWSER,
	loader: env.LOADER,
});

const stream = chat({
	adapter: workersAIText(env.AI, "@cf/zai-org/glm-4.7-flash"),
	tools: [...browserTools, ...otherTools],
	messages,
});
import { createBrowserTools } from "agents/browser/tanstack-ai";
import { chat, workersAIText } from "@tanstack/ai";

const browserTools = createBrowserTools({
	browser: env.BROWSER,
	loader: env.LOADER,
});

const stream = chat({
	adapter: workersAIText(env.AI, "@cf/zai-org/glm-4.7-flash"),
	tools: [...browserTools, ...otherTools],
	messages,
});

実行モデル

  • browser_search は、ブラウザーの /json/protocol エンドポイントからライブの CDP プロトコルを取得し、短時間キャッシュします。
  • browser_execute は呼び出しごとに新しいブラウザーセッションを開き、サンドボックス内のコードへ小さな cdp ヘルパー API を公開し、実行終了時にセッションを閉じます。
  • LLM が生成したコードは Worker サンドボックスで実行されます。CDP トラフィックはホスト Worker 側に残ります。

CDP ヘルパー API

browser_execute 内では、サンドボックスのコードから次の関数を使えます。

cdp.send(method, params?, options?)

CDP コマンドを送信し、応答を待ちます。

パラメーター 説明
method string CDP メソッド。例: "DOM.getDocument""Network.enable"
params unknown メソッドのパラメーター
options.timeoutMs number コマンドごとのタイムアウト(デフォルト: 10 秒)
options.sessionId string ターゲットのセッション ID(ページスコープのコマンドでは必須)

cdp.attachToTarget(targetId, options?)

ターゲットにアタッチし、セッション ID を取得します。flatten: trueTarget.attachToTarget を使います。

パラメーター 説明
targetId string アタッチ先のターゲット
options.timeoutMs number アタッチコマンドのタイムアウト

戻り値は sessionId 文字列です。

cdp.getDebugLog(limit?)

直近の CDP デバッグログ(送信、受信、エラー)を取得します。デフォルトは直近 50 件、最大 400 件です。

cdp.clearDebugLog()

デバッグログのバッファーをクリアします。

設定

createBrowserTools(options)

AI SDK ツール(browser_searchbrowser_execute)を返します。

オプション デフォルト 説明
browser Fetcher Browser Run バインディング
cdpUrl string 独自 CDP エンドポイントへの任意の上書き
cdpHeaders Record<string, string> CDP URL 検出用ヘッダー(例: Cloudflare Access)
loader WorkerLoader 必須 サンドボックス実行用の Worker Loader バインディング
timeout number 30000 実行タイムアウト(ミリ秒)

browser または cdpUrl のどちらかが必須です。両方を指定した場合は cdpUrl が優先されます。

低レベルアクセス

独自の統合では、構成要素を直接インポートします。

import {
	CdpSession,
	connectBrowser,
	connectUrl,
	createBrowserToolHandlers,
} from "agents/browser";

// Connect to a custom CDP endpoint
const session = await connectUrl("http://localhost:9222");
const version = await session.send("Browser.getVersion");
session.close();
import {
	CdpSession,
	connectBrowser,
	connectUrl,
	createBrowserToolHandlers,
} from "agents/browser";

// Connect to a custom CDP endpoint
const session = await connectUrl("http://localhost:9222");
const version = await session.send("Browser.getVersion");
session.close();

ローカル開発

最近の Wrangler リリースは、ローカル開発で Browser Run に対応しています。npx wrangler dev がブラウザーを自動で用意するので、同じ browser: env.BROWSER の設定がローカルとデプロイ先の両方で動きます。

cdpUrl は、トンネルや手動管理の Chrome など、別の CDP 互換エンドポイントへ意図的に接続したい場合にだけ使います。

セキュリティ上の考慮点

  • LLM が生成したコードは 隔離された Worker サンドボックス で実行されます。実行ごとに専用の Worker インスタンスが割り当てられます
  • サンドボックスからの外部ネットワークアクセス(fetchconnect)は、ランタイムレベルで 遮断 されます
  • CDP コマンドは Workers RPC 経由で送出されます。WebSocket はホスト側にあり、サンドボックスにはありません
  • CDP 仕様はサーバー側に残ります。LLM へ流れるのは照会結果だけです
  • コンテキストウィンドウの溢流を防ぐため、応答は約 6,000 トークンで切り詰められます

現在の制限

  • execute 呼び出しごとに 1 セッションbrowser_execute の呼び出しごとに新しいブラウザーセッションが開きます。複数ステップのワークフローは、1 つのコードブロック内で完了する必要があります。
  • 認証済みセッションなし — ブラウザーは Cookie やログイン状態なしで起動します。
  • ピア依存関係として @cloudflare/codemode が必要です。
  • サンドボックス内の実行は JavaScript に限られます(TypeScript 構文は使えません)。

Puppeteer を直接使う

LLM が生成したコードではなく、プログラムからブラウザーを制御したい場合は、Browser Run API と Puppeteer を直接使えます。

npm i -D @cloudflare/puppeteer
import puppeteer from "@cloudflare/puppeteer";

export class MyAgent extends Agent {
	async browse(browserInstance, urls) {
		let responses = [];
		for (const url of urls) {
			const browser = await puppeteer.launch(browserInstance);
			const page = await browser.newPage();
			await page.goto(url);

			await page.waitForSelector("body");
			const bodyContent = await page.$eval(
				"body",
				(element) => element.innerHTML,
			);

			let resp = await this.env.AI.run("@cf/zai-org/glm-4.7-flash", {
				messages: [
					{
						role: "user",
						content: `Return a JSON object with the product names, prices and URLs from the website content below. <content>${bodyContent}</content>`,
					},
				],
			});

			responses.push(resp);
			await browser.close();
		}

		return responses;
	}
}
import puppeteer from "@cloudflare/puppeteer";

interface Env {
	BROWSER: Fetcher;
	AI: Ai;
}

export class MyAgent extends Agent<Env> {
	async browse(browserInstance: Fetcher, urls: string[]) {
		let responses = [];
		for (const url of urls) {
			const browser = await puppeteer.launch(browserInstance);
			const page = await browser.newPage();
			await page.goto(url);

			await page.waitForSelector("body");
			const bodyContent = await page.$eval(
				"body",
				(element) => element.innerHTML,
			);

			let resp = await this.env.AI.run("@cf/zai-org/glm-4.7-flash", {
				messages: [
					{
						role: "user",
						content: `Return a JSON object with the product names, prices and URLs from the website content below. <content>${bodyContent}</content>`,
					},
				],
			});

			responses.push(resp);
			await browser.close();
		}

		return responses;
	}
}

wrangler 設定にブラウザーバインディングを追加します。

{
	"ai": {
		"binding": "AI",
	},
	"browser": {
		"binding": "BROWSER",
	},
}
[ai]
binding = "AI"

[browser]
binding = "BROWSER"

Browserbase を使う

Browserbase も使えます。Agent 内から Browserbase API を直接呼び出します。

Browserbase API キー を取得したら、シークレット を作成して Agent に追加します。

cd your-agent-project-folder
npx wrangler@latest secret put BROWSERBASE_API_KEY

@cloudflare/puppeteer パッケージをインストールし、Agent 内から Browserbase API を呼び出します。

npm i @cloudflare/puppeteer
import puppeteer from "@cloudflare/puppeteer";

export class MyAgent extends Agent {
	async browse(url) {
		const browser = await puppeteer.connect({
			browserWSEndpoint: `wss://connect.browserbase.com?apiKey=${this.env.BROWSERBASE_API_KEY}`,
		});
		const page = await browser.newPage();
		await page.goto(url);
		const content = await page.content();
		await browser.close();
		return content;
	}
}
import puppeteer from "@cloudflare/puppeteer";

interface Env {
	BROWSERBASE_API_KEY: string;
}

export class MyAgent extends Agent<Env> {
	async browse(url: string) {
		const browser = await puppeteer.connect({
			browserWSEndpoint: `wss://connect.browserbase.com?apiKey=${this.env.BROWSERBASE_API_KEY}`,
		});
		const page = await browser.newPage();
		await page.goto(url);
		const content = await page.content();
		await browser.close();
		return content;
	}
}

役に立ちましたか?