把屏幕翻译成 LLM 能读的文本:Agent-View 的桌面感知层设计
大多数桌面智能体的做法是“每一步截一张图,喂给多模态模型”。这当然能跑,但慢、贵,而且很难调试。Agent-View 换了一个方向:把屏幕先翻译成文本大模型能直接读的标记,只有文本不够用时才调用视觉模型。
问题:桌面是像素,LLM 读的是 token
让 LLM 操作桌面,本质上是让它回答一个问题:
在当前界面里,为了完成目标,下一步应该点哪里、输入什么?
多模态模型可以直接看截图回答。但这条路径有三个现实问题:
- 慢:每一步都要上传截图、跑视觉编码,交互延迟高;
- 贵:一整张 1080p 截图的 token 成本,远高于几十行结构化文本;
- 难调:模型说“点右上角的按钮”,但右上角有三个按钮,失败后很难定位是感知错了还是决策错了。
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 不需要“看”截图,也能知道当前复选框是选中还是未选中。
这层看起来只是格式化,但它决定了后面所有环节的稳定性:
- ID 稳定,LLM 的引用才有意义;
- 坐标和文本同时存在,Grounding 才有多个解析路径;
- 结构化的行可以被程序审计,而不是只能靠模型“感觉”。
感知层:五源融合,而不是一张截图
感知层不是简单地“截屏 + OCR”,而是把五个来源合并:
- 视觉元素检测:默认 OpenCV 启发式(零模型依赖),可选 OmniParser(YOLO 图标检测 + Florence-2 描述);
- OCR:
auto模式按顺序探测服务化 OCR → 旧 MCP OCR → GPU PaddleOCR → CPU EasyOCR → Tesseract → CPU PaddleOCR,取首个可用; - Windows UIA 控件树:按钮、输入框、复选框、单选框、滚动条、标题栏按钮等,带原生状态;
- 二维码检测:作为
type=qrcode元素注入; - 容器轮廓:在后台线程补充面板、对话框、列表的边界。
多源结果按 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 报告三种结果:
changed:界面发生了变化;unchanged:看起来没变,可能点错了,也可能本来就没有视觉反馈;unverified:截图失败或无法比较。
如果连续 unchanged 达到阈值(默认 2 次),系统会提示回到最近的 checkpoint 重新决策。这就是恢复机制:不是让模型在错误状态里越陷越深,而是回到一个已知的安全点。
这套机制对桌面自动化尤其重要。网页可以靠 DOM 判断状态,桌面应用没有统一的 DOM;一次误点可能打开一个模态对话框,把后续所有步骤都带偏。前后截图对比不是万能的,但它是最便宜、最通用的验证信号。
记忆:让第二次做同一件事更快
记忆层记录成功任务的完整轨迹:任务描述、每一步的动作、坐标、结果。下一次遇到相似任务时,recall 会把历史轨迹、可复用技能和避坑提示返回给 LLM。
召回策略有三种:
recent:最近 N 条;similarity:字符 bigram Jaccard;info_theoretic:信息论打分,对中文更友好(默认)。
任务结束后,反思引擎会总结成功/失败原因和标签;同类成功轨迹达到阈值(默认 3 条)后,抽象成 ExperienceSkill;失败教训保存为 AntiPattern。这让系统有机会“越做越快”,而不是每次从零开始。
不过,记忆也有边界:它只能复用“见过的路径”,不能保证在新界面上有效。所以记忆是加速器,不是正确性保证;最终还是要靠动作验证和 Grounding 兜底。
成本:文本优先,视觉按需
把上面的设计合起来,成本结构就很清楚:
- 默认输入是 markup,不是截图;
- 视觉模型只在
look工具被 LLM 主动调用时使用,或者 Grounding 的非视觉专家全部失败时升级; - 记忆复用减少重复推理的轮次;
- 分层规划让 Worker 只关注当前子任务,避免长上下文。
在系统设计文档里,评估口径是:网页任务总耗时控制在 3 分钟以内、单任务 5 次运行成功率 ≥80%、Token 消耗相比纯视觉方案降低 50% 以上。这些是设计目标,不是本文宣称的实测结果。 真正要验证,需要在固定机器、固定网络、固定账号状态下跑基准,且把成功判据写死。
工程约束:Windows 优先,DPI 是硬骨头
目前完整验证的平台是 Windows 10/11。原因很实际:UIA、DPI 换算、窗口管理都依赖 Windows 的实现;纯视觉路径设计上跨平台,但 UIA 带来的高置信度元素在别的平台上没有直接替代。
DPI 是桌面自动化里最容易翻车的地方。截图是物理像素,鼠标 API 用的是逻辑坐标;多显示器、不同缩放比例下,两者不是简单的一个系数。执行层在点击前会按当前显示器做物理→逻辑换算,UIA 操作则优先走 Invoke/Toggle/Value 模式,失败再回退鼠标。
危险操作默认逐次确认:PowerShell 工具默认关闭,文件删除、注册表写入等动作需要用户确认。这不是“模型能力”的一部分,而是产品边界。
小结
Agent-View 的核心不是某个模型,而是一条表示管线:
- 把屏幕拆成元素,给每个元素一个稳定的 ID;
- 把元素序列化成 LLM 能读的文本,坐标和状态都不丢;
- 把 LLM 的引用通过多专家 Grounding 解析回像素;
- 用动作验证、恢复、记忆和规划把长任务串起来。
这条路线的价值在于可解释:当一步失败时,你可以问“是感知漏了元素、表示错了 ID、Grounding 选错了专家,还是决策本身错了”。相比“再截一张图问问模型”,这种可分解的结构更容易定位问题。
代码和设计文档在 Agent-View。它目前是一个 Windows 优先的 SDK,不是开箱即用的产品;但感知层、表示层和 Grounding 的分层,对其他平台的桌面智能体同样适用。