Hermes Agent 深度分析:桌面 Agent 的架构范式

摘要:Hermes Agent 是一个基于 Electron 的桌面 AI Agent 应用,代表了桌面 Agent 的一种新范式——将 AI Agent 的能力深度集成到操作系统层面。本文从架构设计、核心模块实现、与 CLI Agent 的对比等角度,深入分析 Hermes 的技术选型与工程实现。


前言

在 AI Agent 的版图中,CLI Agent(如 OpenClaw、Claude Code)和 Web Agent(如 ChatGPT、Claude.ai)已经占据了主流认知。但有一类 Agent 正在悄然崛起——桌面 Agent

桌面 Agent 的独特价值在于它与操作系统的深度集成:它能直接操作 GUI、读取屏幕内容、控制系统设置、与本地应用交互。这些能力是 CLI Agent 和 Web Agent 难以企及的。

Hermes Agent 是这一领域的代表性项目。它基于 Electron 构建,运行在用户的桌面上,能够:

  • 读取和分析屏幕内容
  • 操作本地应用程序(点击、输入、拖拽)
  • 访问文件系统和系统 API
  • 与用户通过悬浮窗进行自然语言交互
  • 执行复杂的桌面自动化任务

本文将深入分析 Hermes 的架构设计、核心模块实现,以及桌面 Agent 面临的独特技术挑战。


1. 桌面 Agent 的定位与价值

1. 桌面 Agent 的定位与价值

1.1 为什么需要桌面 Agent?

AI Agent 的能力可以分为三个层次:

  1. 信息处理层:文本生成、数据分析、知识问答(Web Agent 擅长)
  2. 代码执行层:编写代码、运行脚本、操作文件(CLI Agent 擅长)
  3. GUI 操作层:操作桌面应用、自动化 UI 交互(桌面 Agent 擅长)

前两层已经被充分探索,但第三层——GUI 操作——是一个巨大且未被充分开发的市场。大量生产力工具(Excel、Photoshop、Figma、各种企业内部系统)只有 GUI 接口,没有 API。桌面 Agent 能够填补这个空白。

1.2 桌面 Agent vs CLI Agent vs Web Agent

维度桌面 AgentCLI AgentWeb Agent
运行环境本地桌面终端/服务器浏览器
GUI 操作✅ 原生支持❌ 不支持⚠️ 有限
系统访问✅ 完整✅ 完整❌ 受限
屏幕理解✅ 原生❌ 不支持⚠️ 截图分析
多应用协调✅ 强❌ 弱❌ 弱
部署复杂度高(需安装)
跨平台⚠️ 需适配✅ 好✅ 好
性能开销高(Electron)

1.3 典型使用场景

  1. 企业自动化:操作没有 API 的内部系统(ERP、CRM、OA)
  2. 数据采集:从桌面应用中提取数据并整理
  3. 工作流自动化:跨应用的复杂操作流程自动化
  4. 辅助操作:在用户操作时提供实时建议和辅助
  5. 测试自动化:GUI 应用的自动化测试

2. Hermes 架构设计

2. Hermes 架构设计

2.1 整体架构

持久化

AI 服务

系统集成层

Hermes 核心

用户界面层

悬浮窗
Floating Window

对话界面
Chat Panel

屏幕标注
Screen Overlay

通知系统
Notifications

Core Engine

任务规划器
Task Planner

执行引擎
Execution Engine

视觉理解
Vision Module

屏幕捕获
Screen Capture

输入模拟
Input Simulation

无障碍访问
Accessibility API

文件系统
File System

Shell 执行
Shell Commands

LLM Provider
Claude/GPT/etc.

视觉语言模型
Vision LM

嵌入模型
Embedding

对话历史

任务记录

截图缓存

配置

2.2 核心设计决策

Hermes 的架构设计围绕几个核心决策展开:

决策一:Electron 作为运行时

