把屏幕翻译成 LLM 能读的文本:Agent-View 的桌面感知层设计

大多数桌面智能体的做法是“每一步截一张图,喂给多模态模型”。这当然能跑,但慢、贵,而且很难调试。Agent-View 换了一个方向:把屏幕先翻译成文本大模型能直接读的标记,只有文本不够用时才调用视觉模型。

问题:桌面是像素,LLM 读的是 token

让 LLM 操作桌面,本质上是让它回答一个问题:

在当前界面里,为了完成目标,下一步应该点哪里、输入什么?

多模态模型可以直接看截图回答。但这条路径有三个现实问题:

  1. 慢:每一步都要上传截图、跑视觉编码,交互延迟高;
  2. 贵:一整张 1080p 截图的 token 成本,远高于几十行结构化文本;
  3. 难调:模型说“点右上角的按钮”,但右上角有三个按钮,失败后很难定位是感知错了还是决策错了。

Agent-View 的假设是:桌面上的大部分可操作信息,其实已经以文本形式存在——按钮有标签、输入框有占位符、菜单有文字、UIA 有控件树。把这些信息抽取出来,用稳定的 ID 和坐标组织成文本,纯文本模型就能完成大部分操作。视觉模型只在“必须理解图像内容”时介入,比如识别一张没有文字的海报、判断弹窗的图标含义。

四层架构

代码按职责分成四层,每层可以独立替换:

层 模块 职责
感知层 perception/ 截图、OCR、UI 元素检测、窗口信息、UIA 控件
表示层 representation/ 元素 ID 注册、生成文本标记、解析 LLM 引用
执行层 action/ 鼠标、键盘、剪贴板、启动应用、DPI 换算、重试
Agent 层 agent/ + cli.py 工具注册、smolagents CodeAgent 编排

在这四层之外,还有三个横向模块:grounding/ 负责把引用解析成坐标,memory/ 负责轨迹与经验,planning/ 负责把复杂任务拆成子任务。

数据流很短:

截图 + OCR + 检测 + UIA
        ↓
带 ID 的文本 markup
        ↓
LLM 选择工具 / 输出动作
        ↓
Grounding:ID / 文本 / 坐标 → 物理像素
        ↓
执行 + 前后截图验证

表示层:先给元素一个稳定的名字

表示层做两件事:给每个元素分配 ID,然后把元素列表序列化成文本。

ID 的规则是确定的:先按从上到下、从左到右排序,再按类型加前缀。按钮是 btn_1、输入框是 input_1、文本是 txt_1。同一份布局,两次感知应该得到同一组 ID;这样 LLM 在下一步引用 btn_3 时,指的还是同一个按钮。

序列化后的 markup 大致是这样:

[btn_1] button "发布" at (120,300,60,28)
[input_1] input "标题" at (100,200,400,32)
[chk_1] checkbox "记住我" [unchecked] at (50,350,80,20)
[txt_1] text "推荐" at (50,150,40,20)

每一行包含四类信息:ID、类型、可读文本、屏幕坐标。UIA 能读到的状态也会被注入,比如 [checked]、[value=...]、[disabled]。这样 LLM 不需要“看”截图,也能知道当前复选框是选中还是未选中。

这层看起来只是格式化,但它决定了后面所有环节的稳定性:

感知层:五源融合,而不是一张截图

感知层不是简单地“截屏 + OCR”,而是把五个来源合并:

  1. 视觉元素检测:默认 OpenCV 启发式(零模型依赖),可选 OmniParser(YOLO 图标检测 + Florence-2 描述);
  2. OCR:auto 模式按顺序探测服务化 OCR → 旧 MCP OCR → GPU PaddleOCR → CPU EasyOCR → Tesseract → CPU PaddleOCR,取首个可用;
  3. Windows UIA 控件树:按钮、输入框、复选框、单选框、滚动条、标题栏按钮等,带原生状态;
  4. 二维码检测:作为 type=qrcode 元素注入;
  5. 容器轮廓:在后台线程补充面板、对话框、列表的边界。

多源结果按 IoU 去重,UIA 控件的置信度最高(0.99),因为它来自操作系统而不是像素猜测。图标和标签会做绑定:一个没有文字的图标按钮,如果能和旁边的 OCR 文本对齐,就会被合并成一个带标签的元素。

这里有一个重要的工程取舍:默认路径必须零模型依赖。 OpenCV 检测器和轻量 OCR 开箱即用,不需要下载几百 MB 权重;只有需要更高精度时才切到 OmniParser + Florence-2。这让项目在普通笔记本上也能跑起来,而不是先配一台带 GPU 的机器。

