首页/知识库/AI Agent 桌面集成:从浏览器到操作系统的全栈架构

AI Agent 桌面集成:从浏览器到操作系统的全栈架构

✍️ AI Master📅 创建 2026-05-08📖 22 min 阅读
💡

文章摘要

理解 AI Agent 如何从浏览器扩展到桌面操作系统,掌握桌面 Agent 的核心架构模式、安全模型与工程实践

1什么是 AI Agent 桌面集成

AI Agent 桌面集成是指将大语言模型驱动的智能体深度嵌入到桌面操作系统和浏览器环境中,使其能够感知、理解和操控用户界面上的应用程序、网页和系统资源。这不仅仅是「在浏览器中打开一个聊天窗口」,而是让 Agent 具备视觉理解能力、DOM 操作能力和系统级 API 调用能力,从而像一个虚拟用户一样执行跨应用的复杂工作流。

桌面集成的核心价值在于消除应用之间的切换成本。传统工作流要求用户手动打开浏览器搜索信息、切换到编辑器编写文档、再切换到邮件客户端发送结果——每一步都需要人工介入和上下文切换。桌面 Agent 的目标是让这个流程端到端自动化:用户只需给出一个自然语言指令,Agent 就能自主完成信息检索、内容生成和结果分发的全链路。

2026 年,这个领域出现了三个标志性的产品方向:Perplexity Personal Computer 将 AI 搜索直接集成到Mac 桌面环境,实现全局搜索+多应用联动;OpenAI Codex Chrome 扩展让 Agent 能够在浏览器端自主操作网页、填写表单、提取数据;Cursor 和 GitHub Copilot Workspace 则将 Agent 能力嵌入开发环境,实现代码生成→测试→提交的闭环。

桌面集成的本质:不是让 AI 变得「更聪明」,而是让 AI 变得「更触手可及」——从独立的聊天窗口,变成操作系统原生的一等公民。

从工程角度看,桌面集成涉及四个层次的架构设计:感知层(屏幕截图、DOM 解析、Accessibility API)、理解层(多模态模型、意图识别、上下文管理)、执行层(模拟输入、API 调用、文件操作)和安全层(权限控制、操作确认、审计日志)。理解这四个层次的关系,是设计任何桌面 Agent 方案的基础前提。

学习建议:在深入技术细节之前,先用 10 分钟体验三种主流桌面 Agent(Perplexity PC、Codex Chrome、Cursor),记录它们各自能做什么、不能做什么——这种亲身体验是理解架构差异的最快方式。

常见误区:不要把「桌面集成」等同于「浏览器插件」。浏览器插件只是桌面集成的一种形态(针对网页内容),而真正的桌面集成需要操作系统级别的权限和跨应用的协调能力。如果你的方案只能在单一浏览器内工作,那它只是一个浏览器插件,不是桌面 Agent。

2感知层架构:Agent 如何「看到」桌面

感知层是桌面 Agent 的第一道关口——如果 Agent 无法正确理解当前屏幕上显示的内容,后续的所有推理和执行都无从谈起。桌面感知的核心挑战在于信息密度极高且格式高度异构:一个典型的桌面屏幕可能同时包含浏览器窗口(DOM 结构)、IDE(代码编辑器)、终端(文本流)、邮件客户端(富文本)和系统通知(原生 UI 控件)。

主流感知方案有三种:屏幕截图+视觉模型、DOM/Accessibility API 提取和混合模式。屏幕截图方案将当前屏幕截取为图像,送入多模态大语言模型(如 GPT-4oClaude 的视觉能力)进行解析。这种方案的优势是通用性极强——任何有视觉界面的应用都能被感知,不需要应用提供专用 API。缺点是精度有限:OCR 对小字号文本的识别率通常在 85-92% 之间,且无法区分交互元素的类型(比如一个蓝色矩形是按钮还是装饰框)。

DOM 和 Accessibility API 方案则从应用的内部结构获取信息。浏览器环境中,Agent 可以通过 Chrome DevTools Protocol (CDP) 获取完整的 DOM 树,包括元素的标签、属性、样式和层级关系。在 macOS 上,Accessibility API 可以获取窗口控件的结构化描述——按钮的标题、输入框的值、列表的元素等。这种方案的精度远高于截图方案,因为获取的是机器可读的结构化数据而非像素信息。但它的局限性也很明显:只能感知暴露了 API 的应用,对于没有实现 Accessibility 协议的闭源应用无能为力。

