我见过很多团队第一次跑通Playwright MCP时的反应:Demo很惊艳,Demo之后的真实任务很狼狈。
问题通常不在模型。在于大多数人只看到了“Agent点了一下按钮”,没看到这句话从输入框到浏览器之间,到底经过了哪些层、每一层做了什么判断、哪一层最容易被忽略。
今天把这条链路从头拆到尾。拆完你会发现,Playwright MCP真正值钱的地方,不是“让AI能点按钮”,是它把页面翻译成了Agent能准确理解、稳定操作、事后可追溯的确定性上下文。
先看全貌:五层,一条反馈环
整条链路可以拆成五层,形成一个闭环:
自然语言任务 → AI Agent → MCP客户端 → Playwright MCP Server → 浏览器
以TodoMVC为例,用户输入“打开页面,新增三条任务,标记第一条为完成,验证剩余数量为2”。这句话不会直接变成任何一行代码。它依次穿过:
AI Agent 理解意图,决定“下一步该做什么”
MCP客户端 把决策翻译成符合MCP协议的工具调用请求
MCP Server 把请求路由到对应的Playwright执行器
浏览器 执行操作,返回页面状态
反馈回流 页面状态被转成快照,回到Agent手里,驱动下一次决策
闭环的关键在最后一步。 传统脚本是“代码决定路径”,Agent是“页面反馈决定路径”。这个区别,决定了后面所有工程细节。
第一层:自然语言怎么变成“调用哪个工具”?
Agent手里有一份工具清单。MCP Server启动时,会向客户端声明它暴露了哪些工具、每个工具的参数Schema是什么。
核心工具就那么几个:browser_navigate负责导航,browser_snapshot获取页面快照,browser_click点击元素,browser_type输入文本,browser_verify_text_visible做测试断言。测试类工具需要显式启用--caps=testing才会暴露,默认不开,避免工具列表膨胀、增加模型选错工具的概率。
Agent收到“打开TodoMVC页面”这句话后,做的决策不是“写一行page.goto()”,而是:从工具清单里选出browser_navigate,生成符合参数Schema的调用。
这一步的关键认知是:Agent不写代码,Agent选工具。 代码层的Playwright API被封装成了语义化的工具描述,Agent只需要理解“我要导航”,不需要知道page.goto和browser.newPage之间是什么关系。
第二层:MCP协议层——请求怎么到达浏览器?
Agent的决策通过JSON-RPC 2.0发送给MCP Server。消息格式是标准的:{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "browser_click", "arguments": {...}}}。
MCP Server收到后,走一条固定的路由链:协议层解析 → 路由到对应的Tool处理函数 → Playwright执行引擎在指定的BrowserContext和Page上执行 → 结果或异常序列化后返回。
这里有一个容易被忽略的工程细节:Playwright MCP把browser context/page的状态绑定在MCP session上。如果客户端每次工具调用都新建session,就会出现“上一次打开的页面,下一次调用看不到”的情况。这不是Playwright MCP的bug,是session生命周期管理的问题。正确的做法是让同一个任务线程复用同一个session,同时按user_id + thread_id做隔离,避免跨用户共享浏览器状态。
这一层决定了链路的“状态连续性”。 状态断了,Agent就会在一个全新的空白页面上继续操作,快照再准也没用。
第三层:快照——Agent到底“看见”了什么?
这是整条链路最核心的设计,也是最容易被误解的一层。
很多人以为Agent是“截图看一眼,猜按钮在哪”。Playwright MCP的默认方案不是视觉截图,是可访问性快照(Accessibility Snapshot) ——把浏览器的可访问性树转成结构化文本,包含元素角色、可访问名称、状态,以及一个关键的ref标识。
一个TodoMVC的快照长这样:
- textbox "What needs to be done?" [ref=e5]
- checkbox "Toggle Todo" [ref=e10]
- link "All" [ref=e20]
模型看到的不是像素,是“这是一个textbox,它的可访问名称是What needs to be done,它的引用是e5” 。接下来它调用browser_type时,参数不是CSS选择器,而是{"target": "e5", "text": "Buy groceries", "submit": true}。
为什么不用截图? 成本数据很直接:快照约200到400个token,截图走视觉模型要3000到5000个token,差了将近一个数量级。更重要的是可靠性:快照的ref指向的是确定的元素,截图是坐标猜测,布局一变坐标就废了。
但快照有一个必须记住的边界:ref只在当前快照内有效。 页面导航或DOM更新后,旧的ref就失效了,必须重新browser_snapshot获取新的引用。很多“Agent点错元素”的问题,根源就是复用了已经过期的ref。
第四层:从“ref”到“操作”——闭环是怎么合上的?
Agent拿到快照后,按“观察 → 决策 → 操作 → 再观察”的节奏推进。TodoMVC的完整调用序列是这样的:
browser_navigate 打开页面
browser_snapshot 获取初始快照,确认列表为空
browser_type 依次输入三条任务
每次输入后重新browser_snapshot ,确认新任务出现在列表中
browser_click 勾选第一条任务的checkbox
browser_verify_text_visible 验证“2 items left”可见
切换Active/Completed筛选,再次快照验证
注意第4步。 它不是可选的“优化”,是闭环成立的前提。页面在每次提交后都可能重新渲染,旧ref失效,Agent必须重新“看”一眼才能做出下一步正确决策。
这条“操作→快照→再操作”的循环,就是Playwright MCP从自然语言走到浏览器操作的完整物理路径。
第五层:最危险的一层——工具成功 ≠ 测试通过
链路跑通之后,大多数人会松一口气。但真正的坑在这一层。
browser_click返回成功,只证明点击动作被执行了。它不证明业务结果正确。前端可能乐观更新了Toast,接口实际返回了500;接口可能返回了成功,退款单却没有落库;落库了,状态机可能流转错了。
这就是AI UI自动化最常见的“假通过”。Agent很擅长“把流程跑完”,但不天然知道“页面说成功”和“业务真的成功”是两件事。
工程上的解法不是反复提醒Agent“要校验接口”,而是把规则固化成Skill:不允许只用Toast或按钮状态作为成功断言;必须捕获关键请求并校验状态码和核心字段;创建类操作必须通过业务查询接口确认最终状态。
断言至少要分三层:工具调用成功、页面状态符合预期、业务断言通过。对于支付、订单、库存这类场景,第三层不能省。
这条链路适合什么、不适合什么
Playwright MCP不是“替代所有Playwright脚本”的方案。
适合的场景:新页面探索、流程理解、动态页面交互、辅助构造自动化步骤。Agent能根据页面反馈动态调整路径,这是固定脚本做不到的。
不适合的场景:高频执行、流程稳定、业务断言明确的回归测试。传统Playwright脚本的执行路径可控、CI集成成熟,在这个场景里依然是更优解。
一个实用的选型信号:需要Agent“看着页面决定下一步”的任务,走MCP;流程固定、每晚跑同一套的批量任务,走CLI或原生脚本。两者不是替代关系,是不同任务形态的不同入口。
最后
Playwright MCP的调用链路,表面上看是“自然语言→工具调用→浏览器操作”。拆开看,真正让这条链路成立的是三件事:快照把页面变成了Agent能读懂的确定性文本,ref把“理解”和“操作”用同一个标识连了起来,反馈循环让Agent在每一步之后都有机会纠正自己。
工具会变,模型会变,但这套“观察→决策→操作→再观察”的闭环逻辑不会变。理解了链路,你就不会在Agent点错按钮的时候只想着换模型——你会先检查:快照是不是过期了?session是不是断了?断言是不是只看了Toast?
本文部分内容参考了霍格沃兹测试开发学社整理的相关技术资料,主要涉及软件测试、自动化测试、测试开发及 AI 测试等内容,侧重测试实践、工具应用与工程经验整理。