
莫拉维克悖论这个词最早是从搞机器人方向的朋友嘴里听到的。他的原话是“让机器算微积分它一秒搞定让它像三岁小孩一样自己穿袜子它能把自己缠成粽子。”这话糙但把问题说透了。后来我把这个视角挪到Computer Use这个方向上发现它几乎是理解当前所有“让AI操作电脑”类项目天花板的一把钥匙。所谓Computer Use说白了就是让模型像人一样看屏幕、点鼠标、敲键盘替你把电脑上的活儿干了。听起来很酷但真正上手做过的人都知道难的不是让它写代码而是让它稳稳当当地点中一个按钮。这篇东西就是想把我在这个方向上踩过的坑、想明白的道理、以及那些文档里不会写的细节掰开揉碎讲清楚。不管你是刚接触这个方向想搞明白它为什么这么难还是已经在做相关项目被各种诡异失败折磨得够呛下面这些内容应该都能对上你的胃口。1. 先搞清楚莫拉维克悖论到底在说什么1.1 一个反直觉的难度排序莫拉维克悖论的核心表述其实很简单对人类来说越难的事情对机器往往越容易对人类来说越容易的事情对机器反而越难。下棋、算数、逻辑推理这些人类需要专门训练才能掌握的能力机器靠着算力堆上去就能碾压人类。但走路、抓东西、辨认一张脸、在杂乱的桌面上找到自己的杯子这些人类几乎不费吹灰之力的事情机器做起来却磕磕绊绊。这个悖论放到Computer Use场景里表现得淋漓尽致。你让一个模型去解一道复杂的数学题它可能几秒钟就给你一个漂亮的推导过程。但你让它去一个从没见过的网页上找到“提交”按钮并准确点击它可能反复点偏、点错、或者干脆对着一个长得像按钮的广告图点下去。这不是模型不够聪明而是感知与行动的耦合这件事本身对机器来说就是另一个维度的难题。我刚开始接触这个方向的时候也不理解觉得“看屏幕点鼠标”这么简单的事模型怎么可能做不好。后来自己动手搭了一套流程才明白人类做这件事的时候背后调动的是视觉识别、空间定位、手眼协调、常识判断、纠错反馈一整套系统而且这套系统是经过几十年日常训练打磨出来的。模型要在几秒钟内复现这套东西难度被严重低估了。1.2 为什么“简单”的动作反而最难要理解这个得把“点鼠标”这个动作拆开看。人类点一个按钮大脑里发生的事大致是眼睛看到屏幕视觉皮层识别出“那是一个按钮”空间认知系统定位它的坐标运动皮层规划手部移动轨迹小脑微调力度和精度手指执行点击同时视觉系统持续反馈确认点中了没有。这一整套流程在人类身上是高度自动化、几乎无意识的。模型要做同样的事得把这条链路全部显式地重建出来。它需要从像素里理解界面语义这是按钮还是装饰需要把语义映射到精确的屏幕坐标这个按钮在1920x1080的哪个位置需要生成一个动作点击坐标x,y还需要在执行后判断结果点对了没有界面变了没有。每一步都可能出错而且错误会累积。第一步识别错了后面全白搭。更麻烦的是这些步骤之间没有清晰的边界。人类是一体化处理的模型却得靠不同的模块拼接。拼接的地方就是最容易出问题的地方。我见过太多案例模型明明“看懂”了界面但坐标算错了几个像素点到了旁边的空白处整个任务就卡住了。这种失败在人类看来简直莫名其妙但对模型来说却是家常便饭。1.3 这个悖论对Computer Use意味着什么理解了莫拉维克悖论你就能明白为什么Computer Use类项目的进展总是“看起来很近做起来很远”。演示视频里模型流畅地操作各种软件看起来很惊艳但真正部署到实际环境里各种边角情况立刻暴露。这不是团队不努力而是这个方向天然就踩在悖论最难的那一侧。我的判断是当前阶段不要指望模型能像人一样“通用地”操作任何界面。更现实的路径是把任务拆窄、把环境收窄、把容错做厚。比如专门针对某一类后台管理系统做操作界面元素相对固定模型只需要处理有限的变化成功率就能拉上去。反过来如果你一上来就想做一个“什么都能点”的通用助手大概率会在无穷无尽的边角情况里耗尽精力。这个认知很重要它决定了你做项目的策略。是追求通用性还是追求可靠性是堆模型能力还是堆工程兜底这些选择背后都是对莫拉维克悖论的理解深度。我个人的经验是在感知和行动这条链路上工程兜底的价值被严重低估了。很多人把希望全押在模型变强上但模型再强像素级的坐标误差、界面加载的时序问题、弹窗的意外干扰这些还是得靠工程手段去消化。2. 拆解Computer Use的核心技术链路2.1 从屏幕像素到界面语义的理解层模型要操作电脑第一步是“看懂”屏幕。这一步的输入是一张截图输出是对界面的结构化理解。听起来像普通的图像识别但实际难度高得多。普通图像识别是“这张图里有只猫”而界面理解需要的是“这个区域是一个可点击的按钮它的功能是提交表单当前处于禁用状态”。这里的关键难点在于界面语义的层次性。一个网页截图里有文字、有图标、有输入框、有下拉菜单、有广告、有装饰性元素。模型需要区分哪些是可交互的哪些是纯展示的哪些是干扰项。人类扫一眼就能分辨因为我们对界面有大量的先验知识——我们知道按钮通常长什么样知道输入框有边框知道广告位通常在侧边。模型没有这些先验得靠训练数据去学。我在实际项目里试过几种做法。一种是直接用通用的视觉模型做界面描述让它输出“屏幕上有什么”。实测下来描述得挺像那么回事但一到具体坐标就含糊了。另一种是专门训练一个界面元素检测模型输出所有可交互元素的边界框和类型。这个路子更靠谱但需要大量标注数据而且遇到没见过的界面风格就容易漏检。提示界面理解层不要追求“描述得多好”而要追求“定位得多准”。描述再漂亮坐标偏了就是废的。评估这个模块的指标应该是元素定位的准确率和召回率而不是描述文本的流畅度。还有一个容易被忽略的点是界面状态的时序变化。截图是静态的但界面是动态的。点击一个按钮后可能弹出加载动画可能跳转新页面可能原地刷新内容。模型如果只理解单帧就会在状态切换时迷失。我的做法是在理解层里加入前后帧的对比逻辑让模型知道“哪些区域发生了变化”这样它对“刚才的操作产生了什么效果”就有感知了。2.2 动作空间的表示与生成理解层输出的是“界面上有什么”动作层要解决的是“接下来做什么”。这里的核心问题是动作怎么表示。最直接的方式是坐标点击输出一个(x, y)坐标加上点击类型左键、右键、双击。这种方式简单直接但精度要求极高差几个像素就可能点空。另一种方式是元素索引让理解层给每个可交互元素编个号动作层输出“点击第3号元素”。这种方式对坐标精度不敏感但依赖理解层的检测质量漏检了就点不了。我在项目里两种方式都试过最后倾向于混合方案优先用元素索引索引不可用时回退到坐标点击。这样既保证了常见情况的稳定性又保留了处理意外界面的灵活性。动作空间里还有键盘操作。输入文字、快捷键、Tab切换焦点这些在人类操作里占比很高但对模型来说同样有坑。输入文字看似简单但涉及到焦点管理——你得先确保光标在正确的输入框里否则文字就打到别处去了。快捷键更麻烦不同软件、不同系统下的快捷键行为不一致模型很难建立稳定的预期。动作类型表示方式优势主要坑点坐标点击(x, y) 点击类型通用性强不依赖检测精度要求高易点偏元素索引点击元素ID精度不敏感稳定依赖检测质量漏检即失效文字输入文本内容 目标元素语义清晰焦点管理易出错快捷键组合键序列效率高跨软件行为不一致滚动方向 距离处理长页面滚动量难精确控制动作生成还有一个隐蔽的难点是动作序列的规划。很多任务不是一步能完成的需要多步操作。比如“在电商后台修改商品价格”得先登录、再找到商品、再进入编辑页、再定位价格字段、再修改、再保存。每一步都依赖上一步的结果中间任何一步失败后面全乱。模型需要具备一定的规划能力知道当前处于哪个阶段下一步该干什么。这个规划能力目前主要靠提示词引导或者少量示例来激发稳定性还远远不够。2.3 执行反馈与闭环纠错动作执行出去之后得知道结果如何。这一步是很多项目容易偷懒的地方——点完就点完了不检查结果。但在实际环境里不检查结果的系统基本不可用因为失败太常见了。闭环纠错的核心是“执行后重新观察”。点完一个按钮重新截一张图对比前后变化判断操作是否生效。如果没生效得分析原因是点偏了是按钮禁用了是弹窗挡住了然后决定重试还是换策略。这个循环听起来简单但实现起来要考虑很多细节。首先是等待时机。点完按钮后界面可能不会立刻变化有加载时间。截太早看到的是旧界面会误判为失败截太晚浪费时间。我的做法是设置一个自适应等待先短间隔轮询检测到变化就停超过阈值还没变化就判定为异常。这个阈值需要根据具体应用的响应速度来调没有通用值。其次是变化检测的粒度。整张图对比太粗一个像素的闪烁都会触发误报局部对比又可能漏掉关键变化。我一般是对比关键区域比如按钮附近、内容主区域忽略掉时钟、动画这类持续变化的元素。这个需要针对具体界面做配置没法完全自动化。注意闭环纠错不是万能的。如果模型对界面的理解本身就是错的它可能把“点错了但碰巧触发了别的功能”误判为成功。所以纠错逻辑里最好加入任务层面的校验比如“价格是否真的改成了目标值”而不只是“界面是否发生了变化”。还有一个实战教训是重试策略要有上限。我见过系统陷入死循环反复点同一个按钮每次都失败每次都重试把资源耗光。正确的做法是设置最大重试次数超过就上报异常或者切换策略。这个上限设多少合适取决于任务的重要程度和环境的稳定性一般三到五次比较合理。3. 实操搭建一套可用的操作流程3.1 环境准备与基础工具选型动手之前先把环境理清楚。Computer Use类项目的基础设施大致包括一个能截屏的宿主环境、一个能模拟鼠标键盘的接口、一个能跑模型的推理服务、以及一个调度这些组件的控制逻辑。这四块怎么拼直接决定了后续开发的顺手程度。宿主环境我建议用虚拟机或者容器化的桌面环境好处是隔离性好出问题了好恢复而且可以并行跑多个实例做测试。直接在物理机上跑也不是不行但一旦模型乱点把系统搞乱了恢复成本很高。我早期图省事在主力机上直接跑结果模型把一个配置文件点开改乱了折腾半天才恢复从那以后老老实实用隔离环境。截屏和输入模拟这块不同系统下的工具选择差别挺大。核心要求是截屏要快、输入要准。截屏慢会导致整个循环的节奏被拖垮输入不准会导致点击偏移。选型的时候重点测这两个指标别只看功能列表。有些工具功能很全但延迟高实际用起来很难受。推理服务这块如果模型是本地跑的要注意显存和并发的平衡如果是调远程接口要注意网络延迟对循环节奏的影响。我的经验是整个循环的耗时里推理往往不是瓶颈截屏和输入模拟的延迟才是。所以优化的时候先看这两块别一上来就想着换更大的模型。3.2 界面理解的落地实现界面理解模块的落地我走过两条路各有优劣。第一条是纯提示词路线把截图丢给多模态模型让它输出界面描述和可点击元素列表。这条路开发快不用训练但稳定性和精度都一般尤其是元素坐标经常不准。第二条是专用检测模型路线自己标注数据训练一个界面元素检测器精度高很多但前期投入大。如果项目处于验证阶段建议先走提示词路线快速跑通闭环验证整体思路可行。等确认方向没问题了再逐步替换成专用模型提升精度。这个渐进策略能帮你省下大量前期投入避免在方向没验证清楚的时候就陷进数据标注的泥潭。提示词路线里有个技巧值得分享让模型输出结构化的元素列表而不是自由描述。比如要求它按固定格式输出每个元素的类型、文本、大致位置、是否可点击。结构化输出便于后续程序解析也便于你做质量评估。自由描述看起来灵活但下游没法用等于白做。专用检测模型路线里标注质量是生命线。我踩过的坑是标注标准不统一同一个人不同时间标的都不一样导致模型学得稀里糊涂。后来定了一份详细的标注规范明确什么算按钮、什么算输入框、边界框怎么画情况才好转。这份规范本身也是团队协作的基础没有它多人标注就是灾难。3.3 动作执行与状态机的组织动作执行这块我强烈建议用状态机来组织而不是让模型自由发挥。状态机的好处是把任务拆成明确的阶段每个阶段有清晰的进入条件、执行动作、退出条件。模型只需要在单个阶段内做决策不用操心全局稳定性能提升一大截。举个具体的例子。一个“填写表单并提交”的任务可以拆成这几个状态定位表单区域、逐个填写字段、检查填写结果、点击提交、确认提交成功。每个状态里模型的动作空间被大幅收窄比如“填写字段”状态下它只需要关心当前该填哪个字段、填什么内容不用考虑提交按钮在哪。这种收窄对提升成功率非常有效。状态机的状态转移条件要设计得保守一些。宁可多检查一步也不要贸然进入下一个状态。我见过系统在表单还没填完的时候就跳到了提交状态结果提交了个半成品。转移条件最好基于明确的界面信号比如“检测到所有必填字段都有值了”才允许进入提交状态而不是基于“大概填得差不多了”这种模糊判断。提示状态机的状态数量不要太多超过七八个状态就很难维护了。如果任务确实复杂考虑拆成多个子任务每个子任务一个状态机用上层逻辑串联。这样每个状态机都保持简单整体又可扩展。3.4 一个完整的操作循环示例把上面几块拼起来一个完整的操作循环大致是这样的截屏、理解界面、根据当前状态决定动作、执行动作、等待并观察结果、更新状态、进入下一轮。这个循环不断重复直到任务完成或者触发异常退出。我用一段伪代码把这个循环的骨架写出来方便你对照自己的实现state init max_steps 50 step 0 while state ! done and step max_steps: screenshot capture_screen() ui_graph understand_interface(screenshot) if state init: action plan_initial_action(ui_graph) elif state filling: action decide_fill_action(ui_graph, form_context) elif state submitting: action decide_submit_action(ui_graph) execute_action(action) changed wait_for_change(timeoutadaptive_timeout()) if not changed: handle_no_change(state) else: state transition_state(state, ui_graph, changed) step 1这段骨架里几个关键点值得展开说。adaptive_timeout是自适应等待根据历史响应时间动态调整避免固定等待造成的浪费或误判。handle_no_change是异常处理分支界面没变化时不能傻等得分析原因并决定重试还是放弃。transition_state是状态转移逻辑基于界面变化和当前状态决定下一步去哪。实际跑起来这个循环的节奏控制很关键。太快了界面还没反应过来就截下一张全是旧状态太慢了整体效率低下。我的经验是在关键动作后加一个短延迟在观察阶段用轮询两者结合能兼顾效率和准确性。这个短延迟设多少得根据目标应用的响应速度实测没有通用值。4. 那些文档里不会写的坑与排查经验4.1 坐标偏移与分辨率适配坐标偏移是最高频的问题没有之一。模型输出的坐标和实际点击位置对不上差几个像素就点空。这个问题在换了分辨率或者缩放比例之后尤其明显。我遇到过最诡异的一次模型在开发机上点得好好的部署到测试机上全部偏移查了半天发现是两台机器的显示缩放设置不一样。解决这个问题的根本办法是统一坐标基准。截屏的时候记录下当前的分辨率和缩放比例所有坐标计算都在这个基准下进行执行动作前再换算回实际屏幕坐标。这个换算逻辑要封装成一个独立模块所有涉及坐标的地方都走它避免各处各算各的导致不一致。还有一个隐蔽的坑是多显示器环境。模型可能把坐标算到了主显示器上但目标窗口在副显示器上点击就落空了。处理办法是在截屏时明确指定目标显示器或者在理解层里加入显示器边界的判断。这个坑不常遇到但一旦遇到就很难查因为从截图上看一切正常就是点不中。注意坐标偏移问题在测试阶段容易被忽略因为开发环境往往配置统一。上线前一定要在多种分辨率、多种缩放比例、多显示器环境下做一轮回归测试否则线上翻车概率很高。4.2 界面加载时序与竞态问题界面加载的时序问题比想象中普遍。点击一个按钮后界面可能先显示一个加载动画然后才渲染出目标内容。如果模型在加载动画阶段就截图理解看到的是一个残缺的界面决策自然就错了。更麻烦的是有些界面是分块加载的上半部分先出来下半部分后出来模型可能基于不完整的界面做了判断。应对这个问题的思路是等待稳定态。不要一检测到变化就立刻截图而是等界面连续几帧没有明显变化了再截。这个“连续几帧”的判断可以用简单的图像差异检测实现差异小于阈值就认为稳定了。阈值设多少需要根据界面的动态程度调动态元素多的界面阈值要放宽否则永远等不到稳定。竞态问题在表单填写场景里特别常见。模型快速连续填写多个字段但每个字段的输入可能触发异步校验校验结果回来之前界面状态是不确定的。如果模型不等校验完成就填下一个可能触发校验冲突或者覆盖。我的做法是在每个字段填写后加一个校验等待检测到校验通过的信号比如错误提示消失、对勾出现再继续。这个等待会增加整体耗时但换来的是稳定性值得。4.3 弹窗、广告与意外干扰的处理真实环境里的界面从来不干净。弹窗、广告、通知、引导提示这些干扰元素随时可能冒出来挡住目标区域。模型如果没意识到这些干扰就会对着被遮挡的区域操作自然失败。处理干扰的第一道防线是在理解层识别干扰元素。训练检测模型的时候把弹窗、广告位、通知条这些也作为一类标注进去让模型能认出来。识别到之后动作层优先处理干扰——能关的关掉关不掉的绕开。这个优先级逻辑要写死在状态机里不能指望模型自己判断。第二道防线是操作前的遮挡检查。在执行关键动作前检查目标区域是否被其他元素覆盖。这个检查可以用简单的区域重叠判断实现如果目标元素的边界框和某个干扰元素的边界框重叠面积超过阈值就先处理干扰。这个检查增加了一点开销但能避免大量莫名其妙的失败。还有一种干扰是动态内容。有些页面的某些区域会持续变化比如轮播图、实时数据、动画效果。这些变化会干扰变化检测逻辑让系统误以为操作生效了。处理办法是在变化检测时排除掉这些已知的动态区域只关注关键区域的变化。这个排除列表需要针对具体应用配置没法通用。4.4 常见问题速查与排查思路把上面这些坑整理成一张速查表方便你在遇到问题时快速定位现象可能原因排查方向处理办法点击无反应坐标偏移对比模型坐标与实际点击位置统一坐标基准加偏移校正点击落到错误元素元素检测漏检或误检检查理解层输出的元素列表提升检测精度加元素索引回退操作后界面无变化等待时机不对检查等待逻辑和超时设置改自适应等待加轮询反复重试同一动作状态转移条件失效检查状态机转移逻辑加最大重试次数加异常上报表单填写错位焦点管理出错检查输入前的焦点确认逻辑输入前显式点击目标字段被弹窗挡住操作失败干扰元素未处理检查干扰识别和遮挡判断加干扰处理优先级加遮挡检查换环境后全部失败分辨率或缩放不匹配对比环境配置差异统一环境配置加适配层排查的时候有个通用思路先确认感知对不对再确认动作准不准最后确认反馈灵不灵。大部分问题出在感知层模型看到的和实际的不一致。把截图存下来人工看一眼往往就能发现问题。我养成了一个习惯每次失败都保存当时的截图和模型输出事后复盘的时候一目了然。提示日志要记全。截图、模型输出、执行的动作、执行后的截图这四样凑齐了绝大多数问题都能定位。缺了任何一样排查就像盲人摸象。日志存储成本不高但价值极大别在这上面省。5. 关于能力边界与工程兜底的思考5.1 模型能力的天花板在哪里做了一段时间之后我对模型能力的天花板有了比较清醒的认识。当前的多模态模型在界面语义理解上已经相当不错给它一张截图它能比较准确地告诉你界面上有什么、大概是什么功能。但在精确空间定位上还差得远坐标误差是常态。在多步规划上也不稳定简单任务还行步骤一多就容易乱。这个能力分布决定了当前阶段的项目策略把模型用在它擅长的地方把不擅长的地方用工程手段补上。语义理解交给模型坐标定位用检测模型或者模板匹配兜底多步规划用状态机约束。这种分工不是对模型能力的否定而是对现实的尊重。硬要让模型端到端地搞定一切结果就是无穷无尽的调试。我见过一些团队执着于“纯模型方案”拒绝任何工程兜底觉得那样不够优雅。结果就是系统在演示环境里跑得挺好一到真实环境就崩。工程兜底不丢人它是把不稳定的能力包装成稳定产品的必要手段。用户不关心你内部用了多少模型多少规则他们只关心任务能不能可靠完成。5.2 工程兜底策略的设计原则工程兜底的设计我总结了几条原则。第一条是兜底要分层。最上层是模型决策中间层是规则校验最下层是硬编码的保底逻辑。每层都有明确的职责上层失败了下层接住而不是所有逻辑混在一起。分层的好处是每层可以独立测试和替换维护起来清晰。第二条是兜底要可观测。兜底逻辑触发的时候要留下明确的日志说明是哪一层兜住的、为什么触发的。没有这个观测兜底逻辑就成了黑盒出了问题根本不知道从哪查。我一般会在兜底触发时记录触发原因和当时的界面状态方便后续分析。第三条是兜底不能掩盖问题。兜底是为了保证任务完成不是为了把失败藏起来。如果某个兜底逻辑频繁触发说明上游的模型或者规则有问题应该去修上游而不是让兜底一直兜着。我会定期统计各层兜底的触发频率频率高的地方就是需要优化的地方。5.3 什么任务适合交给Computer Use不是所有任务都适合用Computer Use来做。判断标准大致是界面相对固定、操作步骤可枚举、失败可检测、重试成本低。符合这几条的任务用Computer Use做成功率有保障。反过来界面千变万化、步骤依赖大量常识判断、失败难以检测的任务现阶段还是人工做更靠谱。举个具体的对比。后台管理系统的数据录入界面固定、步骤明确、填错了能看出来、重填成本低非常适合。而开放式的网页浏览和信息搜集界面不可控、目标模糊、失败难判断就不太适合。这个判断很重要它决定了你把资源投在哪个方向上。投错了方向再努力也是事倍功半。我在实际项目里的体会是Computer Use最适合做“有明确成功标准的重复性操作”。这类任务人类做起来枯燥易错模型做起来虽然慢但稳定价值最明显。而那些需要灵活应变、需要大量背景知识的任务现阶段还是人机配合更现实——模型做粗活人做判断。6. 从莫拉维克悖论回看这个方向绕了一圈再回到莫拉维克悖论我对它的理解比刚开始深了不少。这个悖论不是在说机器笨而是在提醒我们人类习以为常的能力背后藏着巨大的复杂性。Computer Use这个方向之所以难正是因为它要求机器去复现那些人类根本不觉得难的能力——看、点、判断、纠错。这些能力在人类身上是打包在一起的拆开来看每一项都不简单。我现在的看法是这个方向的机会不在于追求“像人一样通用”而在于找到那些人类做起来烦、机器做起来虽然笨但能稳定完成的场景。把场景收窄把流程固化把兜底做厚模型的能力就能被转化成实际价值。反过来如果一直盯着“通用操作”这个目标就会不断撞上莫拉维克悖论的墙。最后分享一个我在实操中养成的小习惯每次系统失败不要急着调参数或者换模型先问自己一句“这个失败人类来做会不会也出错”。如果人类在同样情况下也会出错那说明是任务设计或者环境的问题不是模型的问题该改的是任务和环境。如果人类轻松就能做对而模型做不对那才是模型能力需要提升的地方。这个区分能帮你把精力花在刀刃上避免在错误的方向上反复折腾。