混合模式结合了两种方案的优势:优先使用 DOM/Accessibility API 获取结构化信息,当 API 不可用或信息不完整时回退到截图+视觉模型进行补充。这是工程实践中最可靠的方案,也是 OpenAI Codex 和 Perplexity PC 采用的核心策略。

感知的时序问题同样重要。桌面是动态变化的——页面在加载、弹窗在出现、内容在滚动。Agent 需要在正确的时间点获取感知快照,否则可能基于过时的屏幕状态做出错误决策。工程上通常采用轮询+事件驱动的组合:每隔 200-500ms 主动获取一次快照,同时监听系统级事件(如窗口焦点变化、DOM 变更通知)来触发即时感知更新。

python
# 混合感知方案:DOM API + 截图视觉模型回退
import base64
import time
from playwright.sync_api import sync_playwright
from openai import OpenAI

class DesktopAgentPerception:
    def __init__(self, api_key: str):
        self.client = OpenAI(api_key=api_key)
        self.last_perception = None
        self.perception_timestamp = 0

    def perceive_via_dom(self, page) -> dict:
        """通过 DOM 获取结构化信息(高精度,低覆盖)"""
        elements = page.evaluate("""
            () => {
                const interactive = document.querySelectorAll(
                    'button, input, a, select, textarea, [role="button"], [role="link"]'
                );
                return Array.from(interactive).map(el => ({
                    tag: el.tagName.toLowerCase(),
                    type: el.type || el.getAttribute('role'),
                    text: el.textContent?.trim().substring(0, 100),
                    id: el.id,
                    name: el.name,
                    ariaLabel: el.getAttribute('aria-label'),
                    visible: el.offsetParent !== null,
                    rect: el.getBoundingClientRect()
                }));
            }
        """)
        return {"source": "dom", "elements": elements}

    def perceive_via_screenshot(self, page) -> dict:
        """通过截图+视觉模型获取信息(低精度,全覆盖)"""
        screenshot = page.screenshot(type="jpeg", quality=80)
        b64 = base64.b64encode(screenshot).decode()

        response = self.client.chat.completions.create(
            model="gpt-4o",
            messages=[{
                "role": "user",
                "content": [
                    {"type": "text", "text": "描述这个页面上的所有交互元素,包括它们的位置、类型和功能。以JSON格式返回。"},
                    {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}}
                ]
            }]
        )
        return {"source": "vision", "analysis": response.choices[0].message.content}

    def perceive(self, page, prefer_dom: bool = True) -> dict:
        """混合感知:优先 DOM,失败则回退到视觉模型"""
        try:
            if prefer_dom:
                result = self.perceive_via_dom(page)
                if result["elements"] and len(result["elements"]) > 0:
                    self.last_perception = result
                    self.perception_timestamp = time.time()
                    return result
        except Exception as e:
            print(f"DOM 感知失败: {e},回退到视觉模型")

        # 回退方案
        result = self.perceive_via_screenshot(page)
        self.last_perception = result
        self.perception_timestamp = time.time()
        return result
typescript
// macOS Accessibility API 感知方案(Swift)
import ApplicationServices

struct AccessibilityElement {
    let role: String
    let title: String
    let value: String
    let position: CGPoint
    let size: CGSize
    let children: [AccessibilityElement]
}

func perceiveApplication(pid: pid_t) -> [AccessibilityElement] {
    guard let appRef = AXUIElementCreateApplication(pid) else {
        return []
    }

    var windows: CFTypeRef?
    let result = AXUIElementCopyAttributeValue(
        appRef,
        kAXWindowsAttribute as CFString,
        &windows
    )

    guard result == .success, let windowArray = windows as? [AXUIElement] else {
        return []
    }

    var elements: [AccessibilityElement] = []
    for window in windowArray {
        if let root = traverseAXElement(window) {
            elements.append(root)
        }
    }
    return elements
}