Grounding:让多个专家竞争,而不是固定优先级

LLM 输出“点击 btn_3”之后,执行层需要把它变成真实坐标。这一步叫 Grounding。Agent-View 没有采用“先 ID、再文本、最后视觉”的固定优先级,而是让七位专家竞争:

专家 依据 典型置信度
Explicit LLM 直接给出 ID 或坐标 1.0
Text 按可见文本匹配 0.9 / 0.6
UIA UIA 控件树 0.95 / 0.7
VisionDetector 图标/图像描述 0.7 / 0.5
PlaywrightDom 浏览器 DOM 0.85
VisionLLM 通用多模态定位 0.7
UI-TARS 本地 UI-TARS-1.5 视觉 grounding 0.85

路由器按适用性排序,先跑非视觉专家;第一个置信度超过阈值(默认 0.6)的结果直接返回;全部低于阈值时,才升级到视觉专家。这样既避免了“每次都调用视觉模型”的成本,也避免了“文本匹配失败就放弃”的脆弱。

这个设计的关键不是专家数量,而是短路条件:大部分点击都能被 Explicit、Text 或 UIA 以高置信度解决,视觉模型只处理真正的长尾。

动作之后要验证,卡住要能回到安全点

点击和输入不是“发出去就完了”。执行层会在动作前后各截一张图,比较变化,向 LLM 报告三种结果:

如果连续 unchanged 达到阈值(默认 2 次),系统会提示回到最近的 checkpoint 重新决策。这就是恢复机制:不是让模型在错误状态里越陷越深,而是回到一个已知的安全点。

这套机制对桌面自动化尤其重要。网页可以靠 DOM 判断状态,桌面应用没有统一的 DOM;一次误点可能打开一个模态对话框,把后续所有步骤都带偏。前后截图对比不是万能的,但它是最便宜、最通用的验证信号。

记忆:让第二次做同一件事更快

记忆层记录成功任务的完整轨迹:任务描述、每一步的动作、坐标、结果。下一次遇到相似任务时,recall 会把历史轨迹、可复用技能和避坑提示返回给 LLM。

召回策略有三种:

任务结束后,反思引擎会总结成功/失败原因和标签;同类成功轨迹达到阈值(默认 3 条)后,抽象成 ExperienceSkill;失败教训保存为 AntiPattern。这让系统有机会“越做越快”,而不是每次从零开始。

不过,记忆也有边界:它只能复用“见过的路径”,不能保证在新界面上有效。所以记忆是加速器,不是正确性保证;最终还是要靠动作验证和 Grounding 兜底。

成本:文本优先,视觉按需

把上面的设计合起来,成本结构就很清楚:

在系统设计文档里,评估口径是:网页任务总耗时控制在 3 分钟以内、单任务 5 次运行成功率 ≥80%、Token 消耗相比纯视觉方案降低 50% 以上。这些是设计目标,不是本文宣称的实测结果。 真正要验证,需要在固定机器、固定网络、固定账号状态下跑基准,且把成功判据写死。

工程约束:Windows 优先,DPI 是硬骨头

目前完整验证的平台是 Windows 10/11。原因很实际:UIA、DPI 换算、窗口管理都依赖 Windows 的实现;纯视觉路径设计上跨平台,但 UIA 带来的高置信度元素在别的平台上没有直接替代。

DPI 是桌面自动化里最容易翻车的地方。截图是物理像素,鼠标 API 用的是逻辑坐标;多显示器、不同缩放比例下,两者不是简单的一个系数。执行层在点击前会按当前显示器做物理→逻辑换算,UIA 操作则优先走 Invoke/Toggle/Value 模式,失败再回退鼠标。

危险操作默认逐次确认:PowerShell 工具默认关闭,文件删除、注册表写入等动作需要用户确认。这不是“模型能力”的一部分,而是产品边界。

小结

Agent-View 的核心不是某个模型,而是一条表示管线:

  1. 把屏幕拆成元素,给每个元素一个稳定的 ID;
  2. 把元素序列化成 LLM 能读的文本,坐标和状态都不丢;
  3. 把 LLM 的引用通过多专家 Grounding 解析回像素;
  4. 用动作验证、恢复、记忆和规划把长任务串起来。

这条路线的价值在于可解释:当一步失败时,你可以问“是感知漏了元素、表示错了 ID、Grounding 选错了专家,还是决策本身错了”。相比“再截一张图问问模型”,这种可分解的结构更容易定位问题。

代码和设计文档在 Agent-View。它目前是一个 Windows 优先的 SDK,不是开箱即用的产品;但感知层、表示层和 Grounding 的分层,对其他平台的桌面智能体同样适用。