最近社区里关于 GUI-Agent 的讨论一下子就多起来了,阶跃星辰把 GUI-MCP 这个方向摆到台面上之后,HITL(Human In The Loop)又被反复拎出来讲。我花了两周时间把整套链路拆了一遍,顺手在桌面环境里跑了一个带人工确认闸门的最小原型,今天把这阵子的观察、踩坑和理解一次性说清楚。
先说结论:GUI-MCP 的本质不是让模型“看懂屏幕”,而是把“看屏幕、想动作、动手做”这三件事结构化地拆给不同模块,再用一套标准协议串起来。而 HITL 也绝不只是“在关键步骤弹个确认框”,它是在承认模型还很稚嫩的前提下,让 GUI-Agent 能安全地走出实验室、进到真实场景的唯一务实路径。
1. GUI-Agent 突然变热,但绝大多数实现还处在“稚”期
1.1 一个真实的翻车现场,比任何趋势报告都说明问题
我在测试一个基于多模态模型的 GUI 自动化流程时,让它去桌面端飞书里找一个同事的会话,然后回复一句“方案晚点发你”。指令很清晰,模型也真的驱动鼠标移动到了左侧会话列表,甚至还点开了一个头像。但问题出在它点开的不是同事 A,而是置顶群聊里昵称和同事 A 高度相似的一个人。这一步错了之后,后续动作全部错位:它把文字发进了完全不相干的群,还顺手关掉了窗口。
这个翻车现场很典型,它暴露的其实不是“模型傻”,而是整个 GUI-Agent 链路里最容易被忽略的问题:单步准确率再高,长链路执行起来也是指数级放大的风险。假设每一步视觉定位准确率是 95%,一个包含 10 步的操作任务,全部走对的概率只有 95% 的 10 次方,约 59.9%。如果任务有 20 步,就只剩 35.8%。而真实办公场景里的 GUI 操作,30 步以上非常常见。
这就是为什么我特别反对看 Demo 视频就下结论,一个精心挑选的演示任务说明不了任何问题。GUI-Agent 真正难的地方,不是“能不能学会”,而是“能不能在没人盯着的时候持续不犯错”。在这一点上,当前几乎所有方案都还相当稚嫩。
1.2 为什么 2024 年下半年开始,大家都在做同一件事
GUI-Agent 并不是新概念,PyAutoGUI、RPA、UI Automator 这些工具都存在很多年了。但过去用规则脚本做 GUI 自动化,每个应用都要单独写一套选择器,页面改版脚本就废,维护成本极高。大模型出现后,大家看到了用自然语言替代脚本语言的希望,这个方向在 2023 年就有不少工作,但当时普遍遇到一个尴尬:模型能描述“我想做什么”,但接不上“我该怎么调系统能力”。
这时候 MCP(Model Context Protocol)出来了。它解决的是一个很朴素的痛点:如果每个 Agent 都要自己定义一套工具调用协议,那生态会被打散;如果每家都接 MCP,模型就能用一套方式去调用文件、数据库、浏览器、设计软件等外部能力。
阶跃星辰把 MCP 往 GUI 方向延展,做 GUI-MCP,本质上是把“图形界面操作能力”也变成一种标准化的 MCP 工具服务。这样上层 Agent 不需要关心目标应用是用 Electron 写的还是原生 Win32 写的,只需要通过 MCP 接口去拿屏幕结构、做定位、发动作。这件事的巧妙之处在于,它没有试图让模型变得更聪明,而是先把接口拉齐了。
1.3 “稚”字的三层含义,我越拆越认同
标题里那个“稚”字,我一开始以为只是笔误,后来反复琢磨,觉得它比“之”更贴切。当前 GUI-Agent 的状态,完全当得起一个“稚”字。
第一层稚,在模型。多模态大模型对 GUI 的理解类似一个刚入职的实习生:看得懂界面大概长什么样,但对业务上下文、页面层级、交互隐喻的把握非常浅。它能识别出“这是一个按钮”,但很难判断“这个按钮点了会不可逆”。
第二层稚,在数据和评测。GUI-Agent 还缺少像 NLP 领域 GLUE、SuperGLUE 那样公认的评测体系。今天各家 Demo 都是自己出题自己考,很难横向评估一个 Agent 的真实水平,这也给实际选型带来了极大的不确定。
第三层稚,在工程。屏幕坐标漂移、应用内元素识别不稳定、长任务上下文爆炸、操作回滚缺失,这一大堆工程问题都还没有形成行业共识级的成熟方案。这个阶段去谈“全自动”,多少有点勉强,厂商们心里其实也清楚。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从 MCP 到 GUI-MCP:阶跃星辰的结构化思路值得拆开看
2.1 MCP 解决的是接口标准化,而不是让模型变聪明
要理解 GUI-MCP,得先理解 MCP 里三个角色:Host(模型住的地方)、Client(实际发起调用的进程)、Server(提供工具能力的服务)。模型自己并不直接操作外部系统,它通过 Client 去连接不同 Server,Server 暴露一组工具,模型按约定好的 JSON 结构发起调用。
MCP 最聪明的设计是没有发明新的 AI 范式,它只是做了一个极其标准化的“插头”。任何一个工具,只要实现成 MCP Server,接入支持 MCP 的客户端,就能瞬间被模型使用。今天社区里已经有不少现成 Server,比如文件系统、Git、浏览器、数据库等。阶跃星辰做 GUI-MCP,看上去也是沿用了这套思路,把“电脑屏幕前的操作”变成一种标准 Server。
但要小心一点:MCP 标准化了接口,并不意味着模型就能稳定操作 GUI。真正决定上限的还是能力层。GUI-MCP 的价值是把下层能力包装好,让模型不用去纠结“窗口句柄怎么拿”“鼠标事件怎么发”这些脏活,而是专注于“该点什么”“该输入什么”这类决策。
2.2 一个可复用的三层结构:感知、定位、执行
我拆解阶跃星辰 GUI-MCP 技术框架时,发现它实际上没有走“端到端截图预测动作”这种黑盒路线,而是更偏向结构化,把它分成三层:
| 层级 | 核心职责 | 典型实现方式 | 常见瓶颈 |
|---|---|---|---|
| 感知层 | 把屏幕转成模型可理解的输入 | 截图、OCR、可访问性树(A11y Tree) | 截图太大占 token,OCR 识别错字 |
| 定位层 | 把“自然语言目标”转成屏幕坐标或元素 ID | 视觉 grounding 模型、元素匹配 | 相似元素混淆,坐标偏差 |
| 执行层 | 真正模拟用户操作 | 鼠标键盘事件、辅助功能接口、系统 API | 应用不响应、权限不足 |
这三层不是线性关系,而是可以互相校验的。比如感知层拿到截图后,定位层给出“登录按钮在坐标 (1200, 640)”,执行层点击前,还可以用可访问性树反查一下这个坐标下有没有命中一个按钮类型节点,做一次交叉验证。这种冗余设计在“稚”期特别重要,因为它能大幅减少单点误判导致的连锁错误。
普通的 MCP Server 一般只做一件事,比如读写文件,工具之间互相独立。但 GUI-MCP 特殊在,它的工具之间存在很强的状态依赖:先 screenshot,再 detect_element,再到 click,这是一条有顺序的链。这就要求 server 在内部维护一份“当前屏状态”,而不是每次调用都从零开始理解。
2.3 真正值钱的设计:grounding 与动作空间约束
GUI-MCP 整个链路里,我最关注的是两个容易被忽视的设计点:跨模态的目标定位和动作空间约束。
跨模态定位,也就是 grounding,指的是模型要把“文字描述的目标”对应到“图片里的具体坐标”。这里面最大的坑不是模型找不到元素,而是界面里总有一堆相似元素或诱导性元素。比如界面上同时存在“保存”和“另存为”,模型如果只做粗粒度匹配,很容易点错。成熟的 GUI-MCP 会要求定位层同时输出坐标、置信度和元素类型,低置信度时主动触发确认,而不是硬着头皮执行。
动作空间约束更难做,但这恰恰是我认为阶跃这个方案最值得抄作业的地方。所谓约束动作空间,就是强制模型只能从一组预定义好的合法动作中选择,比如 click、double_click、scroll、input、hotkey,禁止它自定义操作。这个小设计能挡住相当大比例的胡来行为。没有约束的 Agent 可能试图“凭空点击一个不存在的元素”,有了约束后模型至少是在一个有限集合里做选择,哪怕选错了,问题也更容易回滚和定位。
我在自己实现的时候还加了另一个约束:每个执行动作必须声明前置条件。比如执行 click 之前必须先确认“目标元素已可见”,执行 input 之前必须确认“焦点在输入框内”。这个思路从自动驾驶领域借鉴过来,效果立竿见影。
3. HITL 不是简单地“找人审批一下”
3.1 纯自动化的窗口期很短,但不是因为 AI 不够强
很多人把 HITL 理解成“对模型能力不信任,所以找个人类盯着”,这个看法不能说错,但实在太肤浅了。我在第一节那个翻车案例里已经说了,GUI 操作链路的错误会指数级放大,这是系统结构性问题,不是模型单独就能解决的。
更进一步说,HITL 的真正价值不是兜底,而是给系统提供反馈信号。模型执行了一个动作,如果人类不纠正,模型并不知道它做对了还是做错了。一旦人类介入,我们就得到了一个“这个场景下,正确动作是什么”的标注数据。短期看 HITL 是在限制自动化,长期看它其实是在给自动化积累训练样本。
我见过不少团队把 HITL 简单做成一个“审批按钮”,动作列表推给人,人点一下允许就继续。这是最入门的形态,它能挡住一部分高风险操作,但挡不住长链路中积累的错误。真正的 HITL 要在多个层级都有介入点,而且介入本身也要被记录和利用。
3.2 我把 HITL 分成四个层次,越往后价值越大
第一个层次是执行前确认,也就是 Pre-execution Confirmation。Agent 每次执行动作前,把动作描述、目标坐标、置信度传给人类,人类批准后才执行。这个层级实现最简单,但对高频操作来说打扰成本高,适合用于第一步启动、不可逆操作这类场景。
第二个层次是执行中中断,也就是 Mid-execution Interrupt。人类不需要逐条审批,Agent 可以连续执行,但人类随时可以叫停并接管。这个机制类似自动驾驶的方向盘介入,需要设计好“人类接管后的权限切换逻辑”。我用一个状态机来管理:Agent 在执行中,人类可发 interrupt 信号,Agent 收到后立即暂停,等待下一步指令。
第三个层次是局部纠偏,也就是 Targeted Correction。这比中断更进一步,人类不仅可以叫停,还可以直接选中某个元素说“点这里,不是那里”,或者修改即将输入的文本。这个层次能够让模型在同一个会话内快速理解人类意图,同时把纠偏动作记录下来,作为后续 prompt 优化的参考。
第四个层次是事后沉淀,也就是 Post-hoc Review and Memory。任务跑完后,人类回看整个操作轨迹,标注哪些步骤是错的,哪些是正确的。这些标注会沉淀到 GUI-MCP 的记忆模块中,以后遇到相似界面时优先参考。这个层次的长期价值最高,因为它实际上把 HITL 变成了一个持续的数据飞轮。
我做的原型最终只实现到第三层,第四层还在整理中,但即便只到第三层,整个系统的可靠性和可解释性已经比“纯自动化”高了一个量级。
3.3 一个反直觉经验:人类越早介入,模型反而越不会犯错
接到阶跃 GUI-MCP + HITL 方向后,我做过一个小小的对照实验:一个 Agent 在操作桌面流程时全程自动,另一个在关键步骤处有人类确认。结果自动的那个在第五步点错了一个按钮,后续全崩。而带 HITL 的那个,虽然慢一点,但全程没有重大失误。
后来我想明白了原因:HITL 介入得早,意味着模型还在低风险阶段就被纠正,避免了错误累积到不可挽回。更重要的是,人类确认时的犹豫本身也是一种信号,模型介入得越多,越能学到一个“这条任务里,哪些步骤是有风险的”先验知识。
这个经验用一句话总结就是:HITL 不是降低效率的妥协,而是让模型在真实世界里边做边学的安全训练场。如果把 GUI Agent 比作一个刚上路的新手司机,HITL 就是副驾的教练员,不是让司机永远开不快,而是让司机知道哪条路能走、哪条路要踩刹车。
4. 手把手搭一个带 HITL 的 GUI-Agent 最小原型
4.1 先想清楚边界:原型只做三件事
理论聊再多,不如跑一个最小闭环。我的原型只做三件事:通过可访问性树读取当前屏幕上的元素,用大模型生成动作序列,在每个动作执行前过一个 HITL 闸门。
这里我刻意没有先用截图驱动,而是选可访问性树作为第一感知源。原因有两个:一是 A11y 树给的是结构化的元素信息,比纯截图转坐标省 token 且更稳定;二是它天然带元素类型,方便做动作空间约束。
原型的技术栈是 Python + PySide6 写的一个很小的桌面应用,加上一个基于 MCP 协议的本地 Server。整个链路是:用户用一个自然语言指令发起任务,比如“帮我在记事本里输入一段自我介绍,然后保存到桌面”,Server 把这个指令和 A11y 树一起发给模型,模型输出动作序列,动作序列经过 HITL 闸门,人类确认后执行。
4.2 核心代码:把 HITL 做成工具层,而不是业务层
这是一个容易被忽视的设计选择。很多团队喜欢在业务代码里到处插桩,问人类“要不要执行”,最终代码耦合得乱七八糟。我的做法是把 HITL 做在 MCP Server 的工具调用层,这样所有业务动作自动获得人工确认能力,不需要上层模型做额外处理。
先定义一个简单的动作数据结构:
python复制from dataclasses import dataclass
from enum import Enum
class ActionType(str, Enum):
CLICK = "click"
INPUT = "input"
SCROLL = "scroll"
HOTKEY = "hotkey"
@dataclass
class GUIAction:
action_type: ActionType
target_element_id: str | None = None
target_text: str | None = None
input_text: str | None = None
confidence: float = 0.0
requires_confirmation: bool = True
然后在 MCP Server 的 call_tool 入口里加一个统一的 HITL 闸门:
python复制class HITLGate:
def __init__(self):
self.pending_queue = []
self.human_events = {}
def request_confirmation(self, action: GUIAction) -> bool:
if not action.requires_confirmation:
return True
ticket_id = uuid4().hex
# 把待确认动作推给前端界面
self.pending_queue.append({
"ticket_id": ticket_id,
"action": action
})
# 阻塞等待人类确认/拒绝/修改
while ticket_id not in self.human_events:
time.sleep(0.05)
result = self.human_events.pop(ticket_id)
return result["approved"]
def call_tool(tool_name: str, arguments: dict):
if tool_name == "click":
action = GUIAction(
action_type=ActionType.CLICK,
target_element_id=arguments["element_id"],
confidence=arguments.get("confidence", 0.5),
)
gate = HITLGate()
if not gate.request_confirmation(action):
return {"status": "rejected", "reason": "human rejected"}
# 真正执行点击
return executor.click(arguments["element_id"])
这个实现会阻塞线程 0.05 秒轮询一次人工事件,虽然不优雅,但对于单用户的桌面原型完全够用。生产环境可以换成消息队列或 WebSocket 事件驱动,但核心思路不变:HITL 是工具层的统一控制器,不是散落在业务逻辑里的零散判断。
我还加了一个细节:如果动作的 confidence 低于 0.7,不管工具声明里 requires_confirmation 是不是 False,闸门都会强制拉起确认框。这个逻辑直接写在 gate 的 request_confirmation 里,我自己的经验是它帮我挡住了不少误点击。
4.3 跑通后我踩过且建议你避开的三个坑
第一个坑是坐标漂移。刚开始我让定位层直接返回屏幕绝对坐标,一旦窗口拖动、缩放或者系统切换了 DPI 缩放比例,执行层点击的位置就全部偏掉。后来统一改成“目标元素 ID + 当前窗口句柄”,执行层每次点击前重新获取元素位置,问题才解决。
第二个坑是可访问性树的稳定性。Electron、Qt 这类跨平台框架的 A11y 树结构非常不稳定,同一个按钮在不同版本的框架里可能暴露成不同类型的节点。我的做法是先跑一轮“A11y 结构探测脚本”,把目标应用的真实树结构打出来,再去适配解析逻辑,避免凭空拼 JSON 路径。
第三个坑是上下文污染。HITL 确认过程中,人类会在界面上输入反馈,这些反馈如果不加处理就直接塞进上下文,会导致模型误以为“这些是人类需要执行的内容”。我在实现时把所有人类反馈统一包成 system 级别的修正指令,并设置独立的 token 预算,一旦超了就提醒模型“仅保留关键纠偏信息,丢弃历史截图”。
5. 从“稚”到“熟”:我判断未来会变的几个方向
5.1 屏幕权威性和 A11y 权威性会合并,而不是二选一
当前很多 GUI-Agent 方案分两派:一派只信截图派,认为屏幕像素就是最终真相;另一派只信可访问性树派,认为结构化数据才是王道。我自己的经验是这两派都有坑。
截图派的模型确实能“看懂”界面,但面对高清大屏时 token 消耗太严重,而且如果目标应用有自定义绘制、模糊效果、不规则 UI 元素,模型经常会被视觉细节带偏。A11y 树则相反,它拿到的是应用内部的结构信息,稳定省 token,但很多 WebView、游戏、视频渲染界面根本不暴露 A11y 结构,有的甚至是空树。
未来 GUI-MCP 的成熟形态,大概率是两条路合并:用截图做粗粒度场景理解,用 A11y 树做细粒度元素定位,两者互为校验,然后再由 HITL 模块兜底处理两者冲突的情况。我在原型里就已经按这种双通道方式设计,只是目前合流逻辑还比较糙。
5.2 Grounding 评测集与回放测试会先于 Agent 成熟
我之前一直在找 GUI-Agent 的公开评测体系,能找到的少且零散。哪怕厂商 Demo 做得再惊艳,没有标准评测集和可回放的测试环境,用户就无法判断它到底能不能上生产。
我觉得下一步会先出现一批围绕 grounding 的专项评测集,用来度量“模型能否在特定屏幕上精确定位目标元素”。这个评测集不需要很大,但必须覆盖相似元素、重叠窗口、多显示器、深浅色模式等边界场景。等 grounding 评测做扎实了,再往上加长链路评测才可信。
与之配套的是回放测试框架,也就是录制一段真实操作轨迹,跑完 Agent 后逐帧对比轨迹与预期的一致性。这个说起来简单,做起来难,因为真实 GUI 的微小时间误差太多,但一旦成熟,它会是比“Demo 演示”可靠得多的验收标准。
5.3 MCP 生态里会分化出“GUI 技能市场”,HITL 是技能上架前的审核员
MCP 生态大了之后,一定会有“技能市场”类的平台出现,开发者可以把特定软件的 GUI 操作流程封装成技能包,比如“用剪辑软件导出字幕”“在 ERP 系统里审批单据”等,这些技能包本质上是一批预置好的工具描述、prompt 模板和校验规则。
在这个生态里,HITL 的价值还会再放大一档。技能包上架之前,要有人去实测它在不同环境下的表现,而 HITL 采集到的人类纠偏记录就是质量验证的核心素材。没有 HITL 踩过的坑,技能包就是空中楼阁。
聊到最后,我想分享一个我自己的实操建议:如果你现在团队里想做 GUI-Agent,别一上来就追求全自动。你可以在 MCP Server 的工具层加一个可配置的 HITL 闸门,第一次先让人确认每一步,跑一段时间积累反馈数据,再逐步放开不需要人工确认的动作。这个过程走完之后,你会发现自己得到的不仅是一个能自动操作 GUI 的系统,而是一份非常珍贵的“哪些步骤模型容易出错、哪些界面会让模型迷惑”的实战地图。这个地图,才是你在“稚”期里能攒下的最值钱的资产。