func traverseAXElement(_ element: AXUIElement) -> AccessibilityElement? {
    var role: CFTypeRef?
    AXUIElementCopyAttributeValue(element, kAXRoleAttribute as CFString, &role)

    var title: CFTypeRef?
    AXUIElementCopyAttributeValue(element, kAXTitleAttribute as CFString, &title)

    var position: CGPoint = .zero
    var positionRef: CFTypeRef?
    if AXUIElementCopyAttributeValue(element, kAXPositionAttribute as CFString, &positionRef) == .success {
        AXValueGetValue(positionRef as! AXValue, .cgPoint, &position)
    }

    // 递归获取子元素
    var children: [AccessibilityElement] = []
    var childrenRef: CFTypeRef?
    if AXUIElementCopyAttributeValue(element, kAXChildrenAttribute as CFString, &childrenRef) == .success {
        if let childArray = childrenRef as? [AXUIElement] {
            for child in childArray {
                if let childEl = traverseAXElement(child) {
                    children.append(childEl)
                }
            }
        }
    }

    return AccessibilityElement(
        role: role as? String ?? "",
        title: title as? String ?? "",
        value: "",
        position: position,
        size: .zero,
        children: children
    )
}

工程实践:在开发桌面 Agent 时,建议先实现 DOM/Accessibility API 感知作为主方案,因为其延迟低(<50ms)、精度高(>98%)且不依赖外部 API 调用。截图方案应仅作为回退手段,因为视觉模型调用通常需要 1-3 秒的响应时间。

隐私风险:屏幕截图方案会将用户的整个桌面内容发送到云端视觉模型,这可能包含敏感信息(密码、私人对话、财务数据)。如果处理的是敏感场景,必须使用本地视觉模型(如 MLX 框架在 Mac 上运行的本地模型)或对截图进行区域裁剪和敏感信息脱敏。

3理解层架构:从感知数据到行动决策

感知层提供了原始数据,但这些数据本身没有意义。理解层的任务是将感知快照转化为结构化的上下文表示,供 Agent 的推理引擎做出行动决策。这个转化过程涉及三个关键步骤:意图解析、上下文建模和行动空间定义。

意图解析是理解用户自然语言指令的核心环节。用户可能说「帮我把今天收到的所有邮件里的发票信息整理成一个表格」,Agent 需要将其分解为可执行的子任务序列:(1) 打开邮件客户端,(2) 筛选今天的邮件,(3) 识别包含发票的邮件,(4) 从每封邮件中提取发票信息(金额、日期、供应商),(5) 打开电子表格应用,(6) 将数据填入表格。这个分解过程本质上是一个任务规划(Task Planning)问题,当前主要依赖 LLM 的 Chain-of-Thought 推理能力来完成。

上下文建模解决了 Agent 的记忆连续性问题。桌面任务通常不是单轮的——用户先让 Agent 搜索信息,然后基于搜索结果让它写报告,再基于报告内容发邮件。Agent 必须维护一个持久化的上下文状态,包括历史感知快照、已执行的行动、行动的结果和用户的后续指令。这类似于 LLM 对话中的上下文窗口管理,但桌面 Agent 的上下文远比对话复杂:它不仅包含文本历史,还包含屏幕状态的序列、文件操作的记录和应用间的数据流转。

行动空间定义决定了 Agent 能做什么和不能做什么。不同的桌面集成方案定义了不同的行动空间:浏览器 Agent 的行动空间包括点击元素、输入文本、导航 URL、执行 JavaScript;桌面 Agent 的行动空间还包括打开应用、操作文件、调用系统 API;开发环境 Agent 的行动空间进一步扩展到运行终端命令、读写文件、执行测试。行动空间的广度与安全风险成正比——行动空间越广,Agent 造成意外损害的可能性越大。因此,行动空间的设计必须在功能性和安全性之间找到平衡点。

最佳实践:使用层级化的行动空间设计——将行动分为安全级(只读操作,如读取文件内容、查看网页)和危险级(写入操作,如删除文件、发送邮件)。安全级行动可以由 Agent 自主执行,危险级行动必须请求用户确认。这种分层策略被 Claude Desktop 和 OpenAI Codex 广泛采用。

关键风险:意图解析的歧义性是桌面 Agent 最常见的失败原因。用户说「把这些文件删了」,Agent 如何判断「这些文件」指的是哪些?如果理解错误,可能导致不可逆的数据丢失。解决方案是在执行危险操作前,Agent 应该复述自己的理解并展示即将操作的对象列表,让用户有机会纠正误解。