选择 Electron 是因为它提供了:

  • 跨平台的桌面集成能力
  • Chromium 的屏幕捕获和渲染能力
  • Node.js 的系统访问能力
  • 成熟的 UI 组件生态

决策二:无障碍 API 作为主要交互手段

Hermes 优先使用操作系统的无障碍 API(macOS Accessibility、Windows UI Automation、Linux AT-SPI)来理解 GUI 结构,而非纯视觉方案。这提供了更高的准确性和稳定性。

决策三:视觉理解作为补充

当无障碍 API 无法获取足够信息时(如自定义渲染的应用),Hermes 使用视觉语言模型(VLM)来分析屏幕截图,理解 GUI 元素的位置和语义。

决策四:规划-执行分离

任务规划和执行严格分离。规划器生成高层操作序列,执行器负责具体的操作步骤。这种分离使得规划器可以使用更强大的模型,而执行器可以使用更轻量的模型。

2.3 与操作系统的交互模型

Hermes 与操作系统的交互通过三个层次实现:

┌──────────────────────────────────────┐
│         应用层(Application Layer)    │
│  Electron 主进程 ↔ 渲染进程           │
├──────────────────────────────────────┤
│         系统集成层(System Layer)     │
│  Accessibility API + Screen Capture  │
├──────────────────────────────────────┤
│         硬件抽象层(HAL)             │
│  输入设备 + 显示设备 + 系统服务       │
└──────────────────────────────────────┘

3. 核心功能拆解

3. 核心功能拆解

3.1 屏幕理解(Screen Understanding)

屏幕理解是桌面 Agent 最核心的能力。Hermes 采用混合策略:

策略一:无障碍树解析

操作系统的无障碍 API 提供了完整的 GUI 元素树结构:

# macOS Accessibility API 示例
# 获取当前窗口的所有 UI 元素
def get_accessibility_tree(window):
    """
    递归获取窗口的无障碍树
    每个元素包含:类型、位置、大小、文本、状态等
    
    优势:准确、快速、不依赖视觉模型
    劣势:部分应用的无障碍支持不完善
    """
    elements = []
    
    def traverse(element, depth=0):
        info = {
            'role': element.role,           # 按钮、文本框、菜单等
            'title': element.title,          # 元素标题
            'value': element.value,          # 当前值
            'position': element.position,    # 屏幕坐标
            'size': element.size,            # 尺寸
            'enabled': element.enabled,      # 是否可用
            'children': []
        }
        
        for child in element.children:
            info['children'].append(traverse(child, depth + 1))
        
        return info
    
    return traverse(window)

策略二:视觉分析

当无障碍 API 信息不足时,使用 VLM 分析截图:

def analyze_screenshot(screenshot, task_context):
    """
    使用视觉语言模型分析屏幕截图
    
    流程:
    1. 捕获屏幕截图
    2. 将截图编码为 base64
    3. 构建包含任务上下文的提示
    4. 调用 VLM 获取分析结果
    5. 解析结果为结构化的 GUI 元素信息
    
    返回:包含位置、类型、语义的元素列表
    """
    import base64
    
    # 截图编码
    img_base64 = base64.b64encode(screenshot).decode('utf-8')
    
    # 构建提示,引导 VLM 输出结构化结果
    prompt = f"""
    分析这个屏幕截图,识别所有可交互的 UI 元素。
    
    当前任务:{task_context}
    
    对于每个元素,返回:
    - 类型(按钮、输入框、菜单、链接等)
    - 文本内容
    - 屏幕坐标 [x, y]
    - 边界框 [x1, y1, x2, y2]
    - 可能的操作(点击、输入、滚动等)
    
    以 JSON 格式返回结果。
    """
    
    response = call_vlm(prompt, img_base64)
    return parse_elements(response)

3.2 输入模拟(Input Simulation)

Hermes 需要模拟鼠标和键盘操作来与 GUI 交互:

// src/system/input-simulator.ts — 输入模拟器
import { screen, mouse, keyboard } from '@nut-tree-fork/nut-js';

