揭秘CAMEL项目:AI Agent如何像人一样“看懂”并操作网页?

AI前沿2小时前发布 yizz
498 0 0

在当今的**AI Agent**研究中,让智能体(Agent)像人一样理解和操作复杂的网页,一直是个巨大的挑战。网页的代码结构(DOM)和用户实际看到的视觉布局往往存在巨大差异。**CAMEL**项目的**hybrid_browser_toolkit**工具包,正是为了解决这一核心难题而生。本文将深度解析其技术实现,从架构设计到核心功能,揭示其如何为**AI**打造一个更干净、更准确的网页视图。

## 一、核心思想是什么?——“结构+视觉”的**双重过滤**机制

在我们深入技术细节之前,必须先理解这个工具包的灵魂所在:一种独特的“**双重过滤**”机制。

### 为什么传统的网页解析方式对**AI**不友好?

传统的**AI Agent**在浏览网页时,大多直接读取网页的源代码(HTML DOM)。但这会遇到一个根本性难题:“代码看起来是一回事,用户实际看到和能交互的又是另一回事”。比如,一个按钮在代码里可能嵌套了多层`

`和``,还有很多元素在视觉上被其他弹窗或图片遮挡了,但它们在代码里依然存在。这给**AI**的判断带来了大量“噪音”,导致它经常点击错误的或不可见的目标。

### **CAMEL**是如何解决这个问题的?

**CAMEL**的`hybrid_browser_toolkit`通过一种更接近人类直觉的方式来解决这个问题。它不仅仅读取代码结构,还会分析元素在屏幕上的**实际布局和大小**(视觉)。具体来说,它设置了两道关卡:

1. **DOM层级分析(结构)**:首先,它会分析代码的层级关系,剔除那些在结构上明显是冗余的、作为包装器的元素。
2. **几何位置分析(视觉)**:接着,它会分析元素在屏幕上的**真实坐标和尺寸**。如果一个元素在视觉上被另一个元素完全覆盖,那么它就会被过滤掉,因为它对用户来说是不可交互的。

这个“**结构 + 视觉**”的**双重过滤**思想,是整个工具包的灵魂。它能高效地剔除大量在视觉上被覆盖或在结构上冗余的元素,只留下真正对用户有意义的交互点,从而让**AI Agent**实现前所未有的**精准网页操作**。

## 二、系统架构是怎样设计的?——兼顾**稳定性**与**扩展性**的混合式设计

为了实现上述目标,该工具包采用了一种**Python**与**TypeScript (Node.js)**相结合的**混合架构**,旨在将上层逻辑(Python)与底层浏览器控制(TypeScript)解耦,以提升系统的**稳定性**、**隔离性**和**扩展性**。

### 这种混合架构有哪些特色?

* **极致的稳定性(进程隔离)**:这可能是最重要的一个设计。每当你创建一个`HybridBrowserToolkit`实例时,它都会启动一个**完全独立**的**Node.js子进程**和**浏览器实例**。这意味着不同的浏览器会话在操作系统层面是**完全隔离**的。就算其中一个会话因为某个奇葩网页而崩溃了,也**绝对不会**影响到你的Python主进程或其他浏览器会话。这对于需要长时间稳定运行的**AI Agent**来说至关重要。

* **灵活的扩展性(命令模式)**:Python端和Node.js端通过**命令模式**进行交互。简单来说,Python端会将一个操作(比如`click`)封装成一个标准的**JSON命令**,然后通过**WebSocket**发送给Node.js端去执行。这种清晰的接口边界让添加新功能变得异常简单:你只需要在TypeScript端实现新命令的逻辑,然后在两端“注册”一下这个新命令即可,完全不需要改动核心的通信代码。

### 如何启动和连接浏览器?——两种灵活的**启动模式**

工具包通过`cdpUrl`这个参数,非常巧妙地支持了两种浏览器启动模式,提供了极大的灵活性:

* **自动模式(默认)**:如果你不提供`cdpUrl`参数,工具包会自动调用**Playwright**的`chromium.launch()`方法,为你启动一个全新的、干净的Chromium浏览器实例。这是最简单、最常用的模式。
* **手动模式(用于高级调试)**:如果你提供了一个`cdpUrl`(例如`http://localhost:9222`),工具包则会通过`chromium.connectOverCDP()`去连接一个**已经启动**并开启了远程调试端口的浏览器实例。这个模式非常适合开发者进行高级调试,或者将工具包集成到现有的浏览器会话中。

## 三、核心功能揭秘:如何为**AI**生成“干净”的**页面快照**?

页面快照功能是整个工具包的重中之重。它通过一个精密的三步流水线,将一个复杂的网页转换成**AI**易于处理的、简洁的结构化数据。

[图片]

### 第一步:生成 – 为什么不直接用DOM,而是用**无障碍树**?

* **技术实现**:这一步调用了**Playwright**一个内部的、未公开的函数`page._snapshotForAI()`。
* **核心原理**:这个函数的聪明之处在于,它遍历的不是原始、臃肿的HTML DOM,而是浏览器的**无障碍树 (Accessibility Tree)**。无障碍树是浏览器为了屏幕阅读器等辅助技术而生成的一种简化版页面结构,它本身就已经过滤掉了大量纯样式的、没有语义的装饰性元素(比如空的`

`)。
* **带来什么优势**:从源头上就实现了第一次“降噪”,获取到的信息天然就包含了元素的**角色(role)**和**名称(name)**,这对于**AI**理解元素功能至关重要。
* **输出格式**:Playwright会为树中的每个节点分配一个唯一的**`[ref=N]` ID**,并以保留层级关系的缩进文本格式输出。

### 第二步:解析 – 如何将文本重建为可操作的**图结构**?

* **执行者**:这一步由`snapshot-parser.ts`中的`parseSnapshotHierarchy`函数完成。
* **核心原理**:该函数会读取上一步生成的缩进文本。它通过分析每一行的**缩进量**,并巧妙地借助一个**栈(Stack)**来高效地追踪当前节点的父节点,从而将扁平的文本行重新构建成一个完整的父子层级关系图。
* **输出结构**:最终生成一个`Map`结构的**层级关系图 (Graph)**。这个图结构是后续所有过滤操作的基础,让程序可以非常方便地查询任何元素的父节点、子节点及其所有属性。

### 第三步:过滤 – **双重过滤**机制如何精确“去芜存菁”?

这是将网页“净化”最核心的步骤,它通过两道关卡,确保最终呈现给**AI**的快照既简洁又精确。

#### **A. 基于DOM层级的过滤(结构过滤)**

* **执行者**:由`snapshot-parser.ts`中的`filterClickableByHierarchy`函数负责。
* **核心规则**:它利用上一步生成的层级图,应用一系列启发式规则来处理嵌套的可点击元素。
* **父元素优先**:在`link > img`(图片链接)或`button > generic`(按钮里包着一个通用元素)这类常见嵌套中,**保留父元素**(`link`或`button`),过滤掉子元素。因为用户的交互意图通常是针对整个链接或按钮。
* **子元素优先**:在`generic > button`这种特殊情况下,反而会**过滤父元素**`generic`,保留子元素`button`,以适应某些前端UI框架的特殊设计。
* **单一包装过滤**:如果一个`generic`元素仅仅是作为单个`button`的包装器(wrapper),那么这个`generic`元素自身也会被过滤掉。

#### **B. 基于几何位置的过滤(视觉过滤)**

* **执行者**:由`parent-child-filter.ts`中的`filterParentChildElements`函数负责。
* **核心原理**:在层级过滤之后,进行第二轮基于**视觉位置**的过滤,专门解决DOM结构与视觉表现不一致的问题。
* **核心逻辑**:
1. **定义“传播性”元素**:首先,定义一组通常会“捕获”点击事件的“传播性”父元素,比如``、``。
在视觉上,``元素(文字“点我”)完全被`

© 版权声明
chatgpt4.0

相关文章