4执行层架构:Agent 如何「操作」桌面

执行层是 Agent 将推理结果转化为实际操作的环节。桌面执行的核心挑战在于跨应用的异构接口——每个应用都有自己独特的交互方式,Agent 需要一套统一的执行抽象来覆盖所有这些差异。

浏览器环境中的执行相对标准化。Playwright 和 Puppeteer 提供了成熟的浏览器自动化 API,Agent 可以通过这些 API 执行点击、输入、导航、截图等操作。更高级的方案使用 Chrome Extension API,让 Agent 能够以用户身份直接与页面交互——这比远程自动化更自然,因为它不会触发网站的反自动化检测机制。OpenAI Codex Chrome 扩展采用的就是这种方案:它作为浏览器扩展运行,通过 Content Script 注入到页面中,直接操作 DOM 和监听用户事件。

桌面环境的执行则复杂得多。macOS 提供了 AppleScript 和 Apple Events 机制来允许应用间的自动化通信,但每个应用对这些协议的支持程度不一。更底层的方案是使用辅助功能框架(Accessibility Framework)来模拟用户输入——这种方式可以控制任何应用,包括那些不支持 AppleScript 的闭源应用。Windows 环境有类似的机制:UI Automation 框架和 PowerShell 自动化脚本。

执行的速度和可靠性是工程上的关键考量。浏览器自动化通常很快(单次操作 10-50ms),但桌面级的辅助功能操作可能慢得多(100-500ms),因为它们需要跨进程通信。更复杂的是,桌面应用的状态变化不一定有明确的完成信号——你点击了一个按钮,但页面可能需要 2 秒才能加载完成,Agent 如何知道何时可以执行下一步?工程上通常采用等待条件(Wait Condition)模式:在执行下一步之前,轮询检查某个条件是否满足(如某个元素是否出现、某个文本是否显示),而不是硬编码延迟。

typescript
// 桌面 Agent 执行层:统一的行动抽象 + 条件等待
interface AgentAction {
  type: "click" | "type" | "navigate" | "read" | "wait" | "screenshot";
  selector?: string;
  text?: string;
  url?: string;
  condition?: (ctx: ExecutionContext) => Promise<boolean>;
  timeoutMs?: number;
}

class DesktopAgentExecutor {
  private browser: Browser;
  private maxRetries = 3;

  async execute(action: AgentAction, ctx: ExecutionContext): Promise<ExecutionResult> {
    switch (action.type) {
      case "click":
        return await this.clickWithWait(action, ctx);
      case "type":
        return await this.typeWithWait(action, ctx);
      case "navigate":
        return await this.navigateAndWait(action);
      case "wait":
        return await this.waitForCondition(action, ctx);
      case "screenshot":
        return await this.captureScreenshot();
      default:
        throw new Error(`未知的行动类型: ${action.type}`);
    }
  }

  private async clickWithWait(action: AgentAction, ctx: ExecutionContext): Promise<ExecutionResult> {
    const page = ctx.currentPage;
    const element = await page.waitForSelector(action.selector!, {
      timeout: action.timeoutMs ?? 5000
    });

    // 点击前截图(用于审计和调试)
    const before = await page.screenshot();

    await element.click();

    // 等待条件满足或超时
    if (action.condition) {
      await this.waitForCondition({
        type: "wait",
        condition: action.condition,
        timeoutMs: action.timeoutMs ?? 10000
      }, ctx);
    }

    return { success: true, before, after: await page.screenshot() };
  }

  private async waitForCondition(
    action: AgentAction,
    ctx: ExecutionContext
  ): Promise<ExecutionResult> {
    const start = Date.now();
    const timeout = action.timeoutMs ?? 10000;
    const pollInterval = 200; // 每 200ms 检查一次

    while (Date.now() - start < timeout) {
      try {
        if (action.condition && await action.condition(ctx)) {
          return { success: true, waitTime: Date.now() - start };
        }
      } catch {
        // 条件检查失败,继续等待
      }
      await new Promise(resolve => setTimeout(resolve, pollInterval));
    }

    return { success: false, error: `条件等待超时 (${timeout}ms)` };
  }