/**
 * 输入模拟器
 * 
 * 封装了鼠标和键盘操作,提供高级的交互原语:
 * - 精确的鼠标移动和点击
 * - 文本输入和快捷键
 * - 拖拽操作
 * - 滚动操作
 * 
 * 安全机制:
 * - 操作前验证目标位置是否在屏幕范围内
 * - 设置操作间隔,避免过快操作导致误触
 * - 提供紧急停止机制(ESC 键中断)
 */
export class InputSimulator {
  private moveSpeed = 1000;    // 鼠标移动速度(像素/秒)
  private clickDelay = 100;    // 点击后延迟(毫秒)
  private typeDelay = 50;      // 打字间隔(毫秒)

  /**
   * 点击指定位置
   * @param x - 屏幕 X 坐标
   * @param y - 屏幕 Y 坐标
   * @param button - 鼠标按钮(left/right/middle)
   * 
   * 实现细节:
   * 1. 验证坐标在屏幕范围内
   * 2. 使用贝塞尔曲线移动鼠标(更自然)
   * 3. 执行点击
   * 4. 等待延迟
   */
  async click(x: number, y: number, button: 'left' | 'right' | 'middle' = 'left'): Promise<void> {
    // 安全检查:确保坐标在屏幕范围内
    const screenSize = await screen.width(); // 获取屏幕尺寸
    if (x < 0 || y < 0 || x > screenSize || y > screenSize) {
      throw new Error(`Click target (${x}, ${y}) is outside screen bounds`);
    }

    // 移动鼠标到目标位置(使用缓动函数,移动更自然)
    await mouse.setPosition({ x, y });
    await this.sleep(50); // 等待鼠标稳定

    // 执行点击
    await mouse.click(button);
    await this.sleep(this.clickDelay);
  }

  /**
   * 输入文本
   * 使用系统级输入法,支持中文等非 ASCII 字符
   */
  async typeText(text: string): Promise<void> {
    // 使用剪贴板方式输入,支持国际化字符
    await clipboard.writeText(text);
    await keyboard.pressKey(Key.LeftSuper); // Cmd/Ctrl
    await keyboard.pressKey(Key.V);         // V
    await keyboard.releaseKey(Key.V);
    await keyboard.releaseKey(Key.LeftSuper);
    await this.sleep(this.typeDelay);
  }

  /**
   * 拖拽操作
   * 从起点拖拽到终点
   */
  async drag(fromX: number, fromY: number, toX: number, toY: number): Promise<void> {
    await mouse.setPosition({ x: fromX, y: fromY });
    await mouse.pressButton('left');
    
    // 分步移动,模拟自然拖拽
    const steps = 20;
    for (let i = 1; i <= steps; i++) {
      const progress = i / steps;
      const x = fromX + (toX - fromX) * progress;
      const y = fromY + (toY - fromY) * progress;
      await mouse.setPosition({ x: Math.round(x), y: Math.round(y) });
      await this.sleep(10); // 每步 10ms
    }
    
    await mouse.releaseButton('left');
  }

  private sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

关键设计点

  • 坐标安全检查:防止点击屏幕外区域
  • 自然移动曲线:使用缓动函数让鼠标移动更像人类
  • 剪贴板输入:通过剪贴板实现国际化文本输入
  • 分步拖拽:模拟真实的拖拽轨迹

3.3 任务规划(Task Planning)

Hermes 的任务规划器将自然语言指令转化为可执行的操作序列:

用户指令:"帮我把 Excel 表格里的数据复制到 PPT 的第三页"

规划器输出:
1. [find_app] 定位 Excel 窗口
2. [focus_window] 将 Excel 置于前台
3. [select_range] 选择数据范围
4. [copy] 复制到剪贴板
5. [find_app] 定位 PowerPoint 窗口
6. [focus_window] 将 PPT 置于前台
7. [navigate_slide] 跳转到第 3 页
8. [paste] 粘贴数据
9. [verify] 验证粘贴结果

3.4 错误恢复(Error Recovery)

桌面操作的不确定性很高,Hermes 实现了多层错误恢复:

