深度对比四大代表性框架:
- 移动端 Agent :
minitap/mobile-use(AndroidWorld 100% 准确率多智能体架构)- Web Agent(状态机剪枝 + DOM 视觉流派) :
browser-use- Web Agent(官方 MCP + YAML 无障碍树流派) :
@playwright/mcp(Playwright MCP)- Web Agent(Rust 直连 CDP + 极致压缩流派) :
vercel-labs/agent-browser
一、 第一性原理:GUI Agent 面临的"根本物理矛盾"
任何操作图形界面(Web 浏览器、Android、iOS、桌面 OS)的 AI Agent,本质上都在求解一个 POMDP(部分可观测马尔可夫决策过程)。在工程落地上,它面临一个不可调和的物理矛盾:
Total Context=∑t=1T(Observationt+Thoughtt+Actiont)
- 瞬时观测极重(Heavy Transient Observation) : 一帧未经压缩的原始 HTML DOM / Android UI Hierarchy XML 加上高清截图(Screenshot),动辄消耗 5,000 ~ 15,000 Tokens。
- 状态极易过期(Rapid State Invalidation) : 一旦发生页面跳转、弹窗或滚动,3 秒前的 DOM 节点坐标(
bounds)和元素引用就彻底失效。 - 长程累积灾难(Trajectory Explosion & Attention Pollution) : 如果按传统 Chatbot 的方式把每一轮的原始观测都留在上下文里,走完 20 步就会:
- 撑爆上下文与账单:即使命中 LLM 的 Prefix KV Cache(打 1~2.5 折),累计读取数百万 Cached Tokens 依然昂贵且拖慢 TTFT(首字延迟)。
- 引发坐标幻觉(Grounding Hallucination) :同一个 App/网站的不同页面有大量同名
resource-id或 CSS class,历史帧的旧坐标会严重污染大模型对当前帧的 Attention 定位。
要理解业界是如何解决这一矛盾的,我们按照数据流动的物理顺序:底层通信协议 → 感知提取与压缩 → 上下文与缓存管理 → 决策与工具执行 来逐层拆解。
二、 浏览器通信协议底座:CDP → Playwright → Rust 直连
在 Web 自动化中,Agent 通过浏览器内核暴露的底层调试协议进行通信,其技术栈演进如下:
text
┌─────────────────────────────────────────────────────────────────────────┐
│ AI Agent 层: browser-use (Python) │ Playwright MCP │ agent-browser │
├──────────────────────────────────────┴────────────────┼─────────────────┤
│ 自动化框架层: Playwright (Microsoft) │ (Rust 直接实现) │
├──────────────────────┬───────────────┬────────────────┼─────────────────┤
│ 浏览器通信协议层: │ Firefox 私有 │ WebKit 私有 │ │
│ │ 调试协议 │ 调试协议 │ CDP 协议 │
│ ├───────────────┴────────────────┤ (WebSocket JSON)│
│ │ CDP (Chrome 调试协议) │ │
├──────────────────────┴────────────────────────────────┴─────────────────┤
│ 浏览器内核层: Chromium / Chrome / Edge / Firefox / Safari │
└─────────────────────────────────────────────────────────────────────────┘
- CDP(Chrome DevTools Protocol,底层原子协议) : Chrome 内核暴露的双向 WebSocket JSON-RPC 协议(即按
F12开发者工具与浏览器内核通信的协议)。它只提供最底层的原子指令(如DOM.getContentQuads获取坐标、Input.dispatchMouseEvent注入硬件级鼠标事件、Accessibility.getFullAXTree拉取无障碍树),本身没有"等待渲染"或"遮挡检查"的概念。 - Playwright(微软开源的现代浏览器自动化框架) : 由原 Google Puppeteer 创始团队跳槽微软后从零重写,已成为 Web 自动化的事实标准。
- 跨内核协议层 :控制 Chromium 时 100% 基于原生 CDP;控制 Firefox 和 WebKit(Safari 内核)时,通过直接修改浏览器 C++ 源码打补丁,植入了类 CDP 的双向调试协议,实现三大内核统一控制。
- 核心价值(Actionability 可操作性机制) :在裸 CDP 之上封装了 自动等待(Auto-Waiting)与安全预检 ------执行
click前自动轮询确认元素已挂载、可见、动画停止(位置稳定)、未被遮罩层挡住(Hit-Test),并支持自动穿透 Shadow DOM / Iframe 及导出ariaSnapshot。
- 为什么
agent-browser抛弃 Playwright 自己用 Rust 直连 CDP? Playwright 基于 Node.js 运行时,在沙箱/容器中依赖重、冷启动慢。agent-browser将 Playwright 最精华的机制(CDP 通信 + A11y 树提取 + 点击前遮挡/可见性预检 Fail Early )用 Rust 重写为零依赖的单文件二进制 CLI,在 Chromium 场景下同时兼顾了 Playwright 级的可靠性与原生二进制的极致速度。
三、 感知层(Perception):DOM Tree vs. A11y Tree 与无障碍树压缩
连接上设备后,Agent 面临的第一个核心设计决策是:到底从内核里抓取什么结构化数据来代表当前屏幕?又如何将其压缩?
1. 什么是 A11y 与 A11y Compression(无障碍树压缩)?
A11y(Accessibility,无障碍/可访问性) :无论是浏览器(Chrome CDP)、Android(AccessibilityNodeInfo)还是 iOS(AXElement),系统内核都会维护一棵 Accessibility Tree(无障碍树)。它原本服务于视障人士的读屏软件(如 VoiceOver、TalkBack),天然过滤了纯装饰性样式,保留了"语义角色(Role)+ 可读名称(Name)+ 状态(State)",恰好也是最契合 LLM 理解页面结构的语义树。A11y Compression(无障碍树压缩) :系统导出的原始无障碍树(Raw A11y Tree)依然充满噪音 (例如mobile-use中每个节点都带有十几个"checkable": "false", "password": "false"的默认布尔属性,以及大量不可交互的FrameLayout容器)。A11y Compression 指在喂给 LLM 之前,用确定性代码完成 4 步脱水裁剪 :- 交互性剪枝(Interactive Pruning):砍掉既无文本语义、又不可交互(不可点击/输入/滚动)的纯容器节点。
- 空层级折叠(Structural Compaction) :将
div -> div -> div -> button拍平折叠为单层button。 - 默认属性剥离(Attribute Stripping) :剔除所有值为
false或空的默认属性,仅保留非默认状态(如[disabled]、[checked])。 - 符号短引用绑定(Symbolic Ref Assignment) :在本地内存维护哈希表
{"@e1": 原节点对象/坐标},向 LLM 输出的文本仅保留@e1。
text
# ❌ 未压缩的 Raw A11y JSON(约 150 Tokens / 节点,整页 5,000+ Tokens):
[
{"class": "android.widget.LinearLayout", "clickable": "false", "bounds": "[0,0][1080,2400]", ...},
{"resource-id": "com.app:id/email", "class": "android.widget.EditText", "text": "Email", "clickable": "true", "focusable": "true", "checked": "false", "bounds": "[100,400][980,520]"},
{"resource-id": "com.app:id/submit", "class": "android.widget.Button", "text": "Continue", "clickable": "true", "checked": "false", "bounds": "[100,600][980,720]"}
]
# ✅ A11y Compression 后的紧凑 DSL(约 8 Tokens / 节点,整页 ~200 Tokens,压缩率 >95%):
@e1 [EditText] "Email"
@e2 [Button] "Continue"
2. 四大框架的底层感知机制深挖
(1)browser-use:基于 Web DOM Tree + 自研 JS 注入遍历(buildDomTree.js)
browser-use 主路径并非直接拉取浏览器原生 A11y 树,而是向页面注入数百行 JS 脚本,直接遍历 Live DOM Tree(含 Shadow DOM 与 iframe):
- 启发式筛选规则 :检查 HTML 标签白名单(
button,a,input等) → 检查onclick/role/tabindex属性 → 调用window.getComputedStyle检查cursor === 'pointer'→ 调用getBoundingClientRect()与document.elementFromPoint(x, y)做命中测试(Hit-Test),剔除被弹窗遮挡的元素。 - 视觉打标(Set-of-Marks, SoM) :在网页 DOM 上动态绘制带数字序号
[1],[2]的彩色高亮框并截图。 - 优劣势 :能兼容大量前端代码不规范的"野鸡网页"(例如全篇用
<div class="btn">绑点击事件、不写 ARIA 属性、仅靠 CSScursor: pointer提示可点击的页面);但全量 DOM 遍历较慢,图文捆绑导致单步 Token 重(3,000~8,000 Tokens)。
(2)Playwright MCP(@playwright/mcp):基于 ariaSnapshot(YAML 无障碍树)+ 动作内嵌快照返回
微软官方的 Playwright MCP 是"A11y Snapshot + ref 短引用"范式的开创者,底层基于 Playwright 原生的 locator.ariaSnapshot():
-
YAML 结构化快照 +
[ref=eN]:将浏览器 A11y 树导出为缩进清晰的 YAML 文本,并为每个节点注入唯一引用(如[ref=e5],iframe 内元素则带前缀如[ref=f1e12]):yaml- heading "todos" [level=1] [ref=e3] - textbox "What needs to be done?" [ref=e5] - list [ref=e8]: - listitem [ref=e9]: - checkbox "Toggle Todo" [ref=e10] - text: Buy groceries -
动作自动附带最新快照(Action-Embedded Snapshot) :每个交互工具(
browser_click、browser_type、browser_navigate)在执行完毕并等待 DOM 稳定后,会自动在ToolMessage返回值中直接附带执行后的最新 YAML Snapshot ,省去了一半的显式查询轮次(并配以browser_fill_form支持一次填完多个表单项后再返回快照)。 -
大页面按需裁剪手段 :因为 MCP Server 无法篡改宿主客户端的历史消息,面对超大页面时提供 4 种降噪机制:
browser_find({ text: "..." }):先搜索关键字,仅返回匹配节点所在路径的子树与ref。browser_snapshot({ target: "e8", depth: 2 }):仅展开指定容器ref下固定深度的局部子树。--snapshot-mode=none:关闭动作自动返回快照,改为按需拉取。browser_snapshot({ filename: "page.yaml" }):将大快照直接落盘为本地文件,供 Coding Agent 用grep检索,零上下文污染。
(3)agent-browser:基于 CDP 原生 A11y Tree + 极致 A11y Compression
使用 Rust 通过 CDP 直接调用浏览器 C++ 内核原生的 Accessibility.getFullAXTree 接口(在 Playwright MCP 思路上进一步向极致轻量化演进):
- 核心机制 :无需注入庞大 JS 遍历 DOM,C++ 内核直接导出语义树;支持
snapshot -i(仅保留可交互节点,比 Playwright MCP 全节点树更精简) 、-c(折叠空容器) 与--delta(增量 Diff,仅输出上一步之后变化的节点) ,脱水为@eN短引用后仅需 200~400 Tokens。 - 视觉按需对齐 :默认纯文本交互;当需要视觉辅助时,支持
screenshot --annotate(在截图上自动绘制与@eN一一对应的[N]标签)与screenshot --if-changed(画面无变化则跳过)。
(4)mobile-use:基于 移动端原生 A11y Tree(未压缩 JSON)+ 必传截图
Android(uiautomator2.dump_hierarchy)与 iOS(idb ui describe-all)底层唯一暴露的结构化接口就是系统原生 A11y 树:
- 双模态互补 :UI 树提供精确的
resource-id、text与像素bounds,但看不见颜色、图标或被遮挡状态;截图提供视觉语义,但让 LLM 直接从像素盲猜坐标不准。因此每步将二者同时喂给模型。 - 局限 :
mobile-use仅将原生 A11y XML 机械转换为扁平 JSON 数组,未做 A11y Compression 属性脱水与容器剪枝,导致单步观测高达 5,000~15,000 Tokens。
四、 上下文与记忆层(Context & KV Cache):两大架构流派的分野
正因为第三章中各框架产出的 单步观测体积(Observation Size)相差了 30 倍(10,000 Tokens vs. 200 Tokens),直接决定了它们在上层上下文管理与 KV Cache 策略上分化为两大流派:
单步观测大小(Observation Size)×任务步数(Steps)≷上下文窗口与缓存成本阈值
流派一:状态机层的"历史观测剪枝"(Observation Pruning / Single-Frame Observation)
代表项目 :browser-use、minitap/mobile-use(单步观测重达 3,000 ~ 15,000 Tokens 的多模态框架)
1. 核心思想:状态投影式提示(State-Projected Context Engineering)
既然单步观测太重,绝对不能在消息列表里无脑 append 原始观测。必须将 Agent 状态拆分为 持久态(Persistent State) 与 瞬态(Transient State):
- 持久态(只增不减,留存历史) :Agent 每一步提炼出的纯文本思考结论(
agents_thoughts/memory)以及显式键值笔记(scratchpad)。每步仅 ~100 Tokens。 - 瞬态(单帧滑动窗口,用完即焚) :当前时刻的 UI 树与截图(
latest_ui_hierarchy+latest_screenshot)。只挂载于 Prompt 的最末尾,当前轮次结束后立即从上下文中驱逐(Evict)。
2. 上下文拼装模型(以 mobile-use 为例)
每一轮调用 LLM 时,不直接复用上一轮的 messages,而是通过状态投影函数重新构造消息序列: Promptt=Append-Only 纯文本前缀(可命中 Prefix KV Cache) System,Thought1,Thought2,...,Thoughtt−1+Ephemeral Tail(每轮替换的单帧观测) UI Hierarchyt,Screenshott
⚠️ 工程避坑指南(Prefix Cache 铁律) : 在使用该模式时,严禁将每轮变化的动态状态(如当前子目标进度、上一步工具报错反馈)渲染进顶部的
SystemMessage(mobile-use源码cortex.py中即犯了此错误)。一旦第 0 条SystemMessage发生字节级变化,后面排队的全部历史Thought缓存将整体失效!动态反馈必须与当前帧观测一起放在消息队列的最尾部。
流派二:工具层的"源头极致压缩 + 原生追加式 ReAct"(Source-Level Compression + Native ReAct)
代表项目 :vercel-labs/agent-browser、Playwright MCP
1. 核心思想:把降噪做在工具侧,而非状态机侧
当工具侧通过 A11y Compression (如 agent-browser snapshot -i --delta)把单步观测从 10,000 Tokens 压缩到 200 Tokens 时,量变引发了质变:
- 无需删除历史消息:走完 25 步累积的观测也只有 ~5,000 Tokens。
- 100% 完美契合原生 Prefix KV Cache :直接采用最标准的 Append-Only ReAct 对话流,历史所有的
User -> Assistant -> Tool(snapshot)原封不动留在上下文中,零缓存破坏。 - 架构解耦 :无需定制专门的 LangGraph /
MessageManager剪枝状态机,直接作为 CLI 或 MCP Server 挂载到 Claude Code、Cursor 等任意通用 Agent 上即可工作。
四大框架全景对比总表
| 维度 | mobile-use (Mobile) |
browser-use (Web) |
Playwright MCP (Web) |
agent-browser (Web) |
|---|---|---|---|---|
| 底层通信协议 | ADB + UIAutomator2 / IDB + WDA | Playwright (CDP) | Playwright (CDP / Firefox / WebKit) | Rust 直连 CDP(无 Node 依赖) |
| 底层感知源 | 系统原生 A11y XML | Web Live DOM Tree(注入 JS 遍历) | Playwright ariaSnapshot (A11y) |
CDP 原生 A11y Tree (getFullAXTree) |
| 感知加工与压缩 | 未压缩扁平 JSON + 原始截图 | 启发式筛 DOM (cursor:pointer) + 截图画框 (SoM) |
导出 YAML 树 + [ref=eN](动作自动附带返回) |
极致 A11y Compression (-i 仅交互 + --delta 差分) |
| 单步观测体积 | 极重(5,000 ~ 15,000 Tokens) | 重(3,000 ~ 8,000 Tokens) | 中轻(500 ~ 2,500 Tokens,支持局部搜索) | 极轻(200 ~ 400 Tokens) |
| 上下文管理流派 | 流派一:单帧观测剪枝 (留存 agents_thoughts) |
流派一:单帧观测剪枝 (留存 memory) |
流派二:原生追加 ReAct (大页用 find/落盘降噪) |
流派二:原生追加 ReAct(天然 100% 命中缓存) |
| 元素定位方式 | 三级 Fallback:bounds -> id -> text |
视觉/DOM 序号 [1], [2] |
引用 [ref=e5] 或 Playwright Locator |
短引用 @e1, @e2 或语义 find |
五、 决策与执行层(Action & Execution):如何快、准、稳地调用工具?
除了"怎么看(感知)"和"怎么记(上下文)",在"怎么动(执行 Tool Call)"上,这些框架沉淀出了三个核心工程模式:
1. 批量动作下发与串行熔断(Action Chaining with Sequential Abort)
为了避免"点一下输入框 → 截屏调 LLM → 打字 → 截屏调 LLM → 点提交"带来的高昂网络往返延迟(RTT),主流框架均支持 单次思考、连续下发多步动作(Multi-Action per Turn),但必须遵循两条安全准则:
- 不可预测动作隔离原则(Isolate Unpredictable Actions) : 凡是会导致页面跳转或大面积刷新的动作(如
launch_app、back、点击导航链接),必须作为该轮次的唯一动作,执行后强制重新感知。只有同页面内的确定性操作(如连续填写多个表单项)才允许打包下发。 - 串行执行与遇错熔断(Sequential Execution & Fail-Fast Abort) :
- LLM 原生的
parallel_tool_calls默认会被框架(如 LangGraph 标准ToolNode)并发同时执行,这在 GUI 操作中会导致灾难。 - 正确做法 :
mobile-use重写了ExecutorToolNode,将多个 Tool Calls 劫持为for循环串行执行 ;一旦第 k 个动作返回失败,立即熔断中止第 k+1...n 个动作(标记Aborted: a previous tool call failed!),带着错误现场回到感知节点。agent-browser在点击被弹窗(如 Cookie 横幅)遮挡时,会立即 Fail Early 并返回遮挡元素信息(如covered by <div#consent-banner>),或在 Shell 中通过&&链式执行实现天然的遇错即停。
- LLM 原生的
2. 决策与执行的解耦 vs. 合一(Brain-Hand Decoupling vs. Unified Output)
在将"大脑决策"转化为"底层接口调用"时,存在两种实现路径:
-
路径 A:双模型解耦(
mobile-use的Cortex+Executor)Cortex(大脑,昂贵多模态大模型如 Gemini-3-Pro / GPT-5) :看图+看树,不绑定复杂 Tool Schema ,仅通过with_structured_output(CortexOutput)输出思维链、已完成的子目标 ID(驱动 Orchestrator 状态流转)以及字符串化的 JSON 决策。Executor(双手,廉价纯文本小模型如 GPT-5-nano / Haiku) :不看图、不看树,仅开启bind_tools()将Cortex的 JSON 字符串翻译成严格的 LangChainToolCall。- 权衡 :规避了部分多模态模型同时处理"复杂视觉推理 + 14 个嵌套 Tool Schema"时注意力退化或格式报错的问题;但代价是每一步多了一次小模型的 LLM RTT 延迟(0.5~1.5s)。
-
路径 B:单模型一步到位(
browser-use/agent-browser/Playwright MCP)browser-use:单次 LLM 调用直接输出强类型 Pydantic 对象AgentOutput(current_state=AgentBrain, action=list[ActionModel]),拿到结果后直接用普通 Pythonfor循环分发执行,零额外 LLM 转换开销。agent-browser/Playwright MCP:因为将元素定位简化为了@e1/e1短引用,工具参数极其简单扁平,主模型在单次推理中即可直接输出高质量 Tool Call 或 CLI 命令。
3. 多级定位器降级策略(Multi-Locator Fallback,mobile-use 特色)
在未引入 @eN 服务端引用映射的框架中,mobile-use 要求 LLM 在指定目标元素 Target 时同时填满三个维度的属性,由底层执行器按优先级自动 Fallback:
bounds(像素中心点坐标) :第一优先级,速度最快,无需重新 dump 界面树,直接下发input tap x y。resource_id+index(原生资源 ID):当坐标越界或失效时,实时重抓 UI 树按 ID 定位并计算中心点。text+index(文本/无障碍标签) :最后兜底,按可见文字或content-desc/AXLabel匹配。
六、 架构选型与融合最佳实践(Takeaways)
如果你正在从零设计或重构一个 GUI / Mobile / Browser Agent,结合上述四大框架的优劣,最优的架构演进路径是:
- 感知层:优先在工具侧做
agent-browser式的A11y Compression+@ref绑定 : 不要像mobile-use那样把原始 XML/HTML 或带十几个"false"属性的冗长 JSON 直接喂给大模型。在 Python/Rust 工具侧先过滤出可见/可交互节点,在内存中维护@e1 -> 节点/坐标映射表,只给大模型输出@e1 [Button] "登录"(必要时辅以screenshot --annotate视觉打标对齐),单步 Token 立省 85% 以上。 - 上下文层:严格遵循
Static Prefix + Append-Only Thoughts + Ephemeral Tail:- 若单步观测已压至百级 Token(纯文本
@ref快照),直接走原生 ReAct 即可。 - 若处于强视觉场景(每步必传大图),必须采用 单帧滑动窗口剪枝(Single-Frame Observation) :历史只留存纯文本
Thought/Memory序列,当前帧截图永远挂在messages最末尾(用完即删),且严禁在SystemMessage中混入动态变量,确保历史前缀 100% 命中 KV Cache。
- 若单步观测已压至百级 Token(纯文本
- 执行层:单模型输出 + 确定性代码串行熔断 : 在 Claude Sonnet 4.6 / GPT-5 / Gemini 3 等现代模型上,无需像
mobile-use那样单独维护一个ExecutorLLM 节点做格式转换。通过@eN简化工具参数后,由单模型一次性输出动作序列,交由底层 Python/Rust 循环串行执行、遇错立即熔断返回,路径最短、延迟最低、成本最优。
七、总结
(感知与上下文的权衡):GUI Agent 架构演进的根本驱动力,是对抗"高维瞬时观测(截图与原始 UI/DOM 树动辄上万 Token 且极易过期)"与"长程历史记忆"之间的物理矛盾。围绕这一矛盾,业界分化出两条殊途同归的路径:
- 当感知层较重时(如
mobile-use与browser-use的双模态图文输入),必须在状态机侧 实施"历史观测剪枝(Single-Frame Observation)",即用完即焚地驱逐旧画面与旧树,仅将高信息密度的纯文本思考结论(Thoughts/Memory)以只增不减的前缀形式留存,从而兼顾注意力纯净度与 KV Cache 命中率; - 而当工具侧 在源头完成了极致的"无障碍树脱水压缩(A11y Compression)"与符号化短引用绑定(如
agent-browser与Playwright MCP将整页压至 200 Tokens 的@eN列表及增量 Diff)时,观测体积发生了数量级坍缩,Agent 便无需再维护复杂的剪枝状态机,直接复用最朴素的原生追加式 ReAct 循环即可跑通长程任务。
(决策与执行的工程实践):凡是会引起页面刷新或路由跳转的不可预测动作(如启动应用、点击导航链接),必须作为当轮唯一动作单步隔离,强制等待新画面渲染并重新感知后再做下一步决策;而同一页面内的确定性连续操作(如多字段表单填写),则允许大模型在单次推理中批量下发动作序列,交由底层执行器接管并以串行循环(而非并发)的方式依次落地------一旦中途任一动作触发遮挡报错或定位失败,立即熔断中止剩余队列并带着真实错误上报。