  // 行动链:将多个行动组合为一个工作流
  async executeChain(actions: AgentAction[], ctx: ExecutionContext): Promise<ExecutionResult[]> {
    const results: ExecutionResult[] = [];
    for (const action of actions) {
      const result = await this.execute(action, ctx);
      results.push(result);
      if (!result.success) {
        // 行动失败,记录审计日志并停止
        ctx.logAudit({ action, result, timestamp: Date.now() });
        break;
      }
    }
    return results;
  }
}

可靠性技巧:为每个行动实现自动重试机制(建议 2-3 次),因为桌面环境中的临时故障(网络延迟、应用卡顿、DOM 加载慢)是常态而非例外。重试时使用指数退避策略(第一次重试等 500ms,第二次等 1000ms),避免在应用尚未就绪时频繁操作。

执行陷阱:不要在执行层硬编码应用特定的逻辑(如「点击 Gmail 的撰写按钮」)。这种做法会导致 Agent 只能操作特定应用,丧失了桌面 Agent 的通用性。正确的做法是定义应用无关的行动原语(如「点击坐标 (x, y) 处的元素」),让 Agent 的推理层根据感知数据动态决定具体操作。

5安全层架构:权限控制与审计

安全层是桌面 Agent 最不可或缺的组成部分。当 Agent 获得了操控用户桌面的能力,它也就获得了造成严重损害的能力——删除重要文件、发送错误邮件、泄露敏感数据、甚至执行恶意代码。因此,安全层不是「锦上添花」的附加功能,而是桌面 Agent 架构的基石。

权限控制是安全层的第一道防线。桌面 Agent 应该采用最小权限原则(Principle of Least Privilege)——只授予完成当前任务所需的最小权限集合。具体来说,权限可以分为三个等级:只读权限(读取文件内容、查看网页、获取系统信息),受限写入权限(在指定目录创建文件、在指定应用输入文本),和完全权限(任意文件操作、任意应用控制、系统级命令执行)。只读权限应该默认授予,受限写入权限需要用户显式授权(指定目录或应用),完全权限则需要每次操作前确认。

操作确认机制是安全层的第二道防线。对于不可逆的操作(删除文件、发送邮件、提交表单),Agent 必须在执行前向用户展示操作详情并等待确认。确认信息应该包含操作类型、操作对象和预期结果三个要素。例如,在发送邮件前,Agent 应该展示:「我将向 recipient@example.com 发送一封主题为「月度报告」的邮件,正文包含3 段文字和 1 个附件。是否继续?」

审计日志是安全层的第三道防线。Agent 的每一次感知、推理和执行都应该被记录下来,形成完整的操作链。这不仅用于事后的问题排查,也是用户建立对 Agent 信任的基础。如果 Agent 执行了一个意外操作,用户可以通过审计日志回溯决策过程,理解 Agent 为什么做出了那个决定。审计日志应该包含时间戳、行动类型、输入参数、输出结果和决策依据(Agent 的推理过程摘要)。

安全最佳实践:实现一个「紧急停止」按钮——在任何时候,用户都可以通过一个全局快捷键(如 Cmd+Shift+Esc)立即暂停 Agent 的所有执行,并回滚最近一次操作。这个功能的优先级应该高于一切其他功能,因为它是用户最后的保护手段。

关键安全警告:永远不要让桌面 Agent 在无人监督的情况下执行完全权限级别的操作。即使 Agent 的推理模型再可靠,也仍然存在模型幻觉(Hallucination)和对抗攻击(Adversarial Attack)的风险。攻击者可能在网页中嵌入精心设计的视觉元素,诱导 Agent 做出非预期操作——这种现象被称为「提示注入攻击」(Prompt Injection),在桌面 Agent 场景下后果更加严重,因为 Agent 不仅会读取恶意内容,还可能执行恶意操作。

6三种主流桌面 Agent 架构对比

当前市场上有三种主流的桌面 Agent 架构方案,它们各有优劣,适用于不同的使用场景。理解这些架构的差异,是选择或设计桌面 Agent 方案的决策基础。

第一种:浏览器扩展方案(OpenAI Codex Chrome、Anthropic Claude for Chrome)。这种方案将 Agent 作为浏览器扩展运行,通过 Content Script 与页面交互。它的优势在于部署简单(用户只需安装扩展)、安全性可控(扩展的权限受浏览器沙箱限制)和跨平台兼容(Chrome 扩展可以在 Chrome、Edge、Arc 等基于 Chromium 的浏览器上运行)。它的劣势在于只能操作浏览器内的内容,无法访问桌面文件、系统应用或其他非浏览器环境。