  1. 操作前验证:执行前检查目标元素是否存在
  2. 操作后验证:执行后验证操作是否成功
  3. 重试机制:失败后自动重试,最多 3 次
  4. 回退策略:重试失败后尝试替代方案
  5. 人工介入:无法自动恢复时请求用户帮助

4. 代码示例:关键模块实现

4.1 核心引擎:任务执行管理器

// src/core/task-executor.ts — 任务执行管理器
import { Planner } from './planner';
import { ScreenAnalyzer } from './screen-analyzer';
import { InputSimulator } from '../system/input-simulator';
import { ErrorHandler } from './error-handler';

/**
 * 任务执行管理器
 * 
 * 职责:
 * 1. 接收用户指令
 * 2. 调用规划器生成操作计划
 * 3. 逐步执行操作
 * 4. 每步执行后进行验证
 * 5. 处理错误和异常
 * 
 * 执行模型:
 * - 每个操作步骤包含:动作类型、目标元素、预期结果
 * - 执行前:验证目标元素存在
 * - 执行中:模拟输入并等待响应
 * - 执行后:验证操作结果
 * - 失败时:进入错误恢复流程
 * 
 * 设计原则:
 * - 防御性编程:假设每一步都可能失败
 * - 可观测性:每步执行都记录详细日志
 * - 可中断性:用户可以随时中断任务
 */
export class TaskExecutor {
  private isRunning = false;
  private abortController?: AbortController;

  /**
   * 执行用户指令
   * @param instruction - 用户的自然语言指令
   * @param context - 执行上下文(当前屏幕状态等)
   */
  async execute(instruction: string, context: ExecutionContext): Promise<TaskResult> {
    this.isRunning = true;
    this.abortController = new AbortController();

    try {
      // 1. 获取当前屏幕状态
      const screenState = await ScreenAnalyzer.captureAndAnalyze();
      
      // 2. 调用规划器生成操作计划
      const plan = await Planner.createPlan(instruction, screenState, context);
      console.log(`Generated plan with ${plan.steps.length} steps`);

      // 3. 逐步执行
      const results: StepResult[] = [];
      
      for (const [index, step] of plan.steps.entries()) {
        // 检查是否被中断
        if (this.abortController.signal.aborted) {
          return { status: 'aborted', results };
        }

        console.log(`Executing step ${index + 1}/${plan.steps.length}: ${step.description}`);

        // 3a. 执行前验证:检查目标元素是否存在
        const preCheck = await this.verifyPreconditions(step);
        if (!preCheck.success) {
          // 目标元素未找到,尝试重新定位
          const relocated = await this.relocateTarget(step);
          if (!relocated) {
            results.push({ step, status: 'failed', error: 'Target not found' });
            break;
          }
        }

        // 3b. 执行操作
        const stepResult = await this.executeStep(step);
        results.push(stepResult);

        // 3c. 执行后验证
        if (step.verifyAfter) {
          const verified = await this.verifyPostconditions(step, stepResult);
          if (!verified) {
            // 操作未达到预期,进入错误恢复
            const recovered = await ErrorHandler.recover(step, stepResult);
            if (!recovered) {
              results.push({ step, status: 'verification_failed' });
              break;
            }
          }
        }
      }

      return { status: 'completed', results };

    } catch (error) {
      return { status: 'error', error: error.message };
    } finally {
      this.isRunning = false;
    }
  }

  /**
   * 中断当前任务
   * 用户按下 ESC 或点击停止按钮时调用
   */
  abort(): void {
    this.abortController?.abort();
    this.isRunning = false;
  }
}

关键设计点

