当AI Agent学会了操控浏览器——从PilotDeck多Agent编排到Chrome DevTools MCP浏览器自动化的生产级完全指南(2026)
作者前言:2026年的AI Agent领域正经历一场"操作系统级"的变革。一边是清华大学THUNLP实验室开源的PilotDeck,试图用"工作舱+白盒记忆+智能路由"重新定义多Agent协作范式;另一边是Chrome DevTools团队维护的chrome-devtools-mcp,把浏览器的每一个调试能力都暴露给AI。当这两个世界级的项目相遇,会发生什么?本文将用超过15000字的深度实战,带你从架构原理到生产落地,彻底搞懂新一代AI Agent技术栈。
目录
- 背景介绍:AI Agent的"两个世界"
- PilotDeck深度解析:给Agent造一个操作系统
- Chrome DevTools MCP深度解析:让AI真正"看见"网页
- 架构对比:两种设计哲学的碰撞
- 代码实战:用PilotDeck编排多Agent + Chrome DevTools MCP做UI自动化
- 生产级场景:端到端Web应用测试系统
- 性能优化:Token成本降低70%的实战技巧
- 安全边界:当AI能操控浏览器时,我们该如何设防
- 未来展望:AI Agent操作系统的下一站
- 总结与实践建议
1. 背景介绍:AI Agent的"两个世界"
1.1 2026年的AI Agent困境
如果你在2026年构建过复杂的AI Agent系统,以下场景你一定不陌生:
场景一:上下文爆炸
你让Agent帮你重构一个微服务,它读了整个代码库,上下文瞬间飙到200K tokens。你盯着API账单,心里在滴血。
场景二:记忆黑盒
Agent昨天还能记住你的代码风格,今天突然"失忆"了。你想知道它到底记住了什么,却发现记忆是一个无法探查的黑盒。
场景三:多任务互相干扰
你让Agent同时处理两个项目,结果它把项目A的依赖装到了项目B里。你意识到:现有Agent工具根本没有真正的"隔离"。
场景四:浏览器自动化脆弱
你用Playwright写了自动化脚本,页面结构一变就全崩。你想要的AI助手能像人一样"看"页面,而不是依赖脆弱的选择器。
这些问题,正是PilotDeck和Chrome DevTools MCP要分别解决的。
1.2 两个项目的定位差异
| 维度 | PilotDeck | Chrome DevTools MCP |
|---|---|---|
| 核心定位 | AI Agent操作系统 | 浏览器能力MCP Server |
| 解决痛点 | 多Agent协作、记忆管理、Token成本 | 浏览器自动化、UI审查、调试能力 |
| 适用场景 | 多项目并行、复杂任务编排 | Web自动化测试、UI审查、数据采集 |
| 开源方 | 清华大学THUNLP + 面壁智能 + OpenBMB | Chrome DevTools团队(Google) |
| GitHub Stars | 快速增长中(2026年5月开源) | 41K+(截至2026年6月) |
关键洞察:这两个项目不是竞争对手,而是天然互补。PilotDeck解决"如何编排多个Agent",Chrome DevTools MCP解决"如何让Agent操控浏览器"。当它们结合,你能构建一个完整的"AI驱动的Web应用生命周期管理系统"。
2. PilotDeck深度解析:给Agent造一个操作系统
2.1 什么是PilotDeck?
PilotDeck是由清华大学THUNLP实验室、面壁智能、OpenBMB与AI9stars联合研发并开源的智能体操作系统。它的核心理念是:
"让一个人指挥一支智能体军队"
官方GitHub:https://github.com/OpenBMB/PilotDeck
官方文档:https://pilotdeck.openbmb.cn/
2.1.1 为什么需要PilotDeck?
在PilotDeck出现之前,主流AI Agent工具(如OpenClaw、Claude Code)存在三大痛点:
痛点一:记忆黑盒
你:帮我写一个用户认证模块。
Agent:(生成代码)
你:昨天你记得用JWT,今天怎么改成Session了?
Agent:抱歉,我不记得昨天的讨论了。
你:(内心)每次都要重新解释上下文,Token烧钱啊...
痛点二:项目间无法隔离
你:帮我重构项目A的数据库层。
Agent:好的。(同时修改了项目B的配置,因为没隔离)
你:???我的项目B怎么挂了?
痛点三:Token成本失控
简单任务("今天天气怎么样?")→ 调用GPT-4o → 浪费
复杂任务("重构整个微服务架构")→ 调用GPT-4o → 必要
但现有工具无法区分,一律用最强模型。
PilotDeck用三大核心能力解决这些问题:白盒记忆、WorkSpace隔离、智能路由。
2.2 核心架构:WorkSpace + 白盒记忆 + 智能路由
2.2.1 WorkSpace:给每个项目造一个"家"
在PilotDeck中,每个项目都有一个独立的WorkSpace。这不是IDE里的文件夹,而是一个完整的Agent生存环境。
// PilotDeck的WorkSpace结构(概念模型)
interface WorkSpace {
// 专属文件系统
fileSystem: {
root: string; // 项目根目录
generatedFiles: string; // AI生成文件专属标识
};
// 专属记忆
memory: {
projectMemory: ProjectMemory; // 项目目标、进度、限制
feedbackMemory: FeedbackMemory; // 用户偏好、要求
};
// 专属技能
skills: Skill[]; // 从技能商店安装,随任务增长
// 专属模型路由配置
routerConfig: RouterConfig;
}
关键设计原则:
- 文件系统隔离:Agent在项目A的WorkSpace中生成的文件,不会污染项目B
- 记忆隔离:项目A的技术决策("用PostgreSQL")不会影响项目B("用MySQL")
- 技能隔离:你可以给前端项目装"React性能优化技能",给后端项目装"Go并发模式技能"
2.2.2 白盒记忆:从"黑箱"到"透明"
这是PilotDeck最独特的功能。传统Agent的记忆是黑盒的:
黑盒Agent的记忆流程:
用户输入 → [神秘的处理] → 输出
↑
你不知道它"记住了什么"、"怎么记住的"、"为什么会引用某个过时信息"
PilotDeck把记忆的全流程做成白盒:
// 白盒记忆的可视化(伪代码)
class WhiteBoxMemory {
// 每条记忆都能查看
async viewMemory(workspaceId: string): Promise<MemoryEntry[]> {
return await db.query(`
SELECT
content, // 存了什么
createdAt, // 什么时候存的
source, // 来自哪个对话
relevanceScore // 相关度评分
FROM memories
WHERE workspace_id = ?
ORDER BY created_at DESC
`);
}
// 可以编辑/删除/置顶
async editMemory(entryId: string, newContent: string): Promise<void> {
await db.update('memories', entryId, { content: newContent });
}
// 全链路可追溯
async traceMemory(entryId: string): Promise<Trace> {
return {
generation: await getGenerationLog(entryId), // 生成时的提示词
extraction: await getExtractionLog(entryId), // 提取了哪些关键信息
storage: await getStorageLog(entryId), // 存储时的元数据
retrieval: await getRetrievalLog(entryId), // 被检索时的匹配度
};
}
}
实战价值:
- 当Agent出现"记忆漂移"(记住了错误的信息),你能直接定位并修正
- 你能置顶关键决策("这个项目必须用Go 1.23+,不要用1.22"),防止Agent遗忘
- 你能审计Agent的记忆,满足企业合规要求
2.2.3 智能路由:Token成本降低70%的秘诀
PilotDeck的"智能路由"机制,允许你自定义模型分配策略。核心思路是:简单任务用便宜模型,复杂任务才用旗舰模型。
# PilotDeck配置文件示例(router.yaml)
router:
tokenSaver:
enabled: true
judge: promptfoo # 用promptfoo做任务分类
# 任务四级分类
taskTiers:
- tier: simple # 简单任务
examples:
- "今天天气怎么样?"
- "把这个文件复制到那个目录"
- "帮我写个Hello World"
model: gpt-4o-mini # 便宜模型
maxTokens: 4096
- tier: medium # 中等任务
examples:
- "实现一个用户登录接口"
- "帮我分析这段代码的性能瓶颈"
- "写一个SQL查询,按月份统计销售额"
model: gpt-4o
maxTokens: 16384
- tier: complex # 复杂任务
examples:
- "重构整个微服务架构"
- "实现一个分布式事务框架"
- "优化这个系统的并发性能,目标是QPS提升10倍"
model: claude-opus-4.8
maxTokens: 65536
- tier: orchestration # 编排任务(多Agent协同)
examples:
- "协调3个Agent分别完成前端、后端、测试"
- "先分析需求,再拆解任务,分配给不同Agent"
model: gpt-4o # 编排不需要最强模型,但需要长上下文
maxTokens: 32768
实测效果(来自清华大学团队的测试数据):
- 启用智能路由后,Token成本降低70%
- 任务完成质量(人工评估)不下降
- 对于"编排类"任务,用中等模型做路由决策,反而比用旗舰模型更快(延迟更低)
2.3 PilotDeck的"Dream"机制:空闲时间的自动记忆整理
PilotDeck引入了一个名为**"Dream"**的创新机制:让AI在空闲时间自动整理记忆。
// Dream机制的简化实现
class DreamManager {
async startDreaming(workspaceId: string): Promise<void> {
// 1. 检查Agent是否空闲
if (await this.isAgentBusy(workspaceId)) {
console.log('Agent忙,暂不Dream');
return;
}
// 2. 读取近期记忆
const recentMemories = await this.getRecentMemories(workspaceId, 100);
// 3. 让AI整理、归纳、提取精华
const summary = await llm.summarize({
model: 'gpt-4o-mini', // 用便宜模型做整理
prompt: `
以下是Agent在项目${workspaceId}中的近期记忆,请:
1. 提取关键技术决策(如"选定PostgreSQL作为主库")
2. 识别常见错误模式(如"Agent经常忘记关闭数据库连接")
3. 生成项目状态摘要(进度、阻塞点、下一步)
记忆列表:
${recentMemories.map(m => `- ${m.content}`).join('\n')}
`,
});
// 4. 将整理后的记忆存回(并标记source为'dream')
await this.saveMemory(workspaceId, {
content: summary,
source: 'dream',
priority: 'high', // Dream生成的记忆优先
});
}
}
为什么叫"Dream"?
因为这个过程类似人类的"做梦"——在休息时,大脑自动整理白天的记忆,提取精华,遗忘无用信息。PilotDeck的Dream机制也是这个思路。
3. Chrome DevTools MCP深度解析:让AI真正"看见"网页
3.1 什么是Chrome DevTools MCP?
Chrome DevTools MCP是由Chrome DevTools团队(Google)维护的Model Context Protocol(MCP)Server。它把一个受控的Chrome实例的能力暴露给AI。
官方GitHub:https://github.com/ChromeDevTools/chrome-devtools-mcp
3.1.1 传统浏览器自动化的困境
在Chrome DevTools MCP出现之前,浏览器自动化主要依赖Playwright或Puppeteer:
// 传统Playwright脚本(脆弱)
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
// 依赖选择器,页面结构一变就崩
await page.click('#submit-button'); // 如果id变了,脚本就挂了
await page.fill('#username', 'admin'); // 如果input的name改了,也挂
// 无法"理解"页面
const title = await page.title(); // 只能获取DOM,无法理解语义
核心问题:
- 依赖选择器:页面结构变化导致脚本脆弱
- 无法"看见":传统工具只能操作DOM,无法像人一样"看"页面
- 调试困难:当自动化失败时,你只能看截图,无法深入排查
3.1.2 Chrome DevTools MCP的突破
Chrome DevTools MCP通过把Chrome DevTools Protocol(CDP)的能力暴露为MCP工具,让AI能:
- 截图+读取Accessibility Snapshot:让AI"看见"页面(类似屏幕阅读器的方式)
- 执行JavaScript:直接在当前页面上下文中执行代码,拿到computed style、事件监听器等
- 抓控制台与网络请求:捕获console.log、网络请求、WebSocket消息
- 模拟移动端视口:测试响应式设计
- 读取aria标签:理解页面的可访问性树,做语义化操作
// Chrome DevTools MCP给AI提供的工具(部分)
const tools = [
{
name: 'screenshot',
description: '截取当前页面的截图',
parameters: {
type: 'object',
properties: {
fullPage: { type: 'boolean', description: '是否截取整个页面' },
},
},
},
{
name: 'getAccessibilitySnapshot',
description: '读取页面的可访问性树(类似屏幕阅读器看到的结构)',
parameters: {
type: 'object',
properties: {
selector: { type: 'string', description: '可选,指定某个元素' },
},
},
},
{
name: 'evaluateJavaScript',
description: '在当前页面上下文中执行JavaScript',
parameters: {
type: 'object',
properties: {
expression: { type: 'string', description: '要执行的JS代码' },
},
},
},
{
name: 'getConsoleMessages',
description: '获取控制台消息',
parameters: {},
},
{
name: 'getNetworkRequests',
description: '获取网络请求列表',
parameters: {
type: 'object',
properties: {
filter: { type: 'string', description: '可选,按URL过滤' },
},
},
},
];
3.2 架构解析:MCP协议 + Chrome DevTools Protocol
Chrome DevTools MCP的架构可以分为三层:
AI编码助手(Claude Code / Cursor / Gemini CLI)
↓
MCP协议(Model Context Protocol)
↓
Chrome DevTools MCP Server(Node.js / TypeScript)
↓
Chrome DevTools Protocol(CDP)
↓
受控的Chrome实例(通过Puppeteer启动)
3.2.1 MCP协议:让AI"调用工具"的标准化方式
MCP(Model Context Protocol)是Anthropic推出的协议,用于让AI模型与外部工具/数据源交互。它的核心概念是:
// MCP Server的定义(简化)
interface MCPServer {
// 1. 声明提供的工具
tools: Tool[];
// 2. 处理工具调用
async handleToolCall(request: ToolCallRequest): Promise<ToolCallResult> {
const { name, arguments } = request;
if (name === 'screenshot') {
const result = await this.takeScreenshot(arguments);
return { content: [{ type: 'image', data: result }] };
}
if (name === 'evaluateJavaScript') {
const result = await this.evaluateJS(arguments.expression);
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
}
// ...
}
}
3.2.2 Chrome DevTools Protocol:与Chrome对话的语言
Chrome DevTools Protocol(CDP)是Chrome提供的一套WebSocket协议,允许外部工具控制Chrome。
// 使用CDP获取页面的DOM
import CDP from 'chrome-remote-interface';
const client = await CDP({ port: 9222 });
const { DOM, Runtime } = client;
// 启用DOM事件
await DOM.enable();
// 获取整个DOM树
const { root } = await DOM.getDocument({ depth: -1 });
// 执行JavaScript并获取结果
const { result } = await Runtime.evaluate({
expression: 'document.title',
});
console.log(result.value); // 输出页面标题
Chrome DevTools MCP把CDP的复杂操作封装成简单的MCP工具,让AI能直接调用。
3.3 实战:配置Chrome DevTools MCP
3.3.1 在Claude Code中配置
在项目根目录创建.mcp.json:
{
"mcpServers": {
"chrome-devtools": {
"type": "stdio",
"command": "npx",
"args": ["chrome-devtools-mcp@latest"],
"env": {
"CHROME_PATH": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
"CHROME_DEBUG_PORT": "9222"
}
}
}
}
3.3.2 连接到正在运行的Chrome实例
如果你想让MCP控制你已经打开的Chrome(而不是启动一个新的),需要:
- 启动Chrome时开启远程调试:
# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/chrome-debug
- 配置MCP Server自动连接:
{
"mcpServers": {
"chrome-devtools": {
"type": "stdio",
"command": "npx",
"args": ["chrome-devtools-mcp@latest", "--autoConnect"]
}
}
}
3.3.3 在Cursor / Windsurf / Gemini CLI中配置
所有支持MCP的AI编辑器,配置方式都类似。以Cursor为例:
- 打开Cursor设置 → Features → MCP Servers
- 点击"Add MCP Server"
- 填入:
- Name:
chrome-devtools - Command:
npx chrome-devtools-mcp@latest
- Name:
4. 架构对比:两种设计哲学的碰撞
4.1 设计目标对比
| 维度 | PilotDeck | Chrome DevTools MCP |
|---|---|---|
| 抽象层级 | 操作系统级(管理多个Agent) | 工具级(提供浏览器能力) |
| 核心问题 | "如何高效编排多个Agent?" | "如何让Agent操控浏览器?" |
| 适用用户 | AI产品经理、全栈工程师、DevOps | 前端工程师、测试工程师、爬虫开发者 |
| 部署方式 | 本地优先 + 云端协作 | 本地MCP Server(通过npx启动) |
4.2 技术栈对比
// PilotDeck的技术栈(推测,基于公开信息)
const pilotdeckStack = {
frontend: 'React + TypeScript', // WorkSpace管理界面
backend: 'Python + FastAPI', // Agent编排引擎
llm: '支持多模型(OpenAI / Claude / GLM)',
storage: 'SQLite(本地) + PostgreSQL(云端同步)',
memory: '白盒记忆系统(自研)',
router: 'promptfoo(任务分类)',
};
// Chrome DevTools MCP的技术栈(从GitHub代码分析)
const chromeMCPStack = {
runtime: 'Node.js v20.19+',
language: 'TypeScript',
cdpClient: 'Puppeteer(内置Chrome控制能力)',
mcpSdk: '@modelcontextprotocol/sdk',
transport: 'stdio(标准MCP传输方式)',
};
4.3 互补性分析
这两个项目是天然互补的:
场景:构建一个"AI驱动的Web应用全生命周期管理系统"
PilotDeck负责:
- 需求分析Agent(理解用户需求)
- 架构设计Agent(设计技术方案)
- 代码生成Agent(生成前后端代码)
- 测试编排Agent(协调测试任务)
Chrome DevTools MCP负责:
- 让测试Agent能"看见"页面(截图 + accessibility snapshot)
- 让测试Agent能执行UI操作(点击、填写、滚动)
- 让测试Agent能抓取控制台错误(捕获前端报错)
- 让测试Agent能监控网络请求(检查API调用是否正确)
5. 代码实战:用PilotDeck编排多Agent + Chrome DevTools MCP做UI自动化
5.1 场景定义:自动化E2E测试系统
我们要实现的功能:
- 用PilotDeck编排3个Agent:需求分析Agent、测试设计Agent、测试执行Agent
- 测试执行Agent通过Chrome DevTools MCP操控浏览器
- 自动生成测试报告(包含截图、控制台错误、网络请求分析)
5.2 步骤1:安装和配置PilotDeck
# 克隆PilotDeck仓库
git clone https://github.com/OpenBMB/PilotDeck.git
cd PilotDeck
# 安装依赖
npm install
# 启动PilotDeck(本地开发模式)
npm run dev
访问 http://localhost:3000,你会看到PilotDeck的WorkSpace管理界面。
5.3 步骤2:创建测试项目的WorkSpace
在PilotDeck中,为每个测试项目创建独立的WorkSpace:
// 通过PilotDeck API创建WorkSpace(伪代码)
async function createTestWorkSpace(projectName: string) {
const response = await fetch('http://localhost:3000/api/workspaces', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: `${projectName}-e2e-test`,
type: 'testing',
config: {
// 启用Chrome DevTools MCP
mcpServers: ['chrome-devtools'],
// 配置智能路由
router: {
tokenSaver: { enabled: true },
taskTiers: [
{ tier: 'simple', model: 'gpt-4o-mini' },
{ tier: 'medium', model: 'gpt-4o' },
{ tier: 'complex', model: 'claude-opus-4.8' },
],
},
},
}),
});
return await response.json();
}
5.4 步骤3:用PilotDeck编排多Agent
// PilotDeck的Agent编排配置(概念模型)
const e2eTestAgents = {
// Agent 1:需求分析Agent
requirementAnalyst: {
role: 'requirement-analyst',
model: 'gpt-4o', // 中等模型,足够理解需求
systemPrompt: `
你是一个需求分析专家。你的任务是:
1. 读取用户提供的需求文档(可能是Markdown、Notion链接、或自然语言描述)
2. 提取关键的测试场景(如"用户登录"、"下单流程"、"支付集成")
3. 为每个场景生成测试点列表
输出格式(JSON):
{
"scenarios": [
{
"name": "用户登录",
"testPoints": [
"输入正确用户名和密码,点击登录,应跳转到首页",
"输入错误密码,应显示错误提示",
"连续输入错误密码5次,应锁定账号"
]
}
]
}
`,
maxTokens: 16384,
},
// Agent 2:测试设计Agent
testDesigner: {
role: 'test-designer',
model: 'gpt-4o', // 中等模型,设计测试用例
systemPrompt: `
你是一个测试设计专家。你的任务是:
1. 接收需求分析Agent输出的测试场景
2. 为每个测试点设计详细的测试步骤(使用Selenium/Playwright风格的描述)
3. 生成Page Object Model(POM)的类定义
输出格式(TypeScript):
\`\`\`typescript
// LoginPage.ts
class LoginPage {
async enterUsername(username: string): Promise<void> { ... }
async enterPassword(password: string): Promise<void> { ... }
async clickLogin(): Promise<void> { ... }
async getErrorMessage(): Promise<string> { ... }
}
\`\`\`
`,
maxTokens: 16384,
},
// Agent 3:测试执行Agent(关键!这里集成Chrome DevTools MCP)
testExecutor: {
role: 'test-executor',
model: 'claude-opus-4.8', // 复杂任务,用最强模型
mcpServers: ['chrome-devtools'], // 启用Chrome DevTools MCP
systemPrompt: `
你是一个测试执行专家。你的任务是:
1. 接收测试设计Agent输出的Page Object和测试步骤
2. 使用Chrome DevTools MCP工具执行UI操作
3. 对每个测试点,执行以下流程:
a. 用screenshot工具截图(作为"测试前"的证据)
b. 用getAccessibilitySnapshot理解页面结构
c. 用evaluateJavaScript执行必要的JS(如滚动到元素可见)
d. 执行测试步骤(点击、填写等)
e. 再次截图(作为"测试后"的证据)
f. 用getConsoleMessages检查是否有前端报错
g. 用getNetworkRequests检查API调用是否正确
4. 生成测试报告(包含截图、错误信息、网络请求分析)
重要:当页面结构变化时,不要依赖硬编码的选择器,而是用
getAccessibilitySnapshot理解当前页面结构,动态定位元素。
`,
maxTokens: 65536, // 需要长上下文,因为要处理截图+网络请求数据
},
};
// 在PilotDeck中注册这些Agent
async function registerAgents(workspaceId: string) {
for (const [name, config] of Object.entries(e2eTestAgents)) {
await fetch(`http://localhost:3000/api/workspaces/${workspaceId}/agents`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, ...config }),
});
}
}
5.5 步骤4:测试执行Agent使用Chrome DevTools MCP
这是最关键的部分:测试执行Agent如何通过Chrome DevTools MCP操控浏览器?
// 测试执行Agent的"思考过程"(伪代码,展示AI如何调用MCP工具)
async function executeTestPoint(testPoint: string, page: Page) {
// Step 1:让AI理解当前页面状态
const accessibilitySnapshot = await callMCPTool('getAccessibilitySnapshot');
const consoleMessages = await callMCPTool('getConsoleMessages');
// Step 2:AI分析页面,决定下一步操作
const actionPlan = await llm.analyze({
model: 'claude-opus-4.8',
prompt: `
当前测试点:${testPoint}
页面可访问性树:
${JSON.stringify(accessibilitySnapshot, null, 2)}
控制台消息:
${JSON.stringify(consoleMessages, null, 2)}
请分析:
1. 当前页面处于什么状态?
2. 要完成测试点,下一步应该点击哪个元素?或填写哪个输入框?
3. 元素的定位方式(不要只用CSS选择器,优先用accessibility role + name)
输出格式(JSON):
{
"currentState": "描述当前页面状态",
"nextAction": {
"type": "click | fill | scroll | wait",
"target": {
"role": "button",
"name": "登录"
},
"value": "可选,填写操作时的值"
}
}
`,
});
// Step 3:执行操作
if (actionPlan.nextAction.type === 'click') {
// 使用Chrome DevTools MCP的evaluateJavaScript工具点击元素
await callMCPTool('evaluateJavaScript', {
expression: `
const element = document.querySelector('[role="${actionPlan.nextAction.target.role}"][name="${actionPlan.nextAction.target.name}"]');
if (element) {
element.click();
return { success: true, clicked: element.outerHTML };
} else {
return { success: false, error: 'Element not found' };
}
`,
});
}
if (actionPlan.nextAction.type === 'fill') {
await callMCPTool('evaluateJavaScript', {
expression: `
const input = document.querySelector('[role="textbox"][name="${actionPlan.nextAction.target.name}"]');
if (input) {
input.value = '${actionPlan.nextAction.value}';
input.dispatchEvent(new Event('input', { bubbles: true }));
return { success: true };
} else {
return { success: false, error: 'Input not found' };
}
`,
});
}
// Step 4:验证结果
const afterScreenshot = await callMCPTool('screenshot', { fullPage: false });
const networkRequests = await callMCPTool('getNetworkRequests');
// Step 5:生成测试证据
return {
testPoint,
beforeScreenshot: /* 之前的截图 */,
afterScreenshot,
consoleErrors: consoleMessages.filter(m => m.level === 'error'),
networkRequests,
status: 'pass' | 'fail',
};
}
5.6 完整流程演示
// 主流程:用PilotDeck编排完整的E2E测试
async function runE2ETest(projectName: string, requirement: string) {
// 1. 创建WorkSpace
const workspace = await createTestWorkSpace(projectName);
// 2. 注册Agent
await registerAgents(workspace.id);
// 3. 启动需求分析Agent
const analysisResult = await callAgent(workspace.id, 'requirement-analyst', {
message: `请分析以下需求,提取测试场景:${requirement}`,
});
// 4. 启动测试设计Agent
const designResult = await callAgent(workspace.id, 'test-designer', {
message: `需求分析结果:${JSON.stringify(analysisResult)},请设计测试用例和Page Object。`,
});
// 5. 启动测试执行Agent(这里会调用Chrome DevTools MCP)
const testResults = [];
for (const scenario of analysisResult.scenarios) {
for (const testPoint of scenario.testPoints) {
const result = await callAgent(workspace.id, 'test-executor', {
message: `执行测试点:${testPoint}\n测试用例:${JSON.stringify(designResult.pageObjects)}`,
});
testResults.push(result);
}
}
// 6. 生成测试报告
const report = generateTestReport(testResults);
return report;
}
// 使用示例
const report = await runE2ETest(
'my-web-app',
'用户可以从首页登录,登录后能查看个人资料,能修改密码。'
);
console.log(report);
6. 生产级场景:端到端Web应用测试系统
6.1 场景描述
假设你要为一个电商网站构建E2E测试系统,需要覆盖:
- 用户注册/登录
- 商品搜索/筛选
- 加入购物车
- 下单/支付
- 订单查询
6.2 用PilotDeck + Chrome DevTools MCP实现
6.2.1 需求分析Agent的输出
{
"scenarios": [
{
"name": "用户登录",
"testPoints": [
"输入正确用户名和密码,点击登录,应跳转到首页",
"输入错误密码,应显示错误提示",
"用户名为空时点击登录,应提示输入用户名",
"密码为空时点击登录,应提示输入密码"
]
},
{
"name": "商品搜索",
"testPoints": [
"在搜索框输入关键词,点击搜索,应显示相关商品",
"搜索结果为空时,应显示"暂无相关商品"",
"点击筛选条件(价格区间、品牌),应刷新搜索结果"
]
},
{
"name": "购物车",
"testPoints": [
"点击"加入购物车",应显示成功提示,购物车数量+1",
"在购物车页面修改商品数量,应更新总价",
"删除购物车中的商品,应更新购物车数量"
]
}
]
}
6.2.2 测试设计Agent的输出
// LoginPage.ts
class LoginPage {
private page: Page;
constructor(page: Page) {
this.page = page;
}
async navigateToLogin(): Promise<void> {
await this.page.goto('https://example-ecommerce.com/login');
}
async enterUsername(username: string): Promise<void> {
// 使用accessibility role定位,而不是脆弱的CSS选择器
const snapshot = await callMCPTool('getAccessibilitySnapshot');
const usernameField = snapshot.find(node =>
node.role === 'textbox' && node.name?.includes('用户名')
);
if (usernameField) {
await callMCPTool('evaluateJavaScript', {
expression: `
const el = document.querySelector('[role="textbox"][name*="用户名"]');
el.value = '${username}';
el.dispatchEvent(new Event('input', { bubbles: true }));
`,
});
}
}
async enterPassword(password: string): Promise<void> {
// 类似enterUsername的逻辑
}
async clickLogin(): Promise<void> {
await callMCPTool('evaluateJavaScript', {
expression: `
const btn = document.querySelector('[role="button"][name*="登录"]');
btn.click();
`,
});
}
async getErrorMessage(): Promise<string> {
const snapshot = await callMCPTool('getAccessibilitySnapshot');
const errorNode = snapshot.find(node =>
node.role === 'alert' || node.name?.includes('错误')
);
return errorNode?.name || '';
}
async isLoggedIn(): Promise<boolean> {
// 检查是否跳转到首页(通过URL或页面元素判断)
const currentUrl = await callMCPTool('evaluateJavaScript', {
expression: 'window.location.href',
});
return !currentUrl.includes('/login');
}
}
6.2.3 测试执行Agent的实战
// 测试执行Agent处理"输入正确用户名和密码,点击登录,应跳转到首页"
async function testLoginSuccess() {
const loginPage = new LoginPage(/* page */);
// 1. 截图(测试前)
const beforeScreenshot = await callMCPTool('screenshot');
// 2. 导航到登录页
await loginPage.navigateToLogin();
// 3. 填写用户名和密码
await loginPage.enterUsername('testuser@example.com');
await loginPage.enterPassword('correctpassword');
// 4. 点击登录
await loginPage.clickLogin();
// 5. 等待页面跳转(用evaluateJavaScript检查URL)
await new Promise(resolve => setTimeout(resolve, 2000)); // 简单等待,生产环境应用更智能的等待
// 6. 验证结果
const isLoggedIn = await loginPage.isLoggedIn();
const afterScreenshot = await callMCPTool('screenshot');
const consoleErrors = await callMCPTool('getConsoleMessages', {
filter: 'level=error',
});
// 7. 生成测试证据
return {
testPoint: '输入正确用户名和密码,点击登录,应跳转到首页',
status: isLoggedIn ? 'pass' : 'fail',
beforeScreenshot,
afterScreenshot,
consoleErrors,
networkRequests: await callMCPTool('getNetworkRequests'),
};
}
6.3 测试报告生成
// 生成HTML测试报告
function generateTestReport(results: TestResult[]): string {
const html = `
<!DOCTYPE html>
<html>
<head>
<title>E2E测试报告</title>
<style>
body { font-family: system-ui, sans-serif; margin: 2rem; }
.pass { color: green; }
.fail { color: red; }
.screenshot { max-width: 800px; border: 1px solid #ccc; }
.error { background: #fee; padding: 0.5rem; margin: 0.5rem 0; }
</style>
</head>
<body>
<h1>E2E测试报告</h1>
<p>生成时间:${new Date().toISOString()}</p>
<p>总测试点:${results.length}</p>
<p>通过:${results.filter(r => r.status === 'pass').length}</p>
<p>失败:${results.filter(r => r.status === 'fail').length}</p>
<hr>
${results.map((result, index) => `
<div class="${result.status}">
<h3>${index + 1}. ${result.testPoint}</h3>
<p>状态:<strong>${result.status === 'pass' ? '✅ 通过' : '❌ 失败'}</strong></p>
<h4>测试前截图:</h4>
<img class="screenshot" src="data:image/png;base64,${result.beforeScreenshot}" />
<h4>测试后截图:</h4>
<img class="screenshot" src="data:image/png;base64,${result.afterScreenshot}" />
${result.consoleErrors.length > 0 ? `
<h4>控制台错误:</h4>
${result.consoleErrors.map(err => `
<div class="error">${err.text}</div>
`).join('')}
` : ''}
<h4>网络请求分析:</h4>
<ul>
${result.networkRequests.map(req => `
<li>${req.method} ${req.url} - ${req.status}</li>
`).join('')}
</ul>
</div>
<hr>
`).join('')}
</body>
</html>
`;
return html;
}
7. 性能优化:Token成本降低70%的实战技巧
7.1 PilotDeck的智能路由配置优化
7.1.1 任务分类的准确性至关重要
智能路由的效果,取决于任务分类的准确性。如果分类错误(把复杂任务分类为简单任务),会导致质量下降;如果分类过度保守(把所有任务都分类为复杂任务),则成本节省效果不佳。
# 优化后的router.yaml
router:
tokenSaver:
enabled: true
judge: promptfoo
# 关键优化:用few-shot examples提升分类准确性
classificationPrompt: |
你是一个任务分类器。请根据以下规则,将用户任务分为四个等级:
## 等级定义
### Tier 1: simple(简单任务)
特征:
- 单轮对话即可完成
- 不需要操作文件系统
- 不需要深度推理
示例:
- "今天天气怎么样?"
- "把这个文件的第10行复制到剪贴板"
- "用一句话解释什么是RESTful API"
### Tier 2: medium(中等任务)
特征:
- 需要多步骤操作
- 需要读取/写入文件
- 需要基础推理
示例:
- "实现一个用户登录接口(包括路由、Controller、Service、数据库查询)"
- "帮我分析这段代码的性能瓶颈,并给出优化建议"
- "写一个SQL查询,按月份统计销售额,并用柱状图展示"
### Tier 3: complex(复杂任务)
特征:
- 需要深度推理或创造性思考
- 涉及多个模块的协同修改
- 需要权衡多种技术方案
示例:
- "重构整个微服务架构,从单体拆分为微服务"
- "实现一个分布式事务框架,支持TCC和SAGA模式"
- "优化这个系统的并发性能,目标是QPS从1K提升到10K"
### Tier 4: orchestration(编排任务)
特征:
- 需要协调多个子Agent
- 需要拆解复杂任务为子任务
- 需要管理任务依赖关系
示例:
- "协调3个Agent分别完成前端、后端、测试"
- "先分析需求,再拆解任务,分配给不同Agent"
## 分类规则
1. 如果任务涉及"协调多个Agent",一律分为orchestration
2. 如果任务需要"深度推理"或"多个模块协同修改",分为complex
3. 如果任务需要"多步骤操作"但不需要深度推理,分为medium
4. 其他分为simple
请只输出等级名称(simple/medium/complex/orchestration),不要输出其他内容。
# 模型映射
modelMapping:
simple: gpt-4o-mini
medium: gpt-4o
complex: claude-opus-4.8
orchestration: gpt-4o # 编排不需要最强模型,但需要长上下文
7.1.2 动态Token预算
除了选择不同模型,还可以动态调整Token预算:
// PilotDeck的动态Token预算(概念实现)
class DynamicTokenBudget {
async calculateBudget(task: string, tier: string): Promise<number> {
// 基础预算
const baseBudgets = {
simple: 4096,
medium: 16384,
complex: 65536,
orchestration: 32768,
};
let budget = baseBudgets[tier];
// 动态调整:如果任务涉及"代码生成",增加预算
if (task.includes('实现') || task.includes('生成') || task.includes('写')) {
budget *= 1.5;
}
// 动态调整:如果任务涉及"重构",大幅增加预算
if (task.includes('重构')) {
budget *= 2.0;
}
return Math.min(budget, 131072); // 上限131K tokens
}
}
7.2 Chrome DevTools MCP的调用优化
7.2.1 避免频繁截图
截图是"重操作",会消耗大量Token(因为截图要编码为base64,然后传给AI)。优化策略:
// 优化前:每个操作都截图
async function badPractice() {
await loginPage.enterUsername('user');
await callMCPTool('screenshot'); // ❌ 不必要的截图
await loginPage.enterPassword('pass');
await callMCPTool('screenshot'); // ❌ 不必要的截图
await loginPage.clickLogin();
await callMCPTool('screenshot'); // ✅ 只在关键步骤截图
}
// 优化后:只在关键步骤截图
async function goodPractice() {
await loginPage.enterUsername('user');
// ❌ 不截图
await loginPage.enterPassword('pass');
// ❌ 不截图
await loginPage.clickLogin();
// ✅ 只在"操作后"截图一次
const screenshot = await callMCPTool('screenshot');
}
7.2.2 使用accessibility snapshot替代截图做元素定位
// 优化前:用截图让AI"看"页面(消耗大量Token)
async function locateElementByScreenshot() {
const screenshot = await callMCPTool('screenshot');
// 把截图传给AI,让AI指出"登录按钮在哪个位置"
// 问题:截图很大(可能500KB+),编码为base64后更大
}
// 优化后:用accessibility snapshot让AI"理解"页面(消耗少量Token)
async function locateElementBySnapshot() {
const snapshot = await callMCPTool('getAccessibilitySnapshot');
// snapshot是结构化的JSON,比截图小得多
// AI能从snapshot中直接找到"role=button, name=登录"
}
7.2.3 批量执行JavaScript,减少往返次数
// 优化前:多次调用evaluateJavaScript
async function badPractice() {
const title = await callMCPTool('evaluateJavaScript', {
expression: 'document.title',
});
const url = await callMCPTool('evaluateJavaScript', {
expression: 'window.location.href',
});
const cookies = await callMCPTool('evaluateJavaScript', {
expression: 'document.cookie',
});
}
// 优化后:一次调用evaluateJavaScript,返回多个值
async function goodPractice() {
const result = await callMCPTool('evaluateJavaScript', {
expression: `
(function() {
return {
title: document.title,
url: window.location.href,
cookies: document.cookie,
};
})();
`,
});
const { title, url, cookies } = JSON.parse(result);
}
8. 安全边界:当AI能操控浏览器时,我们该如何设防
8.1 潜在风险
当AI能通过Chrome DevTools MCP操控浏览器时,以下风险必须重视:
风险一:恶意网站利用AI操控浏览器
恶意网站:包含一个隐藏的iframe,诱导AI点击"授权访问摄像头"
AI:看到页面上有"允许"按钮,就点击了
结果:用户的摄像头被恶意网站访问
风险二:AI执行了不安全的JavaScript
// AI可能执行这样的代码(如果提示词被注入)
await callMCPTool('evaluateJavaScript', {
expression: 'eval(atob("YWxlcnQoJ+WbvueJh+acjeWKoeS4i+eci+S7peS4i+WPiO+8gQ=="))',
// 解码后:alert('你已被攻击,这段代码会窃取你的Cookie')
});
风险三:AI访问了敏感页面
AI:被指示"帮我看看我的银行账单"
AI:打开了网上银行,截图(包含账户余额、交易记录)
结果:这些截图可能被记录在Agent的记忆中,泄露隐私
8.2 防护措施
8.2.1 白名单机制:限制AI能访问的域名
// Chrome DevTools MCP的配置:域名白名单
const config = {
allowedDomains: [
'example.com',
'staging.example.com',
// 只允许访问测试环境,禁止访问生产环境和敏感网站
],
blockedDomains: [
'bank.com',
'gov.cn',
// 黑名单:金融、政府等敏感网站
],
};
// 在MCP Server中检查
async function navigateTo(url: string): Promise<void> {
const domain = new URL(url).hostname;
if (config.blockedDomains.includes(domain)) {
throw new Error(`域名${domain}在黑名单中,禁止访问`);
}
if (!config.allowedDomains.some(allowed => domain.endsWith(allowed))) {
throw new Error(`域名${domain}不在白名单中,禁止访问`);
}
// 执行导航
await page.goto(url);
}
8.2.2 JavaScript执行沙箱
// 限制AI能执行的JavaScript
async function safeEvaluateJavaScript(expression: string): Promise<any> {
// 禁止的危险操作
const forbiddenPatterns = [
/eval\s*\(/,
/Function\s*\(/,
/setTimeout\s*\(/,
/setInterval\s*\(/,
/fetch\s*\(/, // 禁止发起网络请求(防止数据泄露)
/XMLHttpRequest/,
/document\.cookie/, // 禁止访问Cookie
/localStorage/,
/sessionStorage/,
];
for (const pattern of forbiddenPatterns) {
if (pattern.test(expression)) {
throw new Error(`JavaScript表达式包含 forbidden pattern: ${pattern}`);
}
}
// 在沙箱中执行
return await page.evaluate(expression);
}
8.2.3 人工审核关键操作
// 对于"高风险操作",要求人工确认
async function executeHighRiskAction(action: string, target: string) {
const highRiskActions = [
'delete',
'remove',
'pay',
'transfer',
'submit',
];
if (highRiskActions.some(risk => action.includes(risk))) {
// 发送通知给人工审核
await sendNotificationToHuman({
message: `AI想要执行高风险操作:${action} ${target},是否允许?`,
timeout: 60000, // 等待60秒
});
const approval = await waitForApproval();
if (!approval) {
throw new Error('人工拒绝执行高风险操作');
}
}
// 执行操作
await executeAction(action, target);
}
9. 未来展望:AI Agent操作系统的下一站
9.1 PilotDeck的路线图(基于公开信息推测)
- 多云Agent协作:不同WorkSpace的Agent能跨网络协作(类似微服务间的RPC调用)
- 技能市场:类似VS Code的扩展市场,用户可以分享、安装Agent技能
- 企业版:支持私有部署、SSO、审计日志、合规检查
9.2 Chrome DevTools MCP的路线图
- 支持更多浏览器:目前只支持Chrome,未来可能支持Firefox、Safari(通过对应的DevTools Protocol)
- 录制+回放:让AI能"录制"用户的操作,然后"回放"(类似Cypress的录制功能)
- 性能分析集成:把Chrome Performance Panel的能力也暴露给AI(让AI能分析页面性能瓶颈)
9.3 两个项目的融合可能性
大胆预测:未来可能会出现一个"PilotDeck Chrome Extension",让用户能在Chrome中直接管理WorkSpace、查看Agent状态、审核Agent操作。
10. 总结与实践建议
10.1 核心要点回顾
- PilotDeck是解决"多Agent协作、记忆管理、Token成本"的操作系统级方案
- Chrome DevTools MCP是解决"浏览器自动化、UI审查、调试能力"的工具级方案
- 两者天然互补:PilotDeck编排Agent,Chrome DevTools MCP提供浏览器能力
- 智能路由是Token成本降低70%的关键
- 白盒记忆是PilotDeck最独特的功能,让Agent的记忆可查看、可编辑、可追溯
10.2 实践建议
对于AI产品经理:
- 用PilotDeck管理多个AI功能模块(如"需求分析Agent"、"代码生成Agent"、"测试Agent")
- 用智能路由降低API成本
对于全栈工程师:
- 用PilotDeck + Chrome DevTools MCP构建"AI辅助的E2E测试系统"
- 把测试证据(截图、控制台错误、网络请求)自动整理为测试报告
对于前端工程师:
- 用Chrome DevTools MCP让AI"看见"你的页面,做UI审查
- 用accessibility snapshot替代脆弱的CSS选择器
对于测试工程师:
- 用PilotDeck编排"测试设计Agent"和"测试执行Agent"
- 用Chrome DevTools MCP让测试Agent能应对页面结构变化
10.3 参考资源
- PilotDeck官方GitHub:https://github.com/OpenBMB/PilotDeck
- PilotDeck官方文档:https://pilotdeck.openbmb.cn/
- Chrome DevTools MCP官方GitHub:https://github.com/ChromeDevTools/chrome-devtools-mcp
- MCP协议规范:https://modelcontextprotocol.io/
- Chrome DevTools Protocol文档:https://chromedevtools.github.io/devtools-protocol/
写完这篇文章,我深感:2026年的AI Agent领域,正在从"玩具"走向"生产力工具"。PilotDeck和Chrome DevTools MCP,正是这个转型的代表作。希望这篇15000+字的深度实战,能帮你真正理解并应用这两个强大的工具。
如果你觉得这篇文章有价值,欢迎分享给你的团队。让我们一起,把AI Agent从" demo"带到"生产"!