第二种:桌面应用方案(Perplexity PC、Claude Desktop、ChatGPT Desktop)。这种方案将 Agent 作为独立的桌面应用运行,通过操作系统的 API 与系统资源交互。它的优势在于系统级集成能力——可以访问文件系统、调用系统 API、与其他应用交互。Perplexity PC 甚至实现了Spotlight 级别的系统搜索,让 AI 搜索成为操作系统的默认搜索入口。它的劣势在于开发成本高(需要为每个操作系统单独开发)、权限管理复杂(操作系统级别的权限比浏览器沙箱更难控制)和更新频率低(桌面应用的更新需要用户手动安装或配置自动更新)。

第三种:开发环境集成方案(Cursor、GitHub Copilot Workspace、Windsurf)。这种方案将 Agent 嵌入到代码编辑器中,专门针对软件开发工作流进行优化。它的优势在于深度代码理解能力——Agent 不仅能看到代码文件的内容,还能理解项目结构、依赖关系、测试框架和构建系统。它的劣势在于高度专业化——这些 Agent 在其专业领域内表现极佳,但无法处理非开发任务(如邮件处理、日程管理、文档编辑)。

架构选择的核心原则是:没有一种方案适合所有场景。如果你的用户主要在浏览器中工作,浏览器扩展方案是最优选择。如果你的用户需要跨应用协调,桌面应用方案更合适。如果你的用户是开发者,开发环境集成方案的投资回报率最高。

选型建议:对于企业级部署,建议采用混合架构——在开发环境中使用 Cursor/Copilot(开发效率最高),在日常办公中使用浏览器扩展(安全性最好),在特定场景(如数据分析)中使用桌面应用(系统集成度最高)。这种场景化部署策略可以同时满足效率和安全的双重需求。

兼容性陷阱:浏览器扩展方案虽然跨平台兼容性好,但不同浏览器的扩展 API 存在差异。Chrome Extension Manifest V3 与 Firefox 的 WebExtensions API 在权限模型和后台脚本执行上有显著差异。如果你的 Agent 依赖特定的 API(如 webRequest 或 declarativeNetRequest),可能需要在不同浏览器上提供不同的实现。

7工程实践:从零构建一个桌面 Agent

构建一个桌面 Agent 是一个系统工程项目,涉及多个技术栈的整合。本节提供一个最小可行的桌面 Agent 架构,涵盖感知、理解、执行和安全四个核心模块。这个架构的目标不是生产级别的完善,而是帮助理解桌面 Agent 的核心设计模式。

技术选型建议:对于浏览器环境,使用 Playwright 作为自动化引擎(支持多浏览器、提供完善的等待机制)、OpenAI GPT-4oClaude Sonnet 作为推理引擎(视觉+文本多模态能力)、TypeScript 作为开发语言(类型安全、与 Playwright 生态完美集成)。对于桌面环境,使用 Electron 或 Tauri 作为应用框架(跨平台桌面应用)、Python 或 Swift 作为系统交互层(AppleScript/Accessibility API 调用)。

架构的核心设计模式是感知-推理-执行循环(Perception-Reasoning-Execution Loop, PRE Loop),这是所有桌面 Agent 的通用执行模型。Agent 首先获取当前桌面状态的感知快照,然后将快照与用户指令一起送入 LLM 进行推理,推理结果是一个行动计划(包含一系列待执行的行动),最后 Agent 执行这些行动并等待它们完成,完成后进入下一轮循环——重新感知、重新推理、重新执行,直到任务完成或达到最大循环次数。

循环终止条件的设计同样重要。Agent 不应该无限循环——这会导致资源消耗和不可预测的行为。工程上通常设置三个终止条件:任务成功(Agent 判断目标已达成)、任务失败(Agent 判断目标不可达或遇到不可恢复的错误)和达到最大循环次数(通常设置为 10-20 次,防止 Agent 在模糊的任务定义下无限执行)。

typescript
// 桌面 Agent 核心循环:Perception-Reasoning-Execution (PRE) Loop
import { Browser, Page } from "playwright";
import OpenAI from "openai";