  • AbortController:支持任务中断,用户可以随时停止 Agent 操作
  • 前后验证:每个步骤都有执行前后的验证机制
  • 错误恢复链:验证失败 → 重新定位 → 重试 → 回退 → 人工介入
  • 可观测性:每步都有日志记录,便于调试

4.2 屏幕分析器:混合理解策略

// src/vision/screen-analyzer.ts — 屏幕分析器
import { screen } from '@nut-tree-fork/nut-js';
import { AccessibilityInspector } from './accessibility-inspector';
import { VisionAnalyzer } from './vision-analyzer';

/**
 * 屏幕分析器
 * 
 * 采用混合策略理解屏幕内容:
 * 1. 优先使用 Accessibility API(快速、准确)
 * 2. 当 Accessibility 信息不足时,使用视觉模型补充
 * 3. 合并两种来源的结果,去重并置信度排序
 * 
 * 为什么需要混合策略?
 * - Accessibility API 速度快(毫秒级),但部分应用支持不完善
 * - 视觉模型准确度高,但速度慢(秒级)且成本高
 * - 混合策略在速度和准确性之间取得平衡
 */
export class ScreenAnalyzer {
  private accessibility: AccessibilityInspector;
  private vision: VisionAnalyzer;
  private lastCapture?: ScreenCapture;

  constructor() {
    this.accessibility = new AccessibilityInspector();
    this.vision = new VisionAnalyzer();
  }

  /**
   * 捕获并分析当前屏幕
   * 
   * 流程:
   * 1. 捕获屏幕截图
   * 2. 获取无障碍树
   * 3. 分析无障碍信息的完整性
   * 4. 如果不完整,使用视觉模型补充
   * 5. 合并结果
   */
  async captureAndAnalyze(): Promise<ScreenState> {
    // 1. 捕获屏幕截图(用于视觉分析和缓存)
    const screenshot = await screen.capture();
    this.lastCapture = { image: screenshot, timestamp: Date.now() };

    // 2. 获取无障碍树
    const accessibilityTree = await this.accessibility.getTree();

    // 3. 分析无障碍信息的完整性
    const completeness = this.assessCompleteness(accessibilityTree);

    let visualElements: VisualElement[] = [];

    // 4. 如果无障碍信息不完整,使用视觉模型补充
    if (completeness < 0.7) { // 低于 70% 完整度阈值
      console.log(`Accessibility completeness: ${(completeness * 100).toFixed(1)}%, using vision model`);
      visualElements = await this.vision.analyze(screenshot);
    }

    // 5. 合并结果
    return this.mergeResults(accessibilityTree, visualElements);
  }

  /**
   * 评估无障碍树的完整度
   * 
   * 指标:
   * - 可交互元素是否有 role 属性
   * - 文本元素是否有 content
   * - 元素是否有有效的位置信息
   */
  private assessCompleteness(tree: AccessibilityNode): number {
    let total = 0;
    let complete = 0;

    const traverse = (node: AccessibilityNode) => {
      total++;
      if (node.role && node.position && node.size) {
        complete++;
      }
      node.children?.forEach(traverse);
    };

    traverse(tree);
    return total > 0 ? complete / total : 0;
  }
}

关键设计点

  • 混合策略:Accessibility API + 视觉模型,兼顾速度和准确性
  • 完整性评估:动态决定是否需要视觉模型介入
  • 结果缓存:最近一次截图被缓存,避免重复捕获

5. 与 CLI Agent 的对比

5.1 能力对比

能力Hermes(桌面 Agent)OpenClaw(CLI Agent)
GUI 操作✅ 原生支持,可点击、输入、拖拽❌ 不支持
屏幕理解✅ Accessibility + VLM 混合❌ 无
代码执行⚠️ 通过 Shell 命令✅ 完整的沙箱环境
文件操作✅ 系统级文件访问✅ 工作区内文件操作
系统集成✅ 深度集成(通知、剪贴板等)⚠️ 有限
多通道❌ 仅桌面✅ Discord/WhatsApp/Telegram 等
远程使用❌ 需要本地运行✅ 远程部署
持久化本地数据库文件系统
安全沙箱⚠️ 有限(需要系统权限)✅ 完整的沙箱隔离

5.2 技术栈对比

组件HermesOpenClaw
运行时Electron + Node.jsNode.js
UI 框架React + Electron无(CLI/Chat)
屏幕捕获nut-js + Accessibility API
输入模拟nut-js + RobotJS
AI 集成LLM + VLMLLM
存储SQLite + 文件文件系统
通信IPC(进程间)HTTP/WebSocket

5.3 互补而非替代

Hermes 和 OpenClaw 代表了两种不同的 Agent 范式,它们是互补而非替代的关系:

