跳转到内容
搜索文档

Browser Agent 示例

最后更新 查看 MarkdownAgent 设置

构建一个 Agent,它可以浏览网页、检查页面、截取屏幕截图,并使用 Browser Run tool 调试前端问题。 Beta

与固定 browser 操作集(click、screenshot、navigate)不同,LLM 编写 JavaScript 代码对 live browser session 运行 CDP 命令——访问协议中所有 domain、command、event 与 type。

提供两个 tool:

Tool 描述
browser_search 查询 CDP spec 以发现 command、event 与 type。spec 从 browser 的 CDP 端点动态获取并缓存。
browser_execute 通过 cdp helper 对 live browser 运行 CDP 命令。每次调用打开新的 browser session,执行代码后关闭。

何时使用 browser tools

当 Agent 需要以下能力时,browser tool 很有用:

  • 检查网页 — DOM 结构、computed style、无障碍树
  • 调试前端问题 — 网络瀑布图、console 错误、性能 trace
  • 抓取结构化数据 — 从渲染后的页面提取内容
  • 截取屏幕截图或 PDF — 网页内容的视觉快照
  • 分析性能 — Core Web Vitals、JavaScript profiling、内存分析

不需要渲染 DOM 的基本页面 fetch 请改用 fetch()

安装

Browser tool 需要 Agents SDK 与 @cloudflare/codemode

npm install agents @cloudflare/codemode ai zod

快速入门

1. 配置 binding

在 wrangler 配置中添加 Browser Run(原 Browser Rendering)与 Worker Loader 绑定(binding):

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

[browser]
binding = "BROWSER"

[[worker_loaders]]
binding = "LOADER"

2. 创建 browser tool

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,
});

若要连接自定义 CDP 端点而非 Browser Run 绑定(binding),请传入 cdpUrl

3. 与 streamText 配合使用

将 browser tool 与其他 tool 一并传入。model 可为任意 AI SDK provider——此处使用 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,
	},
});

两个 tool 均接受含 JavaScript async 箭头函数的 code 参数。Sandbox 按 tool 注入全局变量——browser_searchspecbrowser_executecdp

LLM 使用 browser_search 时,代码通过注入的 spec 对象查询 CDP spec:

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 helper 运行 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 消息 handler 内创建 browser tool,从而获得消息持久化与流式传输:

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 从 browser 的 /json/protocol 端点获取 live CDP 协议并短暂缓存。
  • browser_execute 每次调用打开新的 browser session,向 sandbox 代码暴露小型 cdp 辅助 API,执行完成后关闭 session。
  • LLM 生成的代码在 Worker sandbox 中运行。CDP 流量保留在 host Worker 中。

CDP 辅助 API

browser_execute 内,sandbox 代码可使用以下函数。

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

发送 CDP 命令并等待响应。

参数 类型 描述
method string CDP method,例如 "DOM.getDocument""Network.enable"
params unknown 方法参数
options.timeoutMs number 单条 command 超时(默认:10 秒)
options.sessionId string Target session ID(页面级 command 必需)

cdp.attachToTarget(targetId, options?)

attach 到 target 并获取 session ID。使用 flatten: trueTarget.attachToTarget

参数 类型 描述
targetId string 要 attach 的 target
options.timeoutMs number attach command 超时

返回 sessionId 字符串。

cdp.getDebugLog(limit?)

获取最近 CDP debug log 条目(send、receive、error)。默认最后 50 条,最多 400。

cdp.clearDebugLog()

清空 debug log 缓冲区。

配置

createBrowserTools(options)

返回 AI SDK tool(browser_searchbrowser_execute)。

选项 类型 默认值 描述
browser Fetcher Browser Run 绑定(binding)
cdpUrl string 自定义 CDP 端点的可选 override
cdpHeaders Record<string, string> CDP URL 发现用的 header(例如 Cloudflare Access)
loader WorkerLoader 必需 sandbox 执行的 Worker Loader 绑定(binding)
timeout number 30000 执行超时(毫秒)

必须提供 browsercdpUrl 之一。两者均设置时 cdpUrl 优先。

原始访问

自定义集成时直接 import 构建块:

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 自动 provision browser,因此相同 browser: env.BROWSER 设置在本地与部署后均可用。

仅当有意连接其他 CDP 兼容 browser 端点(如 tunnel 或手动管理的 Chrome 实例)时使用 cdpUrl

安全注意事项

  • LLM 生成的代码在隔离的 Worker sandbox 中运行——每次执行获得独立 Worker 实例
  • 外部网络访问(fetchconnect)在 runtime 层于 sandbox 中被阻止
  • CDP command 通过 Workers RPC 分发——WebSocket 位于 host,不在 sandbox 中
  • CDP spec 保留在服务端——仅查询结果流向 LLM
  • 响应截断至约 6,000 token,防止 context window 溢出

当前限制

  • 每次 execute 调用一个 session — 每次 browser_execute 调用打开新的 browser session。多步 workflow 必须在单个代码块内完成。
  • 无认证 session — browser 启动时不带任何 cookie 或登录 state。
  • 需要 @cloudflare/codemode 作为 peer dependency(对等依赖)。
  • sandbox 中仅支持 JavaScript 执行(不支持 TypeScript 语法)。

直接使用 Puppeteer

若希望以编程方式控制 browser 而不使用 LLM 生成代码,可直接用 Puppeteer 配合 Browser Run API。

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 配置中添加 browser 绑定(binding):

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

[browser]
binding = "BROWSER"

使用 Browserbase

也可在 Agent 内直接调用 Browserbase API 使用 Browserbase

获得 Browserbase API key 后,通过创建 secret 添加到 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;
	}
}

这篇文档对您有帮助吗?