interface PRELoopConfig {
  maxCycles: number;           // 最大循环次数
  cycleDelayMs: number;        // 循环间隔
  userInstruction: string;     // 用户指令
  dangerousActionsRequireConfirmation: boolean; // 危险操作需要确认
}

interface AgentState {
  history: PerceptionRecord[]; // 历史感知记录
  currentCycle: number;        // 当前循环计数
  taskCompleted: boolean;      // 任务是否完成
  taskFailed: boolean;         // 任务是否失败
  failureReason?: string;      // 失败原因
}

interface PerceptionRecord {
  cycle: number;
  timestamp: number;
  perception: Record<string, any>;
  reasoning: string;
  action: AgentAction;
  result: ExecutionResult;
}

class DesktopAgent {
  private openai: OpenAI;
  private browser: Browser;
  private state: AgentState;

  constructor(config: { openaiApiKey: string; browser: Browser }) {
    this.openai = new OpenAI({ apiKey: config.openaiApiKey });
    this.browser = config.browser;
    this.state = {
      history: [],
      currentCycle: 0,
      taskCompleted: false,
      taskFailed: false,
    };
  }

  async run(instruction: string, config: PRELoopConfig): Promise<AgentState> {
    console.log(`[Agent] 开始执行任务: "${instruction}"`);

    while (!this.state.taskCompleted && !this.state.taskFailed) {
      this.state.currentCycle++;

      if (this.state.currentCycle > config.maxCycles) {
        this.state.taskFailed = true;
        this.state.failureReason = `达到最大循环次数 (${config.maxCycles})`;
        break;
      }

      // 步骤 1: 感知 (Perception)
      const perception = await this.perceive();
      console.log(`[Agent] Cycle ${this.state.currentCycle}: 感知完成`);

      // 步骤 2: 推理 (Reasoning)
      const reasoning = await this.reason(
        instruction,
        perception,
        this.state.history
      );
      console.log(`[Agent] Cycle ${this.state.currentCycle}: 推理完成`);

      // 步骤 3: 执行 (Execution)
      const result = await this.execute(reasoning.action, config);

      // 记录完整的历史
      this.state.history.push({
        cycle: this.state.currentCycle,
        timestamp: Date.now(),
        perception,
        reasoning: reasoning.summary,
        action: reasoning.action,
        result,
      });

      // 判断任务状态
      if (reasoning.taskComplete) {
        this.state.taskCompleted = true;
        console.log(`[Agent] 任务完成(${this.state.currentCycle} 次循环)`);
      } else if (result.error && !reasoning.canRecover) {
        this.state.taskFailed = true;
        this.state.failureReason = result.error;
        console.log(`[Agent] 任务失败: ${result.error}`);
      }

      // 循环间隔
      await new Promise(r => setTimeout(r, config.cycleDelayMs));
    }

    return this.state;
  }

  private async perceive(): Promise<Record<string, any>> {
    // 简化版:获取页面标题和 URL
    const pages = await this.browser.pages();
    if (pages.length === 0) return { empty: true };
    const page = pages[0];
    return {
      url: page.url(),
      title: await page.title(),
      // 实际实现应包含更全面的 DOM 感知
    };
  }

  private async reason(
    instruction: string,
    perception: Record<string, any>,
    history: PerceptionRecord[]
  ): Promise<{ summary: string; action: AgentAction; taskComplete: boolean; canRecover: boolean }> {
    // 使用 LLM 进行推理
    const response = await this.openai.chat.completions.create({
      model: "gpt-4o",
      messages: [
        { role: "system", content: "你是一个桌面 Agent。根据当前页面状态和用户指令,决定下一步行动。以JSON格式返回。" },
        { role: "user", content: JSON.stringify({ instruction, perception, history: history.slice(-3) }) }
      ],
      response_format: { type: "json_object" }
    });
    return JSON.parse(response.choices[0].message.content!);
  }

  private async execute(action: AgentAction, config: PRELoopConfig): Promise<ExecutionResult> {
    // 危险操作需要确认
    if (config.dangerousActionsRequireConfirmation && this.isDangerous(action)) {
      // 实际实现中这里应该请求用户确认
      console.warn(`[Agent] 危险操作待确认: ${JSON.stringify(action)}`);
    }

    // 执行行动...
    return { success: true };
  }

  private isDangerous(action: AgentAction): boolean {
    // 定义危险操作的标准
    return ["delete", "send_email", "submit_payment"].includes(action.type);
  }
}

开发建议:在构建桌面 Agent 的早期阶段,建议启用「详细日志模式」——记录每一次感知快照、每一次 LLM 推理的输入输出、每一次执行的行动和结果。这不仅有助于调试,还能帮助你理解 Agent 的决策模式,从而优化提示词和行动空间设计。当你发现 Agent 在某个环节反复失败时,日志就是你的最佳诊断工具。

性能陷阱:每次 PRE 循环都涉及至少一次 LLM API 调用,这意味着每次循环的成本在 $0.01-0.10 之间(取决于模型和上下文长度)。如果 Agent 需要 20 次循环才能完成一个任务,单次任务的成本可能达到 $2。在高频率使用场景下,这会快速累积成本。优化建议:缓存感知结果(避免重复感知相同的页面状态)和压缩历史上下文(只保留最近 3-5 次循环的完整记录,更早的记录做摘要处理)。

8扩展阅读与未来展望

桌面 Agent 是 AI 从「内容生成」向「行动执行」演进的关键一步。理解这个领域的发展脉络,有助于把握 AI 应用的下一个增长方向。

推荐阅读方向:

  1. 浏览器自动化:深入学习 Playwright 和 Puppeteer 的高级特性——包括网络拦截、Service Worker 操控、Web 组件测试和多页面协调。这些工具不仅是桌面 Agent 的基础,也是自动化测试和数据采集的核心工具。

  2. 多模态大语言模型:关注 GPT-4oClaude Sonnet/Opus 和 Gemini 的视觉理解能力——特别是屏幕截图解析、UI 元素识别和操作意图推断这三个方面。多模态能力的进步直接决定了桌面 Agent 的感知精度上限。

  3. Agent 框架:研究 LangGraphCrewAIAutoGen 等 Agent 框架中的状态管理、工具调用和多智能体协作机制。这些框架提供了高层抽象,可以帮助你在应用层快速构建 Agent 能力,而不需要从零实现 PRE 循环。

  4. 操作系统级 AI 集成:关注 macOS 的 Apple Intelligence、Windows 的 Copilot+ PC 和 Linux 的 AI 桌面环境项目。操作系统级别的 AI 集成将从根本上改变桌面 Agent 的架构——未来的 Agent 可能不再需要绕过操作系统的安全限制,而是作为操作系统的一等公民获得原生支持。

未来展望:桌面 Agent 的发展将经历三个阶段。当前我们处于第一阶段——Agent 作为独立应用或浏览器扩展运行,通过模拟用户操作完成任务。这个阶段的 Agent 通用性有限、可靠性不足且安全隐患较多。第二阶段将在未来 1-2 年到来——操作系统开始提供原生的 Agent API,让 Agent 能够直接调用系统功能而不需要模拟操作。这将大幅提升 Agent 的速度和可靠性。最终,第三阶段将实现全自主的桌面 AI——Agent 不仅能操作现有的应用,还能自主创建新的工作流,甚至在用户没有明确指令的情况下主动识别需求并提供帮助。当然,这也意味着安全和隐私挑战将达到前所未有的高度。

持续学习:关注以下关键词的最新动态——Computer Use(Anthropic 的 Claude 计算机操作能力)、Operator(OpenAI 的自主浏览器 Agent)、Mariner(OpenAI 的浏览器操控模型)和Open-WebUI Agent。这些项目代表了桌面 Agent 的最前沿进展,它们的开源版本或 API 接入将极大地降低桌面 Agent 的开发门槛。

伦理提醒:随着桌面 Agent 能力的增强,人类对 AI 的依赖程度也在加深。当 Agent 能够自主完成搜索、写作、编程、邮件回复等任务时,人类可能逐渐丧失独立完成这些任务的能力。这不是技术问题,而是社会问题。作为开发者,我们有责任在设计 Agent 时考虑人类自主性的保护——让 Agent 成为增强人类能力的工具,而不是替代人类思考的拐杖。

继续你的 AI 学习之旅

浏览更多 AI 知识库文章,或者探索 GitHub 上的优质 AI 项目