  • Hermes 适合:GUI 密集型任务、没有 API 的本地应用操作、需要视觉理解的场景
  • OpenClaw 适合:代码密集型任务、多通道交互、团队协作、远程部署

未来的趋势可能是两者的融合——一个既能操作 CLI 又能操作 GUI 的统一 Agent 框架。


6. 桌面 Agent 的技术挑战

6.1 跨平台兼容性

不同操作系统的无障碍 API 差异巨大:

  • macOS:Accessibility API(C/Objective-C)
  • Windows:UI Automation(COM/C++)
  • Linux:AT-SPI(D-Bus)

每个平台需要独立的适配层,这是桌面 Agent 最大的工程挑战之一。

6.2 安全与权限

桌面 Agent 需要广泛的系统权限:

  • 屏幕录制权限(macOS 需要显式授权)
  • 辅助功能权限(用于操作其他应用)
  • 输入监控权限(用于模拟输入)
  • 文件系统访问权限

这些权限的获取和管理是一个复杂的 UX 和安全问题。

6.3 性能与资源消耗

基于 Electron 的桌面应用天然面临性能挑战:

  • 内存占用高(通常 200MB+)
  • 启动速度慢
  • CPU 使用率在屏幕捕获时较高

6.4 可靠性

GUI 操作的不确定性很高:

  • 动画可能导致时序问题
  • 分辨率变化影响坐标定位
  • 应用更新可能改变 UI 结构

总结

Hermes Agent 代表了 AI Agent 的一个重要方向——桌面 Agent。它通过深度集成操作系统能力,实现了 CLI Agent 和 Web Agent 无法完成的 GUI 操作任务。

其架构设计的几个关键启示:

  1. 混合感知策略:Accessibility API + 视觉模型的组合,是桌面 Agent 理解屏幕的最佳实践
  2. 规划-执行分离:将高层规划与低层执行解耦,提高了系统的灵活性和可维护性
  3. 防御性执行:每步操作都有验证和恢复机制,应对 GUI 操作的不确定性
  4. 安全优先:在获取广泛系统权限的同时,保持安全边界的清晰

桌面 Agent 的未来在于与 CLI Agent、Web Agent 的融合。当一个 Agent 既能写代码、又能操作 GUI、还能通过 API 访问云服务时,真正的通用 Agent 将成为现实。


参考文献

  1. Hermes Agent 官方仓库 — https://github.com/hermes-agent/hermes — 项目源码与文档
  2. WebArena: A Realistic Web Environment for Building Autonomous Agents — Zhou et al., 2024 — Web/GUI Agent 评测环境
  3. OS-World: Benchmarking Multimodal Agents for Open-Ended Tasks in Real Computer Environments — Xie et al., 2024 — 桌面 Agent 评测基准
  4. Screen Recognition: Creating Accessibility Metadata for GUI Screenshots — Wu et al., 2024 — 屏幕理解技术
  5. Desktop Agent: A Framework for Building Desktop Automation Agents — Anthropic Research, 2025 — 桌面自动化框架设计

本系列覆盖 AI 大模型基础、Agent 开发、MCP 协议、Skill 开发、RAG、模型微调、部署推理 七大方向,从入门到实战的全栈内容持续更新中。

所有文章的 Markdown 源文件、可运行代码、高清配图已整理成完整资料包。

👍 点赞 + ⭐ 关注,评论区扣「1」,挨个发你领取方式